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