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