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