ProgressBar.java revision cf4550c3198d6b3d92cdc52707fe70d7cc0caa9f
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.widget; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Bitmap; 22import android.graphics.BitmapShader; 23import android.graphics.Canvas; 24import android.graphics.Shader; 25import android.graphics.Rect; 26import android.graphics.drawable.AnimationDrawable; 27import android.graphics.drawable.BitmapDrawable; 28import android.graphics.drawable.ClipDrawable; 29import android.graphics.drawable.Drawable; 30import android.graphics.drawable.LayerDrawable; 31import android.graphics.drawable.ShapeDrawable; 32import android.graphics.drawable.StateListDrawable; 33import android.graphics.drawable.Animatable; 34import android.graphics.drawable.shapes.RoundRectShape; 35import android.graphics.drawable.shapes.Shape; 36import android.util.AttributeSet; 37import android.view.Gravity; 38import android.view.View; 39import android.view.animation.AlphaAnimation; 40import android.view.animation.Animation; 41import android.view.animation.AnimationUtils; 42import android.view.animation.Interpolator; 43import android.view.animation.LinearInterpolator; 44import android.view.animation.Transformation; 45import android.widget.RemoteViews.RemoteView; 46import android.os.Parcel; 47import android.os.Parcelable; 48import android.os.SystemClock; 49 50import com.android.internal.R; 51 52 53/** 54 * <p> 55 * Visual indicator of progress in some operation. Displays a bar to the user 56 * representing how far the operation has progressed; the application can 57 * change the amount of progress (modifying the length of the bar) as it moves 58 * forward. There is also a secondary progress displayable on a progress bar 59 * which is useful for displaying intermediate progress, such as the buffer 60 * level during a streaming playback progress bar. 61 * </p> 62 * 63 * <p> 64 * A progress bar can also be made indeterminate. In indeterminate mode, the 65 * progress bar shows a cyclic animation. This mode is used by applications 66 * when the length of the task is unknown. 67 * </p> 68 * 69 * <p>The following code example shows how a progress bar can be used from 70 * a worker thread to update the user interface to notify the user of progress: 71 * </p> 72 * 73 * <pre class="prettyprint"> 74 * public class MyActivity extends Activity { 75 * private static final int PROGRESS = 0x1; 76 * 77 * private ProgressBar mProgress; 78 * private int mProgressStatus = 0; 79 * 80 * private Handler mHandler = new Handler(); 81 * 82 * protected void onCreate(Bundle icicle) { 83 * super.onCreate(icicle); 84 * 85 * setContentView(R.layout.progressbar_activity); 86 * 87 * mProgress = (ProgressBar) findViewById(R.id.progress_bar); 88 * 89 * // Start lengthy operation in a background thread 90 * new Thread(new Runnable() { 91 * public void run() { 92 * while (mProgressStatus < 100) { 93 * mProgressStatus = doWork(); 94 * 95 * // Update the progress bar 96 * mHandler.post(new Runnable() { 97 * public void run() { 98 * mProgress.setProgress(mProgressStatus); 99 * } 100 * }); 101 * } 102 * } 103 * }).start(); 104 * } 105 * } 106 * </pre> 107 * 108 * <p><strong>XML attributes</b></strong> 109 * <p> 110 * See {@link android.R.styleable#ProgressBar ProgressBar Attributes}, 111 * {@link android.R.styleable#View View Attributes} 112 * </p> 113 * 114 * <p><strong>Styles</b></strong> 115 * <p> 116 * @attr ref android.R.styleable#Theme_progressBarStyle 117 * @attr ref android.R.styleable#Theme_progressBarStyleSmall 118 * @attr ref android.R.styleable#Theme_progressBarStyleLarge 119 * @attr ref android.R.styleable#Theme_progressBarStyleHorizontal 120 * </p> 121 */ 122@RemoteView 123public class ProgressBar extends View { 124 private static final int MAX_LEVEL = 10000; 125 private static final int ANIMATION_RESOLUTION = 200; 126 127 int mMinWidth; 128 int mMaxWidth; 129 int mMinHeight; 130 int mMaxHeight; 131 132 private int mProgress; 133 private int mSecondaryProgress; 134 private int mMax; 135 136 private int mBehavior; 137 private int mDuration; 138 private boolean mIndeterminate; 139 private boolean mOnlyIndeterminate; 140 private Transformation mTransformation; 141 private AlphaAnimation mAnimation; 142 private Drawable mIndeterminateDrawable; 143 private Drawable mProgressDrawable; 144 private Drawable mCurrentDrawable; 145 Bitmap mSampleTile; 146 private boolean mNoInvalidate; 147 private Interpolator mInterpolator; 148 private RefreshProgressRunnable mRefreshProgressRunnable; 149 private long mUiThreadId; 150 private boolean mShouldStartAnimationDrawable; 151 private long mLastDrawTime; 152 153 private boolean mInDrawing; 154 155 /** 156 * Create a new progress bar with range 0...100 and initial progress of 0. 157 * @param context the application environment 158 */ 159 public ProgressBar(Context context) { 160 this(context, null); 161 } 162 163 public ProgressBar(Context context, AttributeSet attrs) { 164 this(context, attrs, com.android.internal.R.attr.progressBarStyle); 165 } 166 167 public ProgressBar(Context context, AttributeSet attrs, int defStyle) { 168 super(context, attrs, defStyle); 169 mUiThreadId = Thread.currentThread().getId(); 170 initProgressBar(); 171 172 TypedArray a = 173 context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, 0); 174 175 mNoInvalidate = true; 176 177 Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable); 178 if (drawable != null) { 179 drawable = tileify(drawable, false); 180 setProgressDrawable(drawable); 181 } 182 183 184 mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration); 185 186 mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth); 187 mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth); 188 mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight); 189 mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight); 190 191 mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior); 192 193 final int resID = a.getResourceId(com.android.internal.R.styleable.ProgressBar_interpolator, -1); 194 if (resID > 0) { 195 setInterpolator(context, resID); 196 } 197 198 setMax(a.getInt(R.styleable.ProgressBar_max, mMax)); 199 200 setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress)); 201 202 setSecondaryProgress( 203 a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress)); 204 205 drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable); 206 if (drawable != null) { 207 drawable = tileifyIndeterminate(drawable); 208 setIndeterminateDrawable(drawable); 209 } 210 211 mOnlyIndeterminate = a.getBoolean( 212 R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate); 213 214 mNoInvalidate = false; 215 216 setIndeterminate(mOnlyIndeterminate || a.getBoolean( 217 R.styleable.ProgressBar_indeterminate, mIndeterminate)); 218 219 a.recycle(); 220 } 221 222 /** 223 * Converts a drawable to a tiled version of itself. It will recursively 224 * traverse layer and state list drawables. 225 */ 226 private Drawable tileify(Drawable drawable, boolean clip) { 227 228 if (drawable instanceof LayerDrawable) { 229 LayerDrawable background = (LayerDrawable) drawable; 230 final int N = background.getNumberOfLayers(); 231 Drawable[] outDrawables = new Drawable[N]; 232 233 for (int i = 0; i < N; i++) { 234 int id = background.getId(i); 235 outDrawables[i] = tileify(background.getDrawable(i), 236 (id == R.id.progress || id == R.id.secondaryProgress)); 237 } 238 239 LayerDrawable newBg = new LayerDrawable(outDrawables); 240 241 for (int i = 0; i < N; i++) { 242 newBg.setId(i, background.getId(i)); 243 } 244 245 return newBg; 246 247 } else if (drawable instanceof StateListDrawable) { 248 StateListDrawable in = (StateListDrawable) drawable; 249 StateListDrawable out = new StateListDrawable(); 250 int numStates = in.getStateCount(); 251 for (int i = 0; i < numStates; i++) { 252 out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip)); 253 } 254 return out; 255 256 } else if (drawable instanceof BitmapDrawable) { 257 final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap(); 258 if (mSampleTile == null) { 259 mSampleTile = tileBitmap; 260 } 261 262 final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape()); 263 264 final BitmapShader bitmapShader = new BitmapShader(tileBitmap, 265 Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); 266 shapeDrawable.getPaint().setShader(bitmapShader); 267 268 return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT, 269 ClipDrawable.HORIZONTAL) : shapeDrawable; 270 } 271 272 return drawable; 273 } 274 275 Shape getDrawableShape() { 276 final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 }; 277 return new RoundRectShape(roundedCorners, null, null); 278 } 279 280 /** 281 * Convert a AnimationDrawable for use as a barberpole animation. 282 * Each frame of the animation is wrapped in a ClipDrawable and 283 * given a tiling BitmapShader. 284 */ 285 private Drawable tileifyIndeterminate(Drawable drawable) { 286 if (drawable instanceof AnimationDrawable) { 287 AnimationDrawable background = (AnimationDrawable) drawable; 288 final int N = background.getNumberOfFrames(); 289 AnimationDrawable newBg = new AnimationDrawable(); 290 newBg.setOneShot(background.isOneShot()); 291 292 for (int i = 0; i < N; i++) { 293 Drawable frame = tileify(background.getFrame(i), true); 294 frame.setLevel(10000); 295 newBg.addFrame(frame, background.getDuration(i)); 296 } 297 newBg.setLevel(10000); 298 drawable = newBg; 299 } 300 return drawable; 301 } 302 303 /** 304 * <p> 305 * Initialize the progress bar's default values: 306 * </p> 307 * <ul> 308 * <li>progress = 0</li> 309 * <li>max = 100</li> 310 * <li>animation duration = 4000 ms</li> 311 * <li>indeterminate = false</li> 312 * <li>behavior = repeat</li> 313 * </ul> 314 */ 315 private void initProgressBar() { 316 mMax = 100; 317 mProgress = 0; 318 mSecondaryProgress = 0; 319 mIndeterminate = false; 320 mOnlyIndeterminate = false; 321 mDuration = 4000; 322 mBehavior = AlphaAnimation.RESTART; 323 mMinWidth = 24; 324 mMaxWidth = 48; 325 mMinHeight = 24; 326 mMaxHeight = 48; 327 } 328 329 /** 330 * <p>Indicate whether this progress bar is in indeterminate mode.</p> 331 * 332 * @return true if the progress bar is in indeterminate mode 333 */ 334 public synchronized boolean isIndeterminate() { 335 return mIndeterminate; 336 } 337 338 /** 339 * <p>Change the indeterminate mode for this progress bar. In indeterminate 340 * mode, the progress is ignored and the progress bar shows an infinite 341 * animation instead.</p> 342 * 343 * If this progress bar's style only supports indeterminate mode (such as the circular 344 * progress bars), then this will be ignored. 345 * 346 * @param indeterminate true to enable the indeterminate mode 347 */ 348 @android.view.RemotableViewMethod 349 public synchronized void setIndeterminate(boolean indeterminate) { 350 if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) { 351 mIndeterminate = indeterminate; 352 353 if (indeterminate) { 354 // swap between indeterminate and regular backgrounds 355 mCurrentDrawable = mIndeterminateDrawable; 356 startAnimation(); 357 } else { 358 mCurrentDrawable = mProgressDrawable; 359 stopAnimation(); 360 } 361 } 362 } 363 364 /** 365 * <p>Get the drawable used to draw the progress bar in 366 * indeterminate mode.</p> 367 * 368 * @return a {@link android.graphics.drawable.Drawable} instance 369 * 370 * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable) 371 * @see #setIndeterminate(boolean) 372 */ 373 public Drawable getIndeterminateDrawable() { 374 return mIndeterminateDrawable; 375 } 376 377 /** 378 * <p>Define the drawable used to draw the progress bar in 379 * indeterminate mode.</p> 380 * 381 * @param d the new drawable 382 * 383 * @see #getIndeterminateDrawable() 384 * @see #setIndeterminate(boolean) 385 */ 386 public void setIndeterminateDrawable(Drawable d) { 387 if (d != null) { 388 d.setCallback(this); 389 } 390 mIndeterminateDrawable = d; 391 if (mIndeterminate) { 392 mCurrentDrawable = d; 393 postInvalidate(); 394 } 395 } 396 397 /** 398 * <p>Get the drawable used to draw the progress bar in 399 * progress mode.</p> 400 * 401 * @return a {@link android.graphics.drawable.Drawable} instance 402 * 403 * @see #setProgressDrawable(android.graphics.drawable.Drawable) 404 * @see #setIndeterminate(boolean) 405 */ 406 public Drawable getProgressDrawable() { 407 return mProgressDrawable; 408 } 409 410 /** 411 * <p>Define the drawable used to draw the progress bar in 412 * progress mode.</p> 413 * 414 * @param d the new drawable 415 * 416 * @see #getProgressDrawable() 417 * @see #setIndeterminate(boolean) 418 */ 419 public void setProgressDrawable(Drawable d) { 420 if (d != null) { 421 d.setCallback(this); 422 } 423 mProgressDrawable = d; 424 if (!mIndeterminate) { 425 mCurrentDrawable = d; 426 postInvalidate(); 427 } 428 } 429 430 /** 431 * @return The drawable currently used to draw the progress bar 432 */ 433 Drawable getCurrentDrawable() { 434 return mCurrentDrawable; 435 } 436 437 @Override 438 protected boolean verifyDrawable(Drawable who) { 439 return who == mProgressDrawable || who == mIndeterminateDrawable 440 || super.verifyDrawable(who); 441 } 442 443 @Override 444 public void postInvalidate() { 445 if (!mNoInvalidate) { 446 super.postInvalidate(); 447 } 448 } 449 450 private class RefreshProgressRunnable implements Runnable { 451 452 private int mId; 453 private int mProgress; 454 private boolean mFromUser; 455 456 RefreshProgressRunnable(int id, int progress, boolean fromUser) { 457 mId = id; 458 mProgress = progress; 459 mFromUser = fromUser; 460 } 461 462 public void run() { 463 doRefreshProgress(mId, mProgress, mFromUser); 464 // Put ourselves back in the cache when we are done 465 mRefreshProgressRunnable = this; 466 } 467 468 public void setup(int id, int progress, boolean fromUser) { 469 mId = id; 470 mProgress = progress; 471 mFromUser = fromUser; 472 } 473 474 } 475 476 private synchronized void doRefreshProgress(int id, int progress, boolean fromUser) { 477 float scale = mMax > 0 ? (float) progress / (float) mMax : 0; 478 final Drawable d = mCurrentDrawable; 479 if (d != null) { 480 Drawable progressDrawable = null; 481 482 if (d instanceof LayerDrawable) { 483 progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id); 484 } 485 486 final int level = (int) (scale * MAX_LEVEL); 487 (progressDrawable != null ? progressDrawable : d).setLevel(level); 488 } else { 489 invalidate(); 490 } 491 492 if (id == R.id.progress) { 493 onProgressRefresh(scale, fromUser); 494 } 495 } 496 497 void onProgressRefresh(float scale, boolean fromUser) { 498 } 499 500 private synchronized void refreshProgress(int id, int progress, boolean fromUser) { 501 if (mUiThreadId == Thread.currentThread().getId()) { 502 doRefreshProgress(id, progress, fromUser); 503 } else { 504 RefreshProgressRunnable r; 505 if (mRefreshProgressRunnable != null) { 506 // Use cached RefreshProgressRunnable if available 507 r = mRefreshProgressRunnable; 508 // Uncache it 509 mRefreshProgressRunnable = null; 510 r.setup(id, progress, fromUser); 511 } else { 512 // Make a new one 513 r = new RefreshProgressRunnable(id, progress, fromUser); 514 } 515 post(r); 516 } 517 } 518 519 /** 520 * <p>Set the current progress to the specified value. Does not do anything 521 * if the progress bar is in indeterminate mode.</p> 522 * 523 * @param progress the new progress, between 0 and {@link #getMax()} 524 * 525 * @see #setIndeterminate(boolean) 526 * @see #isIndeterminate() 527 * @see #getProgress() 528 * @see #incrementProgressBy(int) 529 */ 530 @android.view.RemotableViewMethod 531 public synchronized void setProgress(int progress) { 532 setProgress(progress, false); 533 } 534 535 @android.view.RemotableViewMethod 536 synchronized void setProgress(int progress, boolean fromUser) { 537 if (mIndeterminate) { 538 return; 539 } 540 541 if (progress < 0) { 542 progress = 0; 543 } 544 545 if (progress > mMax) { 546 progress = mMax; 547 } 548 549 if (progress != mProgress) { 550 mProgress = progress; 551 refreshProgress(R.id.progress, mProgress, fromUser); 552 } 553 } 554 555 /** 556 * <p> 557 * Set the current secondary progress to the specified value. Does not do 558 * anything if the progress bar is in indeterminate mode. 559 * </p> 560 * 561 * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()} 562 * @see #setIndeterminate(boolean) 563 * @see #isIndeterminate() 564 * @see #getSecondaryProgress() 565 * @see #incrementSecondaryProgressBy(int) 566 */ 567 @android.view.RemotableViewMethod 568 public synchronized void setSecondaryProgress(int secondaryProgress) { 569 if (mIndeterminate) { 570 return; 571 } 572 573 if (secondaryProgress < 0) { 574 secondaryProgress = 0; 575 } 576 577 if (secondaryProgress > mMax) { 578 secondaryProgress = mMax; 579 } 580 581 if (secondaryProgress != mSecondaryProgress) { 582 mSecondaryProgress = secondaryProgress; 583 refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false); 584 } 585 } 586 587 /** 588 * <p>Get the progress bar's current level of progress. Return 0 when the 589 * progress bar is in indeterminate mode.</p> 590 * 591 * @return the current progress, between 0 and {@link #getMax()} 592 * 593 * @see #setIndeterminate(boolean) 594 * @see #isIndeterminate() 595 * @see #setProgress(int) 596 * @see #setMax(int) 597 * @see #getMax() 598 */ 599 public synchronized int getProgress() { 600 return mIndeterminate ? 0 : mProgress; 601 } 602 603 /** 604 * <p>Get the progress bar's current level of secondary progress. Return 0 when the 605 * progress bar is in indeterminate mode.</p> 606 * 607 * @return the current secondary progress, between 0 and {@link #getMax()} 608 * 609 * @see #setIndeterminate(boolean) 610 * @see #isIndeterminate() 611 * @see #setSecondaryProgress(int) 612 * @see #setMax(int) 613 * @see #getMax() 614 */ 615 public synchronized int getSecondaryProgress() { 616 return mIndeterminate ? 0 : mSecondaryProgress; 617 } 618 619 /** 620 * <p>Return the upper limit of this progress bar's range.</p> 621 * 622 * @return a positive integer 623 * 624 * @see #setMax(int) 625 * @see #getProgress() 626 * @see #getSecondaryProgress() 627 */ 628 public synchronized int getMax() { 629 return mMax; 630 } 631 632 /** 633 * <p>Set the range of the progress bar to 0...<tt>max</tt>.</p> 634 * 635 * @param max the upper range of this progress bar 636 * 637 * @see #getMax() 638 * @see #setProgress(int) 639 * @see #setSecondaryProgress(int) 640 */ 641 @android.view.RemotableViewMethod 642 public synchronized void setMax(int max) { 643 if (max < 0) { 644 max = 0; 645 } 646 if (max != mMax) { 647 mMax = max; 648 postInvalidate(); 649 650 if (mProgress > max) { 651 mProgress = max; 652 } 653 } 654 } 655 656 /** 657 * <p>Increase the progress bar's progress by the specified amount.</p> 658 * 659 * @param diff the amount by which the progress must be increased 660 * 661 * @see #setProgress(int) 662 */ 663 public synchronized final void incrementProgressBy(int diff) { 664 setProgress(mProgress + diff); 665 } 666 667 /** 668 * <p>Increase the progress bar's secondary progress by the specified amount.</p> 669 * 670 * @param diff the amount by which the secondary progress must be increased 671 * 672 * @see #setSecondaryProgress(int) 673 */ 674 public synchronized final void incrementSecondaryProgressBy(int diff) { 675 setSecondaryProgress(mSecondaryProgress + diff); 676 } 677 678 /** 679 * <p>Start the indeterminate progress animation.</p> 680 */ 681 void startAnimation() { 682 int visibility = getVisibility(); 683 if (visibility != VISIBLE) { 684 return; 685 } 686 687 if (mIndeterminateDrawable instanceof Animatable) { 688 mShouldStartAnimationDrawable = true; 689 mAnimation = null; 690 } else { 691 if (mInterpolator == null) { 692 mInterpolator = new LinearInterpolator(); 693 } 694 695 mTransformation = new Transformation(); 696 mAnimation = new AlphaAnimation(0.0f, 1.0f); 697 mAnimation.setRepeatMode(mBehavior); 698 mAnimation.setRepeatCount(Animation.INFINITE); 699 mAnimation.setDuration(mDuration); 700 mAnimation.setInterpolator(mInterpolator); 701 mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME); 702 postInvalidate(); 703 } 704 } 705 706 /** 707 * <p>Stop the indeterminate progress animation.</p> 708 */ 709 void stopAnimation() { 710 mAnimation = null; 711 mTransformation = null; 712 if (mIndeterminateDrawable instanceof Animatable) { 713 ((Animatable) mIndeterminateDrawable).stop(); 714 mShouldStartAnimationDrawable = false; 715 } 716 } 717 718 /** 719 * Sets the acceleration curve for the indeterminate animation. 720 * The interpolator is loaded as a resource from the specified context. 721 * 722 * @param context The application environment 723 * @param resID The resource identifier of the interpolator to load 724 */ 725 public void setInterpolator(Context context, int resID) { 726 setInterpolator(AnimationUtils.loadInterpolator(context, resID)); 727 } 728 729 /** 730 * Sets the acceleration curve for the indeterminate animation. 731 * Defaults to a linear interpolation. 732 * 733 * @param interpolator The interpolator which defines the acceleration curve 734 */ 735 public void setInterpolator(Interpolator interpolator) { 736 mInterpolator = interpolator; 737 } 738 739 /** 740 * Gets the acceleration curve type for the indeterminate animation. 741 * 742 * @return the {@link Interpolator} associated to this animation 743 */ 744 public Interpolator getInterpolator() { 745 return mInterpolator; 746 } 747 748 @Override 749 public void setVisibility(int v) { 750 if (getVisibility() != v) { 751 super.setVisibility(v); 752 753 if (mIndeterminate) { 754 // let's be nice with the UI thread 755 if (v == GONE || v == INVISIBLE) { 756 stopAnimation(); 757 } else if (v == VISIBLE) { 758 startAnimation(); 759 } 760 } 761 } 762 } 763 764 @Override 765 public void invalidateDrawable(Drawable dr) { 766 if (!mInDrawing) { 767 if (verifyDrawable(dr)) { 768 final Rect dirty = dr.getBounds(); 769 final int scrollX = mScrollX + mPaddingLeft; 770 final int scrollY = mScrollY + mPaddingTop; 771 772 invalidate(dirty.left + scrollX, dirty.top + scrollY, 773 dirty.right + scrollX, dirty.bottom + scrollY); 774 } else { 775 super.invalidateDrawable(dr); 776 } 777 } 778 } 779 780 @Override 781 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 782 // onDraw will translate the canvas so we draw starting at 0,0 783 int right = w - mPaddingRight - mPaddingLeft; 784 int bottom = h - mPaddingBottom - mPaddingTop; 785 786 if (mIndeterminateDrawable != null) { 787 mIndeterminateDrawable.setBounds(0, 0, right, bottom); 788 } 789 790 if (mProgressDrawable != null) { 791 mProgressDrawable.setBounds(0, 0, right, bottom); 792 } 793 } 794 795 @Override 796 protected synchronized void onDraw(Canvas canvas) { 797 super.onDraw(canvas); 798 799 Drawable d = mCurrentDrawable; 800 if (d != null) { 801 // Translate canvas so a indeterminate circular progress bar with padding 802 // rotates properly in its animation 803 canvas.save(); 804 canvas.translate(mPaddingLeft, mPaddingTop); 805 long time = getDrawingTime(); 806 if (mAnimation != null) { 807 mAnimation.getTransformation(time, mTransformation); 808 float scale = mTransformation.getAlpha(); 809 try { 810 mInDrawing = true; 811 d.setLevel((int) (scale * MAX_LEVEL)); 812 } finally { 813 mInDrawing = false; 814 } 815 if (SystemClock.uptimeMillis() - mLastDrawTime >= ANIMATION_RESOLUTION) { 816 mLastDrawTime = SystemClock.uptimeMillis(); 817 postInvalidateDelayed(ANIMATION_RESOLUTION); 818 } 819 } 820 d.draw(canvas); 821 canvas.restore(); 822 if (mShouldStartAnimationDrawable && d instanceof Animatable) { 823 ((Animatable) d).start(); 824 mShouldStartAnimationDrawable = false; 825 } 826 } 827 } 828 829 @Override 830 protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 831 Drawable d = mCurrentDrawable; 832 833 int dw = 0; 834 int dh = 0; 835 if (d != null) { 836 dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); 837 dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight())); 838 } 839 dw += mPaddingLeft + mPaddingRight; 840 dh += mPaddingTop + mPaddingBottom; 841 842 setMeasuredDimension(resolveSize(dw, widthMeasureSpec), 843 resolveSize(dh, heightMeasureSpec)); 844 } 845 846 @Override 847 protected void drawableStateChanged() { 848 super.drawableStateChanged(); 849 850 int[] state = getDrawableState(); 851 852 if (mProgressDrawable != null && mProgressDrawable.isStateful()) { 853 mProgressDrawable.setState(state); 854 } 855 856 if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) { 857 mIndeterminateDrawable.setState(state); 858 } 859 } 860 861 static class SavedState extends BaseSavedState { 862 int progress; 863 int secondaryProgress; 864 865 /** 866 * Constructor called from {@link ProgressBar#onSaveInstanceState()} 867 */ 868 SavedState(Parcelable superState) { 869 super(superState); 870 } 871 872 /** 873 * Constructor called from {@link #CREATOR} 874 */ 875 private SavedState(Parcel in) { 876 super(in); 877 progress = in.readInt(); 878 secondaryProgress = in.readInt(); 879 } 880 881 @Override 882 public void writeToParcel(Parcel out, int flags) { 883 super.writeToParcel(out, flags); 884 out.writeInt(progress); 885 out.writeInt(secondaryProgress); 886 } 887 888 public static final Parcelable.Creator<SavedState> CREATOR 889 = new Parcelable.Creator<SavedState>() { 890 public SavedState createFromParcel(Parcel in) { 891 return new SavedState(in); 892 } 893 894 public SavedState[] newArray(int size) { 895 return new SavedState[size]; 896 } 897 }; 898 } 899 900 @Override 901 public Parcelable onSaveInstanceState() { 902 // Force our ancestor class to save its state 903 Parcelable superState = super.onSaveInstanceState(); 904 SavedState ss = new SavedState(superState); 905 906 ss.progress = mProgress; 907 ss.secondaryProgress = mSecondaryProgress; 908 909 return ss; 910 } 911 912 @Override 913 public void onRestoreInstanceState(Parcelable state) { 914 SavedState ss = (SavedState) state; 915 super.onRestoreInstanceState(ss.getSuperState()); 916 917 setProgress(ss.progress); 918 setSecondaryProgress(ss.secondaryProgress); 919 } 920} 921