DrawableContainer.java revision 8de1494557cf1d00c1c3fce439138a28de7fbd61
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.graphics.drawable; 18 19import android.content.res.ColorStateList; 20import android.content.res.Resources; 21import android.content.res.Resources.Theme; 22import android.graphics.Canvas; 23import android.graphics.ColorFilter; 24import android.graphics.Insets; 25import android.graphics.PixelFormat; 26import android.graphics.Rect; 27import android.graphics.PorterDuff.Mode; 28import android.os.SystemClock; 29import android.util.LayoutDirection; 30import android.util.SparseArray; 31 32/** 33 * A helper class that contains several {@link Drawable}s and selects which one to use. 34 * 35 * You can subclass it to create your own DrawableContainers or directly use one its child classes. 36 */ 37public class DrawableContainer extends Drawable implements Drawable.Callback { 38 private static final boolean DEBUG = false; 39 private static final String TAG = "DrawableContainer"; 40 41 /** 42 * To be proper, we should have a getter for dither (and alpha, etc.) 43 * so that proxy classes like this can save/restore their delegates' 44 * values, but we don't have getters. Since we do have setters 45 * (e.g. setDither), which this proxy forwards on, we have to have some 46 * default/initial setting. 47 * 48 * The initial setting for dither is now true, since it almost always seems 49 * to improve the quality at negligible cost. 50 */ 51 private static final boolean DEFAULT_DITHER = true; 52 private DrawableContainerState mDrawableContainerState; 53 private Drawable mCurrDrawable; 54 private int mAlpha = 0xFF; 55 56 /** Whether setAlpha() has been called at least once. */ 57 private boolean mHasAlpha; 58 59 private int mCurIndex = -1; 60 private boolean mMutated; 61 62 // Animations. 63 private Runnable mAnimationRunnable; 64 private long mEnterAnimationEnd; 65 private long mExitAnimationEnd; 66 private Drawable mLastDrawable; 67 68 // overrides from Drawable 69 70 @Override 71 public void draw(Canvas canvas) { 72 if (mCurrDrawable != null) { 73 mCurrDrawable.draw(canvas); 74 } 75 if (mLastDrawable != null) { 76 mLastDrawable.draw(canvas); 77 } 78 } 79 80 @Override 81 public int getChangingConfigurations() { 82 return super.getChangingConfigurations() 83 | mDrawableContainerState.mChangingConfigurations 84 | mDrawableContainerState.mChildrenChangingConfigurations; 85 } 86 87 private boolean needsMirroring() { 88 return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL; 89 } 90 91 @Override 92 public boolean getPadding(Rect padding) { 93 final Rect r = mDrawableContainerState.getConstantPadding(); 94 boolean result; 95 if (r != null) { 96 padding.set(r); 97 result = (r.left | r.top | r.bottom | r.right) != 0; 98 } else { 99 if (mCurrDrawable != null) { 100 result = mCurrDrawable.getPadding(padding); 101 } else { 102 result = super.getPadding(padding); 103 } 104 } 105 if (needsMirroring()) { 106 final int left = padding.left; 107 final int right = padding.right; 108 padding.left = right; 109 padding.right = left; 110 } 111 return result; 112 } 113 114 /** 115 * @hide 116 */ 117 @Override 118 public Insets getOpticalInsets() { 119 if (mCurrDrawable != null) { 120 return mCurrDrawable.getOpticalInsets(); 121 } 122 return Insets.NONE; 123 } 124 125 @Override 126 public void setAlpha(int alpha) { 127 if (!mHasAlpha || mAlpha != alpha) { 128 mHasAlpha = true; 129 mAlpha = alpha; 130 if (mCurrDrawable != null) { 131 if (mEnterAnimationEnd == 0) { 132 mCurrDrawable.mutate().setAlpha(alpha); 133 } else { 134 animate(false); 135 } 136 } 137 } 138 } 139 140 @Override 141 public int getAlpha() { 142 return mAlpha; 143 } 144 145 @Override 146 public void setDither(boolean dither) { 147 if (mDrawableContainerState.mDither != dither) { 148 mDrawableContainerState.mDither = dither; 149 if (mCurrDrawable != null) { 150 mCurrDrawable.mutate().setDither(mDrawableContainerState.mDither); 151 } 152 } 153 } 154 155 @Override 156 public void setColorFilter(ColorFilter cf) { 157 mDrawableContainerState.mHasColorFilter = (cf != null); 158 159 if (mDrawableContainerState.mColorFilter != cf) { 160 mDrawableContainerState.mColorFilter = cf; 161 162 if (mCurrDrawable != null) { 163 mCurrDrawable.mutate().setColorFilter(cf); 164 } 165 } 166 } 167 168 @Override 169 public void setTint(ColorStateList tint, Mode tintMode) { 170 mDrawableContainerState.mHasTint = (tint != null && tintMode != null); 171 172 if (mDrawableContainerState.mTint != tint || mDrawableContainerState.mTintMode != tintMode) { 173 mDrawableContainerState.mTint = tint; 174 mDrawableContainerState.mTintMode = tintMode; 175 176 if (mCurrDrawable != null) { 177 mCurrDrawable.mutate().setTint(tint, tintMode); 178 } 179 } 180 } 181 182 /** 183 * Change the global fade duration when a new drawable is entering 184 * the scene. 185 * @param ms The amount of time to fade in milliseconds. 186 */ 187 public void setEnterFadeDuration(int ms) { 188 mDrawableContainerState.mEnterFadeDuration = ms; 189 } 190 191 /** 192 * Change the global fade duration when a new drawable is leaving 193 * the scene. 194 * @param ms The amount of time to fade in milliseconds. 195 */ 196 public void setExitFadeDuration(int ms) { 197 mDrawableContainerState.mExitFadeDuration = ms; 198 } 199 200 @Override 201 protected void onBoundsChange(Rect bounds) { 202 if (mLastDrawable != null) { 203 mLastDrawable.setBounds(bounds); 204 } 205 if (mCurrDrawable != null) { 206 mCurrDrawable.setBounds(bounds); 207 } 208 } 209 210 @Override 211 public boolean isStateful() { 212 return mDrawableContainerState.isStateful(); 213 } 214 215 @Override 216 public void setAutoMirrored(boolean mirrored) { 217 if (mDrawableContainerState.mAutoMirrored != mirrored) { 218 mDrawableContainerState.mAutoMirrored = mirrored; 219 if (mCurrDrawable != null) { 220 mCurrDrawable.mutate().setAutoMirrored(mDrawableContainerState.mAutoMirrored); 221 } 222 } 223 } 224 225 @Override 226 public boolean isAutoMirrored() { 227 return mDrawableContainerState.mAutoMirrored; 228 } 229 230 @Override 231 public void jumpToCurrentState() { 232 boolean changed = false; 233 if (mLastDrawable != null) { 234 mLastDrawable.jumpToCurrentState(); 235 mLastDrawable = null; 236 changed = true; 237 } 238 if (mCurrDrawable != null) { 239 mCurrDrawable.jumpToCurrentState(); 240 if (mHasAlpha) { 241 mCurrDrawable.mutate().setAlpha(mAlpha); 242 } 243 } 244 if (mExitAnimationEnd != 0) { 245 mExitAnimationEnd = 0; 246 changed = true; 247 } 248 if (mEnterAnimationEnd != 0) { 249 mEnterAnimationEnd = 0; 250 changed = true; 251 } 252 if (changed) { 253 invalidateSelf(); 254 } 255 } 256 257 @Override 258 public void setHotspot(float x, float y) { 259 if (mCurrDrawable != null) { 260 mCurrDrawable.setHotspot(x, y); 261 } 262 } 263 264 @Override 265 public void setHotspotBounds(int left, int top, int right, int bottom) { 266 if (mCurrDrawable != null) { 267 mCurrDrawable.setHotspotBounds(left, top, right, bottom); 268 } 269 } 270 271 @Override 272 protected boolean onStateChange(int[] state) { 273 if (mLastDrawable != null) { 274 return mLastDrawable.setState(state); 275 } 276 if (mCurrDrawable != null) { 277 return mCurrDrawable.setState(state); 278 } 279 return false; 280 } 281 282 @Override 283 protected boolean onLevelChange(int level) { 284 if (mLastDrawable != null) { 285 return mLastDrawable.setLevel(level); 286 } 287 if (mCurrDrawable != null) { 288 return mCurrDrawable.setLevel(level); 289 } 290 return false; 291 } 292 293 @Override 294 public int getIntrinsicWidth() { 295 if (mDrawableContainerState.isConstantSize()) { 296 return mDrawableContainerState.getConstantWidth(); 297 } 298 return mCurrDrawable != null ? mCurrDrawable.getIntrinsicWidth() : -1; 299 } 300 301 @Override 302 public int getIntrinsicHeight() { 303 if (mDrawableContainerState.isConstantSize()) { 304 return mDrawableContainerState.getConstantHeight(); 305 } 306 return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1; 307 } 308 309 @Override 310 public int getMinimumWidth() { 311 if (mDrawableContainerState.isConstantSize()) { 312 return mDrawableContainerState.getConstantMinimumWidth(); 313 } 314 return mCurrDrawable != null ? mCurrDrawable.getMinimumWidth() : 0; 315 } 316 317 @Override 318 public int getMinimumHeight() { 319 if (mDrawableContainerState.isConstantSize()) { 320 return mDrawableContainerState.getConstantMinimumHeight(); 321 } 322 return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0; 323 } 324 325 @Override 326 public void invalidateDrawable(Drawable who) { 327 if (who == mCurrDrawable && getCallback() != null) { 328 getCallback().invalidateDrawable(this); 329 } 330 } 331 332 @Override 333 public void scheduleDrawable(Drawable who, Runnable what, long when) { 334 if (who == mCurrDrawable && getCallback() != null) { 335 getCallback().scheduleDrawable(this, what, when); 336 } 337 } 338 339 @Override 340 public void unscheduleDrawable(Drawable who, Runnable what) { 341 if (who == mCurrDrawable && getCallback() != null) { 342 getCallback().unscheduleDrawable(this, what); 343 } 344 } 345 346 @Override 347 public boolean setVisible(boolean visible, boolean restart) { 348 boolean changed = super.setVisible(visible, restart); 349 if (mLastDrawable != null) { 350 mLastDrawable.setVisible(visible, restart); 351 } 352 if (mCurrDrawable != null) { 353 mCurrDrawable.setVisible(visible, restart); 354 } 355 return changed; 356 } 357 358 @Override 359 public int getOpacity() { 360 return mCurrDrawable == null || !mCurrDrawable.isVisible() ? PixelFormat.TRANSPARENT : 361 mDrawableContainerState.getOpacity(); 362 } 363 364 /** @hide */ 365 public void setCurrentIndex(int index) { 366 selectDrawable(index); 367 } 368 369 /** @hide */ 370 public int getCurrentIndex() { 371 return mCurIndex; 372 } 373 374 public boolean selectDrawable(int idx) { 375 if (idx == mCurIndex) { 376 return false; 377 } 378 379 final long now = SystemClock.uptimeMillis(); 380 381 if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + idx 382 + ": exit=" + mDrawableContainerState.mExitFadeDuration 383 + " enter=" + mDrawableContainerState.mEnterFadeDuration); 384 385 if (mDrawableContainerState.mExitFadeDuration > 0) { 386 if (mLastDrawable != null) { 387 mLastDrawable.setVisible(false, false); 388 } 389 if (mCurrDrawable != null) { 390 mLastDrawable = mCurrDrawable; 391 mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration; 392 } else { 393 mLastDrawable = null; 394 mExitAnimationEnd = 0; 395 } 396 } else if (mCurrDrawable != null) { 397 mCurrDrawable.setVisible(false, false); 398 } 399 400 if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) { 401 final Drawable d = mDrawableContainerState.getChild(idx); 402 mCurrDrawable = d; 403 mCurIndex = idx; 404 if (d != null) { 405 d.mutate(); 406 if (mDrawableContainerState.mEnterFadeDuration > 0) { 407 mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration; 408 } else if (mHasAlpha) { 409 d.setAlpha(mAlpha); 410 } 411 if (mDrawableContainerState.mHasColorFilter) { 412 d.setColorFilter(mDrawableContainerState.mColorFilter); 413 } else if (mDrawableContainerState.mHasTint) { 414 d.setTint(mDrawableContainerState.mTint, mDrawableContainerState.mTintMode); 415 } 416 d.setVisible(isVisible(), true); 417 d.setDither(mDrawableContainerState.mDither); 418 d.setState(getState()); 419 d.setLevel(getLevel()); 420 d.setBounds(getBounds()); 421 d.setLayoutDirection(getLayoutDirection()); 422 d.setAutoMirrored(mDrawableContainerState.mAutoMirrored); 423 } 424 } else { 425 mCurrDrawable = null; 426 mCurIndex = -1; 427 } 428 429 if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) { 430 if (mAnimationRunnable == null) { 431 mAnimationRunnable = new Runnable() { 432 @Override public void run() { 433 animate(true); 434 invalidateSelf(); 435 } 436 }; 437 } else { 438 unscheduleSelf(mAnimationRunnable); 439 } 440 // Compute first frame and schedule next animation. 441 animate(true); 442 } 443 444 invalidateSelf(); 445 446 return true; 447 } 448 449 void animate(boolean schedule) { 450 mHasAlpha = true; 451 452 final long now = SystemClock.uptimeMillis(); 453 boolean animating = false; 454 if (mCurrDrawable != null) { 455 if (mEnterAnimationEnd != 0) { 456 if (mEnterAnimationEnd <= now) { 457 mCurrDrawable.mutate().setAlpha(mAlpha); 458 mEnterAnimationEnd = 0; 459 } else { 460 int animAlpha = (int)((mEnterAnimationEnd-now)*255) 461 / mDrawableContainerState.mEnterFadeDuration; 462 if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha); 463 mCurrDrawable.mutate().setAlpha(((255-animAlpha)*mAlpha)/255); 464 animating = true; 465 } 466 } 467 } else { 468 mEnterAnimationEnd = 0; 469 } 470 if (mLastDrawable != null) { 471 if (mExitAnimationEnd != 0) { 472 if (mExitAnimationEnd <= now) { 473 mLastDrawable.setVisible(false, false); 474 mLastDrawable = null; 475 mExitAnimationEnd = 0; 476 } else { 477 int animAlpha = (int)((mExitAnimationEnd-now)*255) 478 / mDrawableContainerState.mExitFadeDuration; 479 if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha); 480 mLastDrawable.mutate().setAlpha((animAlpha*mAlpha)/255); 481 animating = true; 482 } 483 } 484 } else { 485 mExitAnimationEnd = 0; 486 } 487 488 if (schedule && animating) { 489 scheduleSelf(mAnimationRunnable, now + 1000/60); 490 } 491 } 492 493 @Override 494 public Drawable getCurrent() { 495 return mCurrDrawable; 496 } 497 498 @Override 499 public void applyTheme(Theme theme) { 500 mDrawableContainerState.applyTheme(theme); 501 } 502 503 @Override 504 public boolean canApplyTheme() { 505 return mDrawableContainerState.canApplyTheme(); 506 } 507 508 @Override 509 public ConstantState getConstantState() { 510 if (mDrawableContainerState.canConstantState()) { 511 mDrawableContainerState.mChangingConfigurations = getChangingConfigurations(); 512 return mDrawableContainerState; 513 } 514 return null; 515 } 516 517 @Override 518 public Drawable mutate() { 519 if (!mMutated && super.mutate() == this) { 520 mDrawableContainerState.mutate(); 521 mMutated = true; 522 } 523 return this; 524 } 525 526 /** 527 * A ConstantState that can contain several {@link Drawable}s. 528 * 529 * This class was made public to enable testing, and its visibility may change in a future 530 * release. 531 */ 532 public abstract static class DrawableContainerState extends ConstantState { 533 final DrawableContainer mOwner; 534 final Resources mRes; 535 536 Theme mTheme; 537 538 SparseArray<ConstantStateFuture> mDrawableFutures; 539 540 int mChangingConfigurations; 541 int mChildrenChangingConfigurations; 542 543 Drawable[] mDrawables; 544 int mNumChildren; 545 546 boolean mVariablePadding; 547 boolean mPaddingChecked; 548 Rect mConstantPadding; 549 550 boolean mConstantSize; 551 boolean mComputedConstantSize; 552 int mConstantWidth; 553 int mConstantHeight; 554 int mConstantMinimumWidth; 555 int mConstantMinimumHeight; 556 557 boolean mCheckedOpacity; 558 int mOpacity; 559 560 boolean mCheckedStateful; 561 boolean mStateful; 562 563 boolean mCheckedConstantState; 564 boolean mCanConstantState; 565 566 boolean mDither = DEFAULT_DITHER; 567 568 boolean mMutated; 569 int mLayoutDirection; 570 571 int mEnterFadeDuration; 572 int mExitFadeDuration; 573 574 boolean mAutoMirrored; 575 576 ColorFilter mColorFilter; 577 boolean mHasColorFilter; 578 579 ColorStateList mTint; 580 Mode mTintMode; 581 boolean mHasTint; 582 583 DrawableContainerState(DrawableContainerState orig, DrawableContainer owner, 584 Resources res) { 585 mOwner = owner; 586 mRes = res; 587 588 if (orig != null) { 589 mChangingConfigurations = orig.mChangingConfigurations; 590 mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations; 591 592 mCheckedConstantState = true; 593 mCanConstantState = true; 594 595 mVariablePadding = orig.mVariablePadding; 596 mConstantSize = orig.mConstantSize; 597 mDither = orig.mDither; 598 mMutated = orig.mMutated; 599 mLayoutDirection = orig.mLayoutDirection; 600 mEnterFadeDuration = orig.mEnterFadeDuration; 601 mExitFadeDuration = orig.mExitFadeDuration; 602 mAutoMirrored = orig.mAutoMirrored; 603 mColorFilter = orig.mColorFilter; 604 mHasColorFilter = orig.mHasColorFilter; 605 mTint = orig.mTint; 606 mTintMode = orig.mTintMode; 607 mHasTint = orig.mHasTint; 608 609 // Cloning the following values may require creating futures. 610 mConstantPadding = orig.getConstantPadding(); 611 mPaddingChecked = true; 612 613 mConstantWidth = orig.getConstantWidth(); 614 mConstantHeight = orig.getConstantHeight(); 615 mConstantMinimumWidth = orig.getConstantMinimumWidth(); 616 mConstantMinimumHeight = orig.getConstantMinimumHeight(); 617 mComputedConstantSize = true; 618 619 mOpacity = orig.getOpacity(); 620 mCheckedOpacity = true; 621 622 mStateful = orig.isStateful(); 623 mCheckedStateful = true; 624 625 // Postpone cloning children and futures until we're absolutely 626 // sure that we're done computing values for the original state. 627 final Drawable[] origDr = orig.mDrawables; 628 mDrawables = new Drawable[origDr.length]; 629 mNumChildren = orig.mNumChildren; 630 631 final SparseArray<ConstantStateFuture> origDf = orig.mDrawableFutures; 632 if (origDf != null) { 633 mDrawableFutures = origDf.clone(); 634 } else { 635 mDrawableFutures = new SparseArray<ConstantStateFuture>(mNumChildren); 636 } 637 638 final int N = mNumChildren; 639 for (int i = 0; i < N; i++) { 640 if (origDr[i] != null) { 641 mDrawableFutures.put(i, new ConstantStateFuture(origDr[i])); 642 } 643 } 644 } else { 645 mDrawables = new Drawable[10]; 646 mNumChildren = 0; 647 } 648 } 649 650 @Override 651 public int getChangingConfigurations() { 652 return mChangingConfigurations | mChildrenChangingConfigurations; 653 } 654 655 public final int addChild(Drawable dr) { 656 final int pos = mNumChildren; 657 658 if (pos >= mDrawables.length) { 659 growArray(pos, pos+10); 660 } 661 662 dr.setVisible(false, true); 663 dr.setCallback(mOwner); 664 665 mDrawables[pos] = dr; 666 mNumChildren++; 667 mChildrenChangingConfigurations |= dr.getChangingConfigurations(); 668 mCheckedStateful = false; 669 mCheckedOpacity = false; 670 671 mConstantPadding = null; 672 mPaddingChecked = false; 673 mComputedConstantSize = false; 674 675 return pos; 676 } 677 678 final int getCapacity() { 679 return mDrawables.length; 680 } 681 682 private final void createAllFutures() { 683 if (mDrawableFutures != null) { 684 final int futureCount = mDrawableFutures.size(); 685 for (int keyIndex = 0; keyIndex < futureCount; keyIndex++) { 686 final int index = mDrawableFutures.keyAt(keyIndex); 687 mDrawables[index] = mDrawableFutures.valueAt(keyIndex).get(this); 688 } 689 690 mDrawableFutures = null; 691 } 692 } 693 694 public final int getChildCount() { 695 return mNumChildren; 696 } 697 698 /* 699 * @deprecated Use {@link #getChild} instead. 700 */ 701 public final Drawable[] getChildren() { 702 // Create all futures for backwards compatibility. 703 createAllFutures(); 704 705 return mDrawables; 706 } 707 708 public final Drawable getChild(int index) { 709 final Drawable result = mDrawables[index]; 710 if (result != null) { 711 return result; 712 } 713 714 // Prepare future drawable if necessary. 715 if (mDrawableFutures != null) { 716 final int keyIndex = mDrawableFutures.indexOfKey(index); 717 if (keyIndex >= 0) { 718 final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this); 719 mDrawables[index] = prepared; 720 mDrawableFutures.removeAt(keyIndex); 721 return prepared; 722 } 723 } 724 725 return null; 726 } 727 728 final void setLayoutDirection(int layoutDirection) { 729 // No need to call createAllFutures, since future drawables will 730 // change layout direction when they are prepared. 731 final int N = mNumChildren; 732 final Drawable[] drawables = mDrawables; 733 for (int i = 0; i < N; i++) { 734 if (drawables[i] != null) { 735 drawables[i].setLayoutDirection(layoutDirection); 736 } 737 } 738 739 mLayoutDirection = layoutDirection; 740 } 741 742 final void applyTheme(Theme theme) { 743 // No need to call createAllFutures, since future drawables will 744 // apply the theme when they are prepared. 745 final int N = mNumChildren; 746 final Drawable[] drawables = mDrawables; 747 for (int i = 0; i < N; i++) { 748 if (drawables[i] != null) { 749 drawables[i].applyTheme(theme); 750 } 751 } 752 753 mTheme = theme; 754 } 755 756 @Override 757 public boolean canApplyTheme() { 758 final int N = mNumChildren; 759 final Drawable[] drawables = mDrawables; 760 for (int i = 0; i < N; i++) { 761 final Drawable d = drawables[i]; 762 if (d != null) { 763 if (d.canApplyTheme()) { 764 return true; 765 } 766 } else { 767 final ConstantStateFuture future = mDrawableFutures.get(i); 768 if (future != null && future.canApplyTheme()) { 769 return true; 770 } 771 } 772 } 773 774 return false; 775 } 776 777 final void mutate() { 778 // No need to call createAllFutures, since future drawables will 779 // mutate when they are prepared. 780 final int N = mNumChildren; 781 final Drawable[] drawables = mDrawables; 782 for (int i = 0; i < N; i++) { 783 if (drawables[i] != null) { 784 drawables[i].mutate(); 785 } 786 } 787 788 mMutated = true; 789 } 790 791 /** 792 * A boolean value indicating whether to use the maximum padding value 793 * of all frames in the set (false), or to use the padding value of the 794 * frame being shown (true). Default value is false. 795 */ 796 public final void setVariablePadding(boolean variable) { 797 mVariablePadding = variable; 798 } 799 800 public final Rect getConstantPadding() { 801 if (mVariablePadding) { 802 return null; 803 } 804 805 if ((mConstantPadding != null) || mPaddingChecked) { 806 return mConstantPadding; 807 } 808 809 createAllFutures(); 810 811 Rect r = null; 812 final Rect t = new Rect(); 813 final int N = mNumChildren; 814 final Drawable[] drawables = mDrawables; 815 for (int i = 0; i < N; i++) { 816 if (drawables[i].getPadding(t)) { 817 if (r == null) r = new Rect(0, 0, 0, 0); 818 if (t.left > r.left) r.left = t.left; 819 if (t.top > r.top) r.top = t.top; 820 if (t.right > r.right) r.right = t.right; 821 if (t.bottom > r.bottom) r.bottom = t.bottom; 822 } 823 } 824 825 mPaddingChecked = true; 826 return (mConstantPadding = r); 827 } 828 829 public final void setConstantSize(boolean constant) { 830 mConstantSize = constant; 831 } 832 833 public final boolean isConstantSize() { 834 return mConstantSize; 835 } 836 837 public final int getConstantWidth() { 838 if (!mComputedConstantSize) { 839 computeConstantSize(); 840 } 841 842 return mConstantWidth; 843 } 844 845 public final int getConstantHeight() { 846 if (!mComputedConstantSize) { 847 computeConstantSize(); 848 } 849 850 return mConstantHeight; 851 } 852 853 public final int getConstantMinimumWidth() { 854 if (!mComputedConstantSize) { 855 computeConstantSize(); 856 } 857 858 return mConstantMinimumWidth; 859 } 860 861 public final int getConstantMinimumHeight() { 862 if (!mComputedConstantSize) { 863 computeConstantSize(); 864 } 865 866 return mConstantMinimumHeight; 867 } 868 869 protected void computeConstantSize() { 870 mComputedConstantSize = true; 871 872 createAllFutures(); 873 874 final int N = mNumChildren; 875 final Drawable[] drawables = mDrawables; 876 mConstantWidth = mConstantHeight = -1; 877 mConstantMinimumWidth = mConstantMinimumHeight = 0; 878 for (int i = 0; i < N; i++) { 879 final Drawable dr = drawables[i]; 880 int s = dr.getIntrinsicWidth(); 881 if (s > mConstantWidth) mConstantWidth = s; 882 s = dr.getIntrinsicHeight(); 883 if (s > mConstantHeight) mConstantHeight = s; 884 s = dr.getMinimumWidth(); 885 if (s > mConstantMinimumWidth) mConstantMinimumWidth = s; 886 s = dr.getMinimumHeight(); 887 if (s > mConstantMinimumHeight) mConstantMinimumHeight = s; 888 } 889 } 890 891 public final void setEnterFadeDuration(int duration) { 892 mEnterFadeDuration = duration; 893 } 894 895 public final int getEnterFadeDuration() { 896 return mEnterFadeDuration; 897 } 898 899 public final void setExitFadeDuration(int duration) { 900 mExitFadeDuration = duration; 901 } 902 903 public final int getExitFadeDuration() { 904 return mExitFadeDuration; 905 } 906 907 public final int getOpacity() { 908 if (mCheckedOpacity) { 909 return mOpacity; 910 } 911 912 createAllFutures(); 913 914 mCheckedOpacity = true; 915 916 final int N = mNumChildren; 917 final Drawable[] drawables = mDrawables; 918 int op = (N > 0) ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT; 919 for (int i = 1; i < N; i++) { 920 op = Drawable.resolveOpacity(op, drawables[i].getOpacity()); 921 } 922 923 mOpacity = op; 924 return op; 925 } 926 927 public final boolean isStateful() { 928 if (mCheckedStateful) { 929 return mStateful; 930 } 931 932 createAllFutures(); 933 934 mCheckedStateful = true; 935 936 final int N = mNumChildren; 937 final Drawable[] drawables = mDrawables; 938 for (int i = 0; i < N; i++) { 939 if (drawables[i].isStateful()) { 940 mStateful = true; 941 return true; 942 } 943 } 944 945 mStateful = false; 946 return false; 947 } 948 949 public void growArray(int oldSize, int newSize) { 950 Drawable[] newDrawables = new Drawable[newSize]; 951 System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize); 952 mDrawables = newDrawables; 953 } 954 955 public synchronized boolean canConstantState() { 956 if (mCheckedConstantState) { 957 return mCanConstantState; 958 } 959 960 createAllFutures(); 961 962 mCheckedConstantState = true; 963 964 final int N = mNumChildren; 965 final Drawable[] drawables = mDrawables; 966 for (int i = 0; i < N; i++) { 967 if (drawables[i].getConstantState() == null) { 968 mCanConstantState = false; 969 return false; 970 } 971 } 972 973 mCanConstantState = true; 974 return true; 975 } 976 977 /** 978 * Class capable of cloning a Drawable from another Drawable's 979 * ConstantState. 980 */ 981 private static class ConstantStateFuture { 982 private final ConstantState mConstantState; 983 984 private ConstantStateFuture(Drawable source) { 985 mConstantState = source.getConstantState(); 986 } 987 988 /** 989 * Obtains and prepares the Drawable represented by this future. 990 * 991 * @param state the container into which this future will be placed 992 * @return a prepared Drawable 993 */ 994 public Drawable get(DrawableContainerState state) { 995 final Drawable result; 996 if (state.mRes == null) { 997 result = mConstantState.newDrawable(); 998 } else if (state.mTheme == null) { 999 result = mConstantState.newDrawable(state.mRes); 1000 } else { 1001 result = mConstantState.newDrawable(state.mRes, state.mTheme); 1002 } 1003 result.setLayoutDirection(state.mLayoutDirection); 1004 result.setCallback(state.mOwner); 1005 1006 if (state.mMutated) { 1007 result.mutate(); 1008 } 1009 1010 return result; 1011 } 1012 1013 /** 1014 * Whether the constant state wrapped by this future can apply a 1015 * theme. 1016 */ 1017 public boolean canApplyTheme() { 1018 return mConstantState.canApplyTheme(); 1019 } 1020 } 1021 } 1022 1023 protected void setConstantState(DrawableContainerState state) { 1024 mDrawableContainerState = state; 1025 } 1026} 1027