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