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