LayerDrawable.java revision 0ef12aefe548e0d9472e2c23ab3374197a28b6bc
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.annotation.NonNull; 20import android.annotation.Nullable; 21import android.content.res.ColorStateList; 22import android.content.res.Resources; 23import android.content.res.Resources.Theme; 24import android.content.res.TypedArray; 25import android.graphics.Bitmap; 26import android.graphics.Canvas; 27import android.graphics.ColorFilter; 28import android.graphics.Outline; 29import android.graphics.PixelFormat; 30import android.graphics.PorterDuff.Mode; 31import android.graphics.Rect; 32import android.util.AttributeSet; 33import android.util.DisplayMetrics; 34import android.util.LayoutDirection; 35import android.view.Gravity; 36import android.view.View; 37 38import com.android.internal.R; 39 40import org.xmlpull.v1.XmlPullParser; 41import org.xmlpull.v1.XmlPullParserException; 42 43import java.io.IOException; 44import java.util.Collection; 45 46/** 47 * A Drawable that manages an array of other Drawables. These are drawn in array 48 * order, so the element with the largest index will be drawn on top. 49 * <p> 50 * It can be defined in an XML file with the <code><layer-list></code> element. 51 * Each Drawable in the layer is defined in a nested <code><item></code>. 52 * <p> 53 * For more information, see the guide to 54 * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>. 55 * 56 * @attr ref android.R.styleable#LayerDrawable_paddingMode 57 * @attr ref android.R.styleable#LayerDrawableItem_left 58 * @attr ref android.R.styleable#LayerDrawableItem_top 59 * @attr ref android.R.styleable#LayerDrawableItem_right 60 * @attr ref android.R.styleable#LayerDrawableItem_bottom 61 * @attr ref android.R.styleable#LayerDrawableItem_start 62 * @attr ref android.R.styleable#LayerDrawableItem_end 63 * @attr ref android.R.styleable#LayerDrawableItem_width 64 * @attr ref android.R.styleable#LayerDrawableItem_height 65 * @attr ref android.R.styleable#LayerDrawableItem_gravity 66 * @attr ref android.R.styleable#LayerDrawableItem_drawable 67 * @attr ref android.R.styleable#LayerDrawableItem_id 68*/ 69public class LayerDrawable extends Drawable implements Drawable.Callback { 70 /** 71 * Padding mode used to nest each layer inside the padding of the previous 72 * layer. 73 * 74 * @see #setPaddingMode(int) 75 */ 76 public static final int PADDING_MODE_NEST = 0; 77 78 /** 79 * Padding mode used to stack each layer directly atop the previous layer. 80 * 81 * @see #setPaddingMode(int) 82 */ 83 public static final int PADDING_MODE_STACK = 1; 84 85 /** 86 * Value used for undefined start and end insets. 87 * 88 * @see #getLayerInsetStart(int) 89 * @see #getLayerInsetEnd(int) 90 */ 91 public static final int UNDEFINED_INSET = Integer.MIN_VALUE; 92 93 LayerState mLayerState; 94 95 private int[] mPaddingL; 96 private int[] mPaddingT; 97 private int[] mPaddingR; 98 private int[] mPaddingB; 99 100 private final Rect mTmpRect = new Rect(); 101 private final Rect mTmpOutRect = new Rect(); 102 private final Rect mTmpContainer = new Rect(); 103 private Rect mHotspotBounds; 104 private boolean mMutated; 105 106 /** 107 * Creates a new layer drawable with the list of specified layers. 108 * 109 * @param layers a list of drawables to use as layers in this new drawable, 110 * must be non-null 111 */ 112 public LayerDrawable(@NonNull Drawable[] layers) { 113 this(layers, null); 114 } 115 116 /** 117 * Creates a new layer drawable with the specified list of layers and the 118 * specified constant state. 119 * 120 * @param layers The list of layers to add to this drawable. 121 * @param state The constant drawable state. 122 */ 123 LayerDrawable(@NonNull Drawable[] layers, @Nullable LayerState state) { 124 this(state, null); 125 126 if (layers == null) { 127 throw new IllegalArgumentException("layers must be non-null"); 128 } 129 130 final int length = layers.length; 131 final ChildDrawable[] r = new ChildDrawable[length]; 132 for (int i = 0; i < length; i++) { 133 r[i] = new ChildDrawable(mLayerState.mDensity); 134 r[i].mDrawable = layers[i]; 135 layers[i].setCallback(this); 136 mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations(); 137 } 138 mLayerState.mNum = length; 139 mLayerState.mChildren = r; 140 141 ensurePadding(); 142 refreshPadding(); 143 } 144 145 LayerDrawable() { 146 this((LayerState) null, null); 147 } 148 149 /** 150 * The one constructor to rule them all. This is called by all public 151 * constructors to set the state and initialize local properties. 152 */ 153 LayerDrawable(@Nullable LayerState state, @Nullable Resources res) { 154 mLayerState = createConstantState(state, res); 155 if (mLayerState.mNum > 0) { 156 ensurePadding(); 157 refreshPadding(); 158 } 159 } 160 161 LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) { 162 return new LayerState(state, this, res); 163 } 164 165 @Override 166 public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, 167 @NonNull AttributeSet attrs, @Nullable Theme theme) 168 throws XmlPullParserException, IOException { 169 super.inflate(r, parser, attrs, theme); 170 171 final LayerState state = mLayerState; 172 if (state == null) { 173 return; 174 } 175 176 // The density may have changed since the last update. This will 177 // apply scaling to any existing constant state properties. 178 final int density = Drawable.resolveDensity(r, 0); 179 state.setDensity(density); 180 181 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable); 182 updateStateFromTypedArray(a); 183 a.recycle(); 184 185 final ChildDrawable[] array = state.mChildren; 186 final int N = state.mNum; 187 for (int i = 0; i < N; i++) { 188 final ChildDrawable layer = array[i]; 189 layer.setDensity(density); 190 } 191 192 inflateLayers(r, parser, attrs, theme); 193 194 ensurePadding(); 195 refreshPadding(); 196 } 197 198 @Override 199 public void applyTheme(@NonNull Theme t) { 200 super.applyTheme(t); 201 202 final LayerState state = mLayerState; 203 if (state == null) { 204 return; 205 } 206 207 final int density = Drawable.resolveDensity(t.getResources(), 0); 208 state.setDensity(density); 209 210 if (state.mThemeAttrs != null) { 211 final TypedArray a = t.resolveAttributes( 212 state.mThemeAttrs, R.styleable.LayerDrawable); 213 updateStateFromTypedArray(a); 214 a.recycle(); 215 } 216 217 final ChildDrawable[] array = state.mChildren; 218 final int N = state.mNum; 219 for (int i = 0; i < N; i++) { 220 final ChildDrawable layer = array[i]; 221 layer.setDensity(density); 222 223 if (layer.mThemeAttrs != null) { 224 final TypedArray a = t.resolveAttributes( 225 layer.mThemeAttrs, R.styleable.LayerDrawableItem); 226 updateLayerFromTypedArray(layer, a); 227 a.recycle(); 228 } 229 230 final Drawable d = layer.mDrawable; 231 if (d != null && d.canApplyTheme()) { 232 d.applyTheme(t); 233 234 // Update cached mask of child changing configurations. 235 state.mChildrenChangingConfigurations |= d.getChangingConfigurations(); 236 } 237 } 238 } 239 240 /** 241 * Inflates child layers using the specified parser. 242 */ 243 private void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser, 244 @NonNull AttributeSet attrs, @Nullable Theme theme) 245 throws XmlPullParserException, IOException { 246 final LayerState state = mLayerState; 247 248 final int innerDepth = parser.getDepth() + 1; 249 int type; 250 int depth; 251 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 252 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { 253 if (type != XmlPullParser.START_TAG) { 254 continue; 255 } 256 257 if (depth > innerDepth || !parser.getName().equals("item")) { 258 continue; 259 } 260 261 final ChildDrawable layer = new ChildDrawable(state.mDensity); 262 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem); 263 updateLayerFromTypedArray(layer, a); 264 a.recycle(); 265 266 // If the layer doesn't have a drawable or unresolved theme 267 // attribute for a drawable, attempt to parse one from the child 268 // element. 269 if (layer.mDrawable == null && (layer.mThemeAttrs == null || 270 layer.mThemeAttrs[R.styleable.LayerDrawableItem_drawable] == 0)) { 271 while ((type = parser.next()) == XmlPullParser.TEXT) { 272 } 273 if (type != XmlPullParser.START_TAG) { 274 throw new XmlPullParserException(parser.getPositionDescription() 275 + ": <item> tag requires a 'drawable' attribute or " 276 + "child tag defining a drawable"); 277 } 278 layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme); 279 } 280 281 if (layer.mDrawable != null) { 282 state.mChildrenChangingConfigurations |= 283 layer.mDrawable.getChangingConfigurations(); 284 layer.mDrawable.setCallback(this); 285 } 286 287 addLayer(layer); 288 } 289 } 290 291 /** 292 * Initializes the constant state from the values in the typed array. 293 */ 294 private void updateStateFromTypedArray(@NonNull TypedArray a) { 295 final LayerState state = mLayerState; 296 297 // Account for any configuration changes. 298 state.mChangingConfigurations |= a.getChangingConfigurations(); 299 300 // Extract the theme attributes, if any. 301 state.mThemeAttrs = a.extractThemeAttrs(); 302 303 final int N = a.getIndexCount(); 304 for (int i = 0; i < N; i++) { 305 final int attr = a.getIndex(i); 306 switch (attr) { 307 case R.styleable.LayerDrawable_opacity: 308 state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride); 309 break; 310 case R.styleable.LayerDrawable_paddingTop: 311 state.mPaddingTop = a.getDimensionPixelOffset(attr, state.mPaddingTop); 312 break; 313 case R.styleable.LayerDrawable_paddingBottom: 314 state.mPaddingBottom = a.getDimensionPixelOffset(attr, state.mPaddingBottom); 315 break; 316 case R.styleable.LayerDrawable_paddingLeft: 317 state.mPaddingLeft = a.getDimensionPixelOffset(attr, state.mPaddingLeft); 318 break; 319 case R.styleable.LayerDrawable_paddingRight: 320 state.mPaddingRight = a.getDimensionPixelOffset(attr, state.mPaddingRight); 321 break; 322 case R.styleable.LayerDrawable_paddingStart: 323 state.mPaddingStart = a.getDimensionPixelOffset(attr, state.mPaddingStart); 324 break; 325 case R.styleable.LayerDrawable_paddingEnd: 326 state.mPaddingEnd = a.getDimensionPixelOffset(attr, state.mPaddingEnd); 327 break; 328 case R.styleable.LayerDrawable_autoMirrored: 329 state.mAutoMirrored = a.getBoolean(attr, state.mAutoMirrored); 330 break; 331 case R.styleable.LayerDrawable_paddingMode: 332 state.mPaddingMode = a.getInteger(attr, state.mPaddingMode); 333 break; 334 } 335 } 336 } 337 338 private void updateLayerFromTypedArray(@NonNull ChildDrawable layer, @NonNull TypedArray a) { 339 final LayerState state = mLayerState; 340 341 // Account for any configuration changes. 342 state.mChildrenChangingConfigurations |= a.getChangingConfigurations(); 343 344 // Extract the theme attributes, if any. 345 layer.mThemeAttrs = a.extractThemeAttrs(); 346 347 final int N = a.getIndexCount(); 348 for (int i = 0; i < N; i++) { 349 final int attr = a.getIndex(i); 350 switch (attr) { 351 case R.styleable.LayerDrawableItem_left: 352 layer.mInsetL = a.getDimensionPixelOffset(attr, layer.mInsetL); 353 break; 354 case R.styleable.LayerDrawableItem_top: 355 layer.mInsetT = a.getDimensionPixelOffset(attr, layer.mInsetT); 356 break; 357 case R.styleable.LayerDrawableItem_right: 358 layer.mInsetR = a.getDimensionPixelOffset(attr, layer.mInsetR); 359 break; 360 case R.styleable.LayerDrawableItem_bottom: 361 layer.mInsetB = a.getDimensionPixelOffset(attr, layer.mInsetB); 362 break; 363 case R.styleable.LayerDrawableItem_start: 364 layer.mInsetS = a.getDimensionPixelOffset(attr, layer.mInsetS); 365 break; 366 case R.styleable.LayerDrawableItem_end: 367 layer.mInsetE = a.getDimensionPixelOffset(attr, layer.mInsetE); 368 break; 369 case R.styleable.LayerDrawableItem_width: 370 layer.mWidth = a.getDimensionPixelSize(attr, layer.mWidth); 371 break; 372 case R.styleable.LayerDrawableItem_height: 373 layer.mHeight = a.getDimensionPixelSize(attr, layer.mHeight); 374 break; 375 case R.styleable.LayerDrawableItem_gravity: 376 layer.mGravity = a.getInteger(attr, layer.mGravity); 377 break; 378 case R.styleable.LayerDrawableItem_id: 379 layer.mId = a.getResourceId(attr, layer.mId); 380 break; 381 } 382 } 383 384 final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable); 385 if (dr != null) { 386 layer.mDrawable = dr; 387 } 388 } 389 390 @Override 391 public boolean canApplyTheme() { 392 return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme(); 393 } 394 395 /** 396 * @hide 397 */ 398 @Override 399 public boolean isProjected() { 400 if (super.isProjected()) { 401 return true; 402 } 403 404 final ChildDrawable[] layers = mLayerState.mChildren; 405 final int N = mLayerState.mNum; 406 for (int i = 0; i < N; i++) { 407 if (layers[i].mDrawable.isProjected()) { 408 return true; 409 } 410 } 411 412 return false; 413 } 414 415 /** 416 * Adds a new layer at the end of list of layers and returns its index. 417 * 418 * @param layer The layer to add. 419 * @return The index of the layer. 420 */ 421 int addLayer(@NonNull ChildDrawable layer) { 422 final LayerState st = mLayerState; 423 final int N = st.mChildren != null ? st.mChildren.length : 0; 424 final int i = st.mNum; 425 if (i >= N) { 426 final ChildDrawable[] nu = new ChildDrawable[N + 10]; 427 if (i > 0) { 428 System.arraycopy(st.mChildren, 0, nu, 0, i); 429 } 430 431 st.mChildren = nu; 432 } 433 434 st.mChildren[i] = layer; 435 st.mNum++; 436 st.invalidateCache(); 437 return i; 438 } 439 440 /** 441 * Add a new layer to this drawable. The new layer is identified by an id. 442 * 443 * @param dr The drawable to add as a layer. 444 * @param themeAttrs Theme attributes extracted from the layer. 445 * @param id The id of the new layer. 446 * @param left The left padding of the new layer. 447 * @param top The top padding of the new layer. 448 * @param right The right padding of the new layer. 449 * @param bottom The bottom padding of the new layer. 450 */ 451 ChildDrawable addLayer(Drawable dr, int[] themeAttrs, int id, 452 int left, int top, int right, int bottom) { 453 final ChildDrawable childDrawable = createLayer(dr); 454 childDrawable.mId = id; 455 childDrawable.mThemeAttrs = themeAttrs; 456 childDrawable.mDrawable.setAutoMirrored(isAutoMirrored()); 457 childDrawable.mInsetL = left; 458 childDrawable.mInsetT = top; 459 childDrawable.mInsetR = right; 460 childDrawable.mInsetB = bottom; 461 462 addLayer(childDrawable); 463 464 mLayerState.mChildrenChangingConfigurations |= dr.getChangingConfigurations(); 465 dr.setCallback(this); 466 467 return childDrawable; 468 } 469 470 private ChildDrawable createLayer(Drawable dr) { 471 final ChildDrawable layer = new ChildDrawable(mLayerState.mDensity); 472 layer.mDrawable = dr; 473 return layer; 474 } 475 476 /** 477 * Adds a new layer containing the specified {@code drawable} to the end of 478 * the layer list and returns its index. 479 * 480 * @param dr The drawable to add as a new layer. 481 * @return The index of the new layer. 482 */ 483 public int addLayer(Drawable dr) { 484 final ChildDrawable layer = createLayer(dr); 485 final int index = addLayer(layer); 486 ensurePadding(); 487 refreshChildPadding(index, layer); 488 return index; 489 } 490 491 /** 492 * Looks for a layer with the given ID and returns its {@link Drawable}. 493 * <p> 494 * If multiple layers are found for the given ID, returns the 495 * {@link Drawable} for the matching layer at the highest index. 496 * 497 * @param id The layer ID to search for. 498 * @return The {@link Drawable} for the highest-indexed layer that has the 499 * given ID, or null if not found. 500 */ 501 public Drawable findDrawableByLayerId(int id) { 502 final ChildDrawable[] layers = mLayerState.mChildren; 503 for (int i = mLayerState.mNum - 1; i >= 0; i--) { 504 if (layers[i].mId == id) { 505 return layers[i].mDrawable; 506 } 507 } 508 509 return null; 510 } 511 512 /** 513 * Sets the ID of a layer. 514 * 515 * @param index The index of the layer to modify, must be in the range 516 * {@code 0...getNumberOfLayers()-1}. 517 * @param id The id to assign to the layer. 518 * 519 * @see #getId(int) 520 * @attr ref android.R.styleable#LayerDrawableItem_id 521 */ 522 public void setId(int index, int id) { 523 mLayerState.mChildren[index].mId = id; 524 } 525 526 /** 527 * Returns the ID of the specified layer. 528 * 529 * @param index The index of the layer, must be in the range 530 * {@code 0...getNumberOfLayers()-1}. 531 * @return The id of the layer or {@link android.view.View#NO_ID} if the 532 * layer has no id. 533 * 534 * @see #setId(int, int) 535 * @attr ref android.R.styleable#LayerDrawableItem_id 536 */ 537 public int getId(int index) { 538 if (index >= mLayerState.mNum) { 539 throw new IndexOutOfBoundsException(); 540 } 541 return mLayerState.mChildren[index].mId; 542 } 543 544 /** 545 * Returns the number of layers contained within this layer drawable. 546 * 547 * @return The number of layers. 548 */ 549 public int getNumberOfLayers() { 550 return mLayerState.mNum; 551 } 552 553 /** 554 * Replaces the {@link Drawable} for the layer with the given id. 555 * 556 * @param id The layer ID to search for. 557 * @param drawable The replacement {@link Drawable}. 558 * @return Whether the {@link Drawable} was replaced (could return false if 559 * the id was not found). 560 */ 561 public boolean setDrawableByLayerId(int id, Drawable drawable) { 562 final int index = findIndexByLayerId(id); 563 if (index < 0) { 564 return false; 565 } 566 567 setDrawable(index, drawable); 568 return true; 569 } 570 571 /** 572 * Returns the layer with the specified {@code id}. 573 * <p> 574 * If multiple layers have the same ID, returns the layer with the lowest 575 * index. 576 * 577 * @param id The ID of the layer to return. 578 * @return The index of the layer with the specified ID. 579 */ 580 public int findIndexByLayerId(int id) { 581 final ChildDrawable[] layers = mLayerState.mChildren; 582 final int N = mLayerState.mNum; 583 for (int i = 0; i < N; i++) { 584 final ChildDrawable childDrawable = layers[i]; 585 if (childDrawable.mId == id) { 586 return i; 587 } 588 } 589 590 return -1; 591 } 592 593 /** 594 * Sets the drawable for the layer at the specified index. 595 * 596 * @param index The index of the layer to modify, must be in the range 597 * {@code 0...getNumberOfLayers()-1}. 598 * @param drawable The drawable to set for the layer. 599 * 600 * @see #getDrawable(int) 601 * @attr ref android.R.styleable#LayerDrawableItem_drawable 602 */ 603 public void setDrawable(int index, Drawable drawable) { 604 if (index >= mLayerState.mNum) { 605 throw new IndexOutOfBoundsException(); 606 } 607 608 final ChildDrawable[] layers = mLayerState.mChildren; 609 final ChildDrawable childDrawable = layers[index]; 610 if (childDrawable.mDrawable != null) { 611 if (drawable != null) { 612 final Rect bounds = childDrawable.mDrawable.getBounds(); 613 drawable.setBounds(bounds); 614 } 615 616 childDrawable.mDrawable.setCallback(null); 617 } 618 619 if (drawable != null) { 620 drawable.setCallback(this); 621 } 622 623 childDrawable.mDrawable = drawable; 624 mLayerState.invalidateCache(); 625 626 refreshChildPadding(index, childDrawable); 627 } 628 629 /** 630 * Returns the drawable for the layer at the specified index. 631 * 632 * @param index The index of the layer, must be in the range 633 * {@code 0...getNumberOfLayers()-1}. 634 * @return The {@link Drawable} at the specified layer index. 635 * 636 * @see #setDrawable(int, Drawable) 637 * @attr ref android.R.styleable#LayerDrawableItem_drawable 638 */ 639 public Drawable getDrawable(int index) { 640 if (index >= mLayerState.mNum) { 641 throw new IndexOutOfBoundsException(); 642 } 643 return mLayerState.mChildren[index].mDrawable; 644 } 645 646 /** 647 * Sets an explicit size for the specified layer. 648 * <p> 649 * <strong>Note:</strong> Setting an explicit layer size changes the 650 * default layer gravity behavior. See {@link #setLayerGravity(int, int)} 651 * for more information. 652 * 653 * @param index the index of the layer to adjust 654 * @param w width in pixels, or -1 to use the intrinsic width 655 * @param h height in pixels, or -1 to use the intrinsic height 656 * @see #getLayerWidth(int) 657 * @see #getLayerHeight(int) 658 * @attr ref android.R.styleable#LayerDrawableItem_width 659 * @attr ref android.R.styleable#LayerDrawableItem_height 660 */ 661 public void setLayerSize(int index, int w, int h) { 662 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 663 childDrawable.mWidth = w; 664 childDrawable.mHeight = h; 665 } 666 667 /** 668 * @param index the index of the layer to adjust 669 * @param w width in pixels, or -1 to use the intrinsic width 670 * @attr ref android.R.styleable#LayerDrawableItem_width 671 */ 672 public void setLayerWidth(int index, int w) { 673 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 674 childDrawable.mWidth = w; 675 } 676 677 /** 678 * @param index the index of the drawable to adjust 679 * @return the explicit width of the layer, or -1 if not specified 680 * @see #setLayerSize(int, int, int) 681 * @attr ref android.R.styleable#LayerDrawableItem_width 682 */ 683 public int getLayerWidth(int index) { 684 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 685 return childDrawable.mWidth; 686 } 687 688 /** 689 * @param index the index of the layer to adjust 690 * @param h height in pixels, or -1 to use the intrinsic height 691 * @attr ref android.R.styleable#LayerDrawableItem_height 692 */ 693 public void setLayerHeight(int index, int h) { 694 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 695 childDrawable.mHeight = h; 696 } 697 698 /** 699 * @param index the index of the drawable to adjust 700 * @return the explicit height of the layer, or -1 if not specified 701 * @see #setLayerSize(int, int, int) 702 * @attr ref android.R.styleable#LayerDrawableItem_height 703 */ 704 public int getLayerHeight(int index) { 705 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 706 return childDrawable.mHeight; 707 } 708 709 /** 710 * Sets the gravity used to position or stretch the specified layer within 711 * its container. Gravity is applied after any layer insets (see 712 * {@link #setLayerInset(int, int, int, int, int)}) or padding (see 713 * {@link #setPaddingMode(int)}). 714 * <p> 715 * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default 716 * behavior depends on whether an explicit width or height has been set 717 * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set, 718 * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or 719 * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction 720 * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}. 721 * 722 * @param index the index of the drawable to adjust 723 * @param gravity the gravity to set for the layer 724 * 725 * @see #getLayerGravity(int) 726 * @attr ref android.R.styleable#LayerDrawableItem_gravity 727 */ 728 public void setLayerGravity(int index, int gravity) { 729 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 730 childDrawable.mGravity = gravity; 731 } 732 733 /** 734 * @param index the index of the layer 735 * @return the gravity used to position or stretch the specified layer 736 * within its container 737 * 738 * @see #setLayerGravity(int, int) 739 * @attr ref android.R.styleable#LayerDrawableItem_gravity 740 */ 741 public int getLayerGravity(int index) { 742 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 743 return childDrawable.mGravity; 744 } 745 746 /** 747 * Specifies the insets in pixels for the drawable at the specified index. 748 * 749 * @param index the index of the drawable to adjust 750 * @param l number of pixels to add to the left bound 751 * @param t number of pixels to add to the top bound 752 * @param r number of pixels to subtract from the right bound 753 * @param b number of pixels to subtract from the bottom bound 754 * 755 * @attr ref android.R.styleable#LayerDrawableItem_left 756 * @attr ref android.R.styleable#LayerDrawableItem_top 757 * @attr ref android.R.styleable#LayerDrawableItem_right 758 * @attr ref android.R.styleable#LayerDrawableItem_bottom 759 */ 760 public void setLayerInset(int index, int l, int t, int r, int b) { 761 setLayerInsetInternal(index, l, t, r, b, UNDEFINED_INSET, UNDEFINED_INSET); 762 } 763 764 /** 765 * Specifies the relative insets in pixels for the drawable at the 766 * specified index. 767 * 768 * @param index the index of the layer to adjust 769 * @param s number of pixels to inset from the start bound 770 * @param t number of pixels to inset from the top bound 771 * @param e number of pixels to inset from the end bound 772 * @param b number of pixels to inset from the bottom bound 773 * 774 * @attr ref android.R.styleable#LayerDrawableItem_start 775 * @attr ref android.R.styleable#LayerDrawableItem_top 776 * @attr ref android.R.styleable#LayerDrawableItem_end 777 * @attr ref android.R.styleable#LayerDrawableItem_bottom 778 */ 779 public void setLayerInsetRelative(int index, int s, int t, int e, int b) { 780 setLayerInsetInternal(index, 0, t, 0, b, s, e); 781 } 782 783 /** 784 * @param index the index of the layer to adjust 785 * @param l number of pixels to inset from the left bound 786 * @attr ref android.R.styleable#LayerDrawableItem_left 787 */ 788 public void setLayerInsetLeft(int index, int l) { 789 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 790 childDrawable.mInsetL = l; 791 } 792 793 /** 794 * @param index the index of the layer 795 * @return number of pixels to inset from the left bound 796 * @attr ref android.R.styleable#LayerDrawableItem_left 797 */ 798 public int getLayerInsetLeft(int index) { 799 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 800 return childDrawable.mInsetL; 801 } 802 803 /** 804 * @param index the index of the layer to adjust 805 * @param r number of pixels to inset from the right bound 806 * @attr ref android.R.styleable#LayerDrawableItem_right 807 */ 808 public void setLayerInsetRight(int index, int r) { 809 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 810 childDrawable.mInsetR = r; 811 } 812 813 /** 814 * @param index the index of the layer 815 * @return number of pixels to inset from the right bound 816 * @attr ref android.R.styleable#LayerDrawableItem_right 817 */ 818 public int getLayerInsetRight(int index) { 819 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 820 return childDrawable.mInsetR; 821 } 822 823 /** 824 * @param index the index of the layer to adjust 825 * @param t number of pixels to inset from the top bound 826 * @attr ref android.R.styleable#LayerDrawableItem_top 827 */ 828 public void setLayerInsetTop(int index, int t) { 829 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 830 childDrawable.mInsetT = t; 831 } 832 833 /** 834 * @param index the index of the layer 835 * @return number of pixels to inset from the top bound 836 * @attr ref android.R.styleable#LayerDrawableItem_top 837 */ 838 public int getLayerInsetTop(int index) { 839 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 840 return childDrawable.mInsetT; 841 } 842 843 /** 844 * @param index the index of the layer to adjust 845 * @param b number of pixels to inset from the bottom bound 846 * @attr ref android.R.styleable#LayerDrawableItem_bottom 847 */ 848 public void setLayerInsetBottom(int index, int b) { 849 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 850 childDrawable.mInsetB = b; 851 } 852 853 /** 854 * @param index the index of the layer 855 * @return number of pixels to inset from the bottom bound 856 * @attr ref android.R.styleable#LayerDrawableItem_bottom 857 */ 858 public int getLayerInsetBottom(int index) { 859 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 860 return childDrawable.mInsetB; 861 } 862 863 /** 864 * @param index the index of the layer to adjust 865 * @param s number of pixels to inset from the start bound 866 * @attr ref android.R.styleable#LayerDrawableItem_start 867 */ 868 public void setLayerInsetStart(int index, int s) { 869 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 870 childDrawable.mInsetS = s; 871 } 872 873 /** 874 * @param index the index of the layer 875 * @return the number of pixels to inset from the start bound, or 876 * {@link #UNDEFINED_INSET} if not specified 877 * @attr ref android.R.styleable#LayerDrawableItem_start 878 */ 879 public int getLayerInsetStart(int index) { 880 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 881 return childDrawable.mInsetS; 882 } 883 884 /** 885 * @param index the index of the layer to adjust 886 * @param e number of pixels to inset from the end bound, or 887 * {@link #UNDEFINED_INSET} if not specified 888 * @attr ref android.R.styleable#LayerDrawableItem_end 889 */ 890 public void setLayerInsetEnd(int index, int e) { 891 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 892 childDrawable.mInsetE = e; 893 } 894 895 /** 896 * @param index the index of the layer 897 * @return number of pixels to inset from the end bound 898 * @attr ref android.R.styleable#LayerDrawableItem_end 899 */ 900 public int getLayerInsetEnd(int index) { 901 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 902 return childDrawable.mInsetE; 903 } 904 905 private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) { 906 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 907 childDrawable.mInsetL = l; 908 childDrawable.mInsetT = t; 909 childDrawable.mInsetR = r; 910 childDrawable.mInsetB = b; 911 childDrawable.mInsetS = s; 912 childDrawable.mInsetE = e; 913 } 914 915 /** 916 * Specifies how layer padding should affect the bounds of subsequent 917 * layers. The default value is {@link #PADDING_MODE_NEST}. 918 * 919 * @param mode padding mode, one of: 920 * <ul> 921 * <li>{@link #PADDING_MODE_NEST} to nest each layer inside the 922 * padding of the previous layer 923 * <li>{@link #PADDING_MODE_STACK} to stack each layer directly 924 * atop the previous layer 925 * </ul> 926 * 927 * @see #getPaddingMode() 928 * @attr ref android.R.styleable#LayerDrawable_paddingMode 929 */ 930 public void setPaddingMode(int mode) { 931 if (mLayerState.mPaddingMode != mode) { 932 mLayerState.mPaddingMode = mode; 933 } 934 } 935 936 /** 937 * @return the current padding mode 938 * 939 * @see #setPaddingMode(int) 940 * @attr ref android.R.styleable#LayerDrawable_paddingMode 941 */ 942 public int getPaddingMode() { 943 return mLayerState.mPaddingMode; 944 } 945 946 @Override 947 public void invalidateDrawable(Drawable who) { 948 invalidateSelf(); 949 } 950 951 @Override 952 public void scheduleDrawable(Drawable who, Runnable what, long when) { 953 scheduleSelf(what, when); 954 } 955 956 @Override 957 public void unscheduleDrawable(Drawable who, Runnable what) { 958 unscheduleSelf(what); 959 } 960 961 @Override 962 public void draw(Canvas canvas) { 963 final ChildDrawable[] array = mLayerState.mChildren; 964 final int N = mLayerState.mNum; 965 for (int i = 0; i < N; i++) { 966 final Drawable dr = array[i].mDrawable; 967 if (dr != null) { 968 dr.draw(canvas); 969 } 970 } 971 } 972 973 @Override 974 public int getChangingConfigurations() { 975 return super.getChangingConfigurations() | mLayerState.getChangingConfigurations(); 976 } 977 978 @Override 979 public boolean getPadding(Rect padding) { 980 final LayerState layerState = mLayerState; 981 if (layerState.mPaddingMode == PADDING_MODE_NEST) { 982 computeNestedPadding(padding); 983 } else { 984 computeStackedPadding(padding); 985 } 986 987 final int paddingT = layerState.mPaddingTop; 988 final int paddingB = layerState.mPaddingBottom; 989 990 // Resolve padding for RTL. Relative padding overrides absolute 991 // padding. 992 final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL; 993 final int paddingRtlL = isLayoutRtl ? layerState.mPaddingEnd : layerState.mPaddingStart; 994 final int paddingRtlR = isLayoutRtl ? layerState.mPaddingStart : layerState.mPaddingEnd; 995 final int paddingL = paddingRtlL >= 0 ? paddingRtlL : layerState.mPaddingLeft; 996 final int paddingR = paddingRtlR >= 0 ? paddingRtlR : layerState.mPaddingRight; 997 998 // If padding was explicitly specified (e.g. not -1) then override the 999 // computed padding in that dimension. 1000 if (paddingL >= 0) { 1001 padding.left = paddingL; 1002 } 1003 1004 if (paddingT >= 0) { 1005 padding.top = paddingT; 1006 } 1007 1008 if (paddingR >= 0) { 1009 padding.right = paddingR; 1010 } 1011 1012 if (paddingB >= 0) { 1013 padding.bottom = paddingB; 1014 } 1015 1016 return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0; 1017 } 1018 1019 /** 1020 * Sets the absolute padding. 1021 * <p> 1022 * If padding in a dimension is specified as {@code -1}, the resolved 1023 * padding will use the value computed according to the padding mode (see 1024 * {@link #setPaddingMode(int)}). 1025 * <p> 1026 * Calling this method clears any relative padding values previously set 1027 * using {@link #setPaddingRelative(int, int, int, int)}. 1028 * 1029 * @param left the left padding in pixels, or -1 to use computed padding 1030 * @param top the top padding in pixels, or -1 to use computed padding 1031 * @param right the right padding in pixels, or -1 to use computed padding 1032 * @param bottom the bottom padding in pixels, or -1 to use computed 1033 * padding 1034 * @attr ref android.R.styleable#LayerDrawable_paddingLeft 1035 * @attr ref android.R.styleable#LayerDrawable_paddingTop 1036 * @attr ref android.R.styleable#LayerDrawable_paddingRight 1037 * @attr ref android.R.styleable#LayerDrawable_paddingBottom 1038 * @see #setPaddingRelative(int, int, int, int) 1039 */ 1040 public void setPadding(int left, int top, int right, int bottom) { 1041 final LayerState layerState = mLayerState; 1042 layerState.mPaddingLeft = left; 1043 layerState.mPaddingTop = top; 1044 layerState.mPaddingRight = right; 1045 layerState.mPaddingBottom = bottom; 1046 1047 // Clear relative padding values. 1048 layerState.mPaddingStart = -1; 1049 layerState.mPaddingEnd = -1; 1050 } 1051 1052 /** 1053 * Sets the relative padding. 1054 * <p> 1055 * If padding in a dimension is specified as {@code -1}, the resolved 1056 * padding will use the value computed according to the padding mode (see 1057 * {@link #setPaddingMode(int)}). 1058 * <p> 1059 * Calling this method clears any absolute padding values previously set 1060 * using {@link #setPadding(int, int, int, int)}. 1061 * 1062 * @param start the start padding in pixels, or -1 to use computed padding 1063 * @param top the top padding in pixels, or -1 to use computed padding 1064 * @param end the end padding in pixels, or -1 to use computed padding 1065 * @param bottom the bottom padding in pixels, or -1 to use computed 1066 * padding 1067 * @attr ref android.R.styleable#LayerDrawable_paddingStart 1068 * @attr ref android.R.styleable#LayerDrawable_paddingTop 1069 * @attr ref android.R.styleable#LayerDrawable_paddingEnd 1070 * @attr ref android.R.styleable#LayerDrawable_paddingBottom 1071 * @see #setPadding(int, int, int, int) 1072 */ 1073 public void setPaddingRelative(int start, int top, int end, int bottom) { 1074 final LayerState layerState = mLayerState; 1075 layerState.mPaddingStart = start; 1076 layerState.mPaddingTop = top; 1077 layerState.mPaddingEnd = end; 1078 layerState.mPaddingBottom = bottom; 1079 1080 // Clear absolute padding values. 1081 layerState.mPaddingLeft = -1; 1082 layerState.mPaddingRight = -1; 1083 } 1084 1085 /** 1086 * Returns the left padding in pixels. 1087 * <p> 1088 * A return value of {@code -1} means there is no explicit padding set for 1089 * this dimension. As a result, the value for this dimension returned by 1090 * {@link #getPadding(Rect)} will be computed from the child layers 1091 * according to the padding mode (see {@link #getPaddingMode()}. 1092 * 1093 * @return the left padding in pixels, or -1 if not explicitly specified 1094 * @see #setPadding(int, int, int, int) 1095 * @see #getPadding(Rect) 1096 */ 1097 public int getLeftPadding() { 1098 return mLayerState.mPaddingLeft; 1099 } 1100 1101 /** 1102 * Returns the right padding in pixels. 1103 * <p> 1104 * A return value of {@code -1} means there is no explicit padding set for 1105 * this dimension. As a result, the value for this dimension returned by 1106 * {@link #getPadding(Rect)} will be computed from the child layers 1107 * according to the padding mode (see {@link #getPaddingMode()}. 1108 * 1109 * @return the right padding in pixels, or -1 if not explicitly specified 1110 * @see #setPadding(int, int, int, int) 1111 * @see #getPadding(Rect) 1112 */ 1113 public int getRightPadding() { 1114 return mLayerState.mPaddingRight; 1115 } 1116 1117 /** 1118 * Returns the start padding in pixels. 1119 * <p> 1120 * A return value of {@code -1} means there is no explicit padding set for 1121 * this dimension. As a result, the value for this dimension returned by 1122 * {@link #getPadding(Rect)} will be computed from the child layers 1123 * according to the padding mode (see {@link #getPaddingMode()}. 1124 * 1125 * @return the start padding in pixels, or -1 if not explicitly specified 1126 * @see #setPaddingRelative(int, int, int, int) 1127 * @see #getPadding(Rect) 1128 */ 1129 public int getStartPadding() { 1130 return mLayerState.mPaddingStart; 1131 } 1132 1133 /** 1134 * Returns the end padding in pixels. 1135 * <p> 1136 * A return value of {@code -1} means there is no explicit padding set for 1137 * this dimension. As a result, the value for this dimension returned by 1138 * {@link #getPadding(Rect)} will be computed from the child layers 1139 * according to the padding mode (see {@link #getPaddingMode()}. 1140 * 1141 * @return the end padding in pixels, or -1 if not explicitly specified 1142 * @see #setPaddingRelative(int, int, int, int) 1143 * @see #getPadding(Rect) 1144 */ 1145 public int getEndPadding() { 1146 return mLayerState.mPaddingEnd; 1147 } 1148 1149 /** 1150 * Returns the top padding in pixels. 1151 * <p> 1152 * A return value of {@code -1} means there is no explicit padding set for 1153 * this dimension. As a result, the value for this dimension returned by 1154 * {@link #getPadding(Rect)} will be computed from the child layers 1155 * according to the padding mode (see {@link #getPaddingMode()}. 1156 * 1157 * @return the top padding in pixels, or -1 if not explicitly specified 1158 * @see #setPadding(int, int, int, int) 1159 * @see #setPaddingRelative(int, int, int, int) 1160 * @see #getPadding(Rect) 1161 */ 1162 public int getTopPadding() { 1163 return mLayerState.mPaddingTop; 1164 } 1165 1166 /** 1167 * Returns the bottom padding in pixels. 1168 * <p> 1169 * A return value of {@code -1} means there is no explicit padding set for 1170 * this dimension. As a result, the value for this dimension returned by 1171 * {@link #getPadding(Rect)} will be computed from the child layers 1172 * according to the padding mode (see {@link #getPaddingMode()}. 1173 * 1174 * @return the bottom padding in pixels, or -1 if not explicitly specified 1175 * @see #setPadding(int, int, int, int) 1176 * @see #setPaddingRelative(int, int, int, int) 1177 * @see #getPadding(Rect) 1178 */ 1179 public int getBottomPadding() { 1180 return mLayerState.mPaddingBottom; 1181 } 1182 1183 private void computeNestedPadding(Rect padding) { 1184 padding.left = 0; 1185 padding.top = 0; 1186 padding.right = 0; 1187 padding.bottom = 0; 1188 1189 // Add all the padding. 1190 final ChildDrawable[] array = mLayerState.mChildren; 1191 final int N = mLayerState.mNum; 1192 for (int i = 0; i < N; i++) { 1193 refreshChildPadding(i, array[i]); 1194 1195 padding.left += mPaddingL[i]; 1196 padding.top += mPaddingT[i]; 1197 padding.right += mPaddingR[i]; 1198 padding.bottom += mPaddingB[i]; 1199 } 1200 } 1201 1202 private void computeStackedPadding(Rect padding) { 1203 padding.left = 0; 1204 padding.top = 0; 1205 padding.right = 0; 1206 padding.bottom = 0; 1207 1208 // Take the max padding. 1209 final ChildDrawable[] array = mLayerState.mChildren; 1210 final int N = mLayerState.mNum; 1211 for (int i = 0; i < N; i++) { 1212 refreshChildPadding(i, array[i]); 1213 1214 padding.left = Math.max(padding.left, mPaddingL[i]); 1215 padding.top = Math.max(padding.top, mPaddingT[i]); 1216 padding.right = Math.max(padding.right, mPaddingR[i]); 1217 padding.bottom = Math.max(padding.bottom, mPaddingB[i]); 1218 } 1219 } 1220 1221 /** 1222 * Populates <code>outline</code> with the first available (non-empty) layer outline. 1223 * 1224 * @param outline Outline in which to place the first available layer outline 1225 */ 1226 @Override 1227 public void getOutline(@NonNull Outline outline) { 1228 final ChildDrawable[] array = mLayerState.mChildren; 1229 final int N = mLayerState.mNum; 1230 for (int i = 0; i < N; i++) { 1231 final Drawable dr = array[i].mDrawable; 1232 if (dr != null) { 1233 dr.getOutline(outline); 1234 if (!outline.isEmpty()) { 1235 return; 1236 } 1237 } 1238 } 1239 } 1240 1241 @Override 1242 public void setHotspot(float x, float y) { 1243 final ChildDrawable[] array = mLayerState.mChildren; 1244 final int N = mLayerState.mNum; 1245 for (int i = 0; i < N; i++) { 1246 final Drawable dr = array[i].mDrawable; 1247 if (dr != null) { 1248 dr.setHotspot(x, y); 1249 } 1250 } 1251 } 1252 1253 @Override 1254 public void setHotspotBounds(int left, int top, int right, int bottom) { 1255 final ChildDrawable[] array = mLayerState.mChildren; 1256 final int N = mLayerState.mNum; 1257 for (int i = 0; i < N; i++) { 1258 final Drawable dr = array[i].mDrawable; 1259 if (dr != null) { 1260 dr.setHotspotBounds(left, top, right, bottom); 1261 } 1262 } 1263 1264 if (mHotspotBounds == null) { 1265 mHotspotBounds = new Rect(left, top, right, bottom); 1266 } else { 1267 mHotspotBounds.set(left, top, right, bottom); 1268 } 1269 } 1270 1271 @Override 1272 public void getHotspotBounds(Rect outRect) { 1273 if (mHotspotBounds != null) { 1274 outRect.set(mHotspotBounds); 1275 } else { 1276 super.getHotspotBounds(outRect); 1277 } 1278 } 1279 1280 @Override 1281 public boolean setVisible(boolean visible, boolean restart) { 1282 final boolean changed = super.setVisible(visible, restart); 1283 final ChildDrawable[] array = mLayerState.mChildren; 1284 final int N = mLayerState.mNum; 1285 for (int i = 0; i < N; i++) { 1286 final Drawable dr = array[i].mDrawable; 1287 if (dr != null) { 1288 dr.setVisible(visible, restart); 1289 } 1290 } 1291 1292 return changed; 1293 } 1294 1295 @Override 1296 public void setDither(boolean dither) { 1297 final ChildDrawable[] array = mLayerState.mChildren; 1298 final int N = mLayerState.mNum; 1299 for (int i = 0; i < N; i++) { 1300 final Drawable dr = array[i].mDrawable; 1301 if (dr != null) { 1302 dr.setDither(dither); 1303 } 1304 } 1305 } 1306 1307 @Override 1308 public void setAlpha(int alpha) { 1309 final ChildDrawable[] array = mLayerState.mChildren; 1310 final int N = mLayerState.mNum; 1311 for (int i = 0; i < N; i++) { 1312 final Drawable dr = array[i].mDrawable; 1313 if (dr != null) { 1314 dr.setAlpha(alpha); 1315 } 1316 } 1317 } 1318 1319 @Override 1320 public int getAlpha() { 1321 final Drawable dr = getFirstNonNullDrawable(); 1322 if (dr != null) { 1323 return dr.getAlpha(); 1324 } else { 1325 return super.getAlpha(); 1326 } 1327 } 1328 1329 @Override 1330 public void setColorFilter(ColorFilter colorFilter) { 1331 final ChildDrawable[] array = mLayerState.mChildren; 1332 final int N = mLayerState.mNum; 1333 for (int i = 0; i < N; i++) { 1334 final Drawable dr = array[i].mDrawable; 1335 if (dr != null) { 1336 dr.setColorFilter(colorFilter); 1337 } 1338 } 1339 } 1340 1341 @Override 1342 public void setTintList(ColorStateList tint) { 1343 final ChildDrawable[] array = mLayerState.mChildren; 1344 final int N = mLayerState.mNum; 1345 for (int i = 0; i < N; i++) { 1346 final Drawable dr = array[i].mDrawable; 1347 if (dr != null) { 1348 dr.setTintList(tint); 1349 } 1350 } 1351 } 1352 1353 @Override 1354 public void setTintMode(Mode tintMode) { 1355 final ChildDrawable[] array = mLayerState.mChildren; 1356 final int N = mLayerState.mNum; 1357 for (int i = 0; i < N; i++) { 1358 final Drawable dr = array[i].mDrawable; 1359 if (dr != null) { 1360 dr.setTintMode(tintMode); 1361 } 1362 } 1363 } 1364 1365 private Drawable getFirstNonNullDrawable() { 1366 final ChildDrawable[] array = mLayerState.mChildren; 1367 final int N = mLayerState.mNum; 1368 for (int i = 0; i < N; i++) { 1369 final Drawable dr = array[i].mDrawable; 1370 if (dr != null) { 1371 return dr; 1372 } 1373 } 1374 return null; 1375 } 1376 1377 /** 1378 * Sets the opacity of this drawable directly instead of collecting the 1379 * states from the layers. 1380 * 1381 * @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN 1382 * PixelFormat.UNKNOWN} for the default behavior 1383 * @see PixelFormat#UNKNOWN 1384 * @see PixelFormat#TRANSLUCENT 1385 * @see PixelFormat#TRANSPARENT 1386 * @see PixelFormat#OPAQUE 1387 */ 1388 public void setOpacity(int opacity) { 1389 mLayerState.mOpacityOverride = opacity; 1390 } 1391 1392 @Override 1393 public int getOpacity() { 1394 if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) { 1395 return mLayerState.mOpacityOverride; 1396 } 1397 return mLayerState.getOpacity(); 1398 } 1399 1400 @Override 1401 public void setAutoMirrored(boolean mirrored) { 1402 mLayerState.mAutoMirrored = mirrored; 1403 1404 final ChildDrawable[] array = mLayerState.mChildren; 1405 final int N = mLayerState.mNum; 1406 for (int i = 0; i < N; i++) { 1407 final Drawable dr = array[i].mDrawable; 1408 if (dr != null) { 1409 dr.setAutoMirrored(mirrored); 1410 } 1411 } 1412 } 1413 1414 @Override 1415 public boolean isAutoMirrored() { 1416 return mLayerState.mAutoMirrored; 1417 } 1418 1419 @Override 1420 public void jumpToCurrentState() { 1421 final ChildDrawable[] children = mLayerState.mChildren; 1422 for (int i = 0, count = mLayerState.mNum; i < count; i++) { 1423 children[i].mDrawable.jumpToCurrentState(); 1424 } 1425 } 1426 1427 @Override 1428 public boolean isStateful() { 1429 return mLayerState.isStateful(); 1430 } 1431 1432 @Override 1433 protected boolean onStateChange(int[] state) { 1434 boolean changed = false; 1435 1436 final ChildDrawable[] array = mLayerState.mChildren; 1437 final int N = mLayerState.mNum; 1438 for (int i = 0; i < N; i++) { 1439 final Drawable dr = array[i].mDrawable; 1440 if (dr != null && dr.isStateful() && dr.setState(state)) { 1441 refreshChildPadding(i, array[i]); 1442 changed = true; 1443 } 1444 } 1445 1446 if (changed) { 1447 updateLayerBounds(getBounds()); 1448 } 1449 1450 return changed; 1451 } 1452 1453 @Override 1454 protected boolean onLevelChange(int level) { 1455 boolean changed = false; 1456 1457 final ChildDrawable[] array = mLayerState.mChildren; 1458 final int N = mLayerState.mNum; 1459 for (int i = 0; i < N; i++) { 1460 final Drawable dr = array[i].mDrawable; 1461 if (dr != null && dr.setLevel(level)) { 1462 refreshChildPadding(i, array[i]); 1463 changed = true; 1464 } 1465 } 1466 1467 if (changed) { 1468 updateLayerBounds(getBounds()); 1469 } 1470 1471 return changed; 1472 } 1473 1474 @Override 1475 protected void onBoundsChange(Rect bounds) { 1476 updateLayerBounds(bounds); 1477 } 1478 1479 private void updateLayerBounds(Rect bounds) { 1480 int paddingL = 0; 1481 int paddingT = 0; 1482 int paddingR = 0; 1483 int paddingB = 0; 1484 1485 final Rect outRect = mTmpOutRect; 1486 final int layoutDirection = getLayoutDirection(); 1487 final boolean isLayoutRtl = layoutDirection == LayoutDirection.RTL; 1488 final boolean isPaddingNested = mLayerState.mPaddingMode == PADDING_MODE_NEST; 1489 final ChildDrawable[] array = mLayerState.mChildren; 1490 1491 for (int i = 0, count = mLayerState.mNum; i < count; i++) { 1492 final ChildDrawable r = array[i]; 1493 final Drawable d = r.mDrawable; 1494 if (d == null) { 1495 continue; 1496 } 1497 1498 final int insetT = r.mInsetT; 1499 final int insetB = r.mInsetB; 1500 1501 // Resolve insets for RTL. Relative insets override absolute 1502 // insets. 1503 final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS; 1504 final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE; 1505 final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL; 1506 final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR; 1507 1508 // Establish containing region based on aggregate padding and 1509 // requested insets for the current layer. 1510 final Rect container = mTmpContainer; 1511 container.set(bounds.left + insetL + paddingL, bounds.top + insetT + paddingT, 1512 bounds.right - insetR - paddingR, bounds.bottom - insetB - paddingB); 1513 1514 // Compute a reasonable default gravity based on the intrinsic and 1515 // explicit dimensions, if specified. 1516 final int intrinsicW = d.getIntrinsicWidth(); 1517 final int intrinsicH = d.getIntrinsicHeight(); 1518 final int layerW = r.mWidth; 1519 final int layerH = r.mHeight; 1520 final int gravity = resolveGravity(r.mGravity, layerW, layerH, intrinsicW, intrinsicH); 1521 1522 // Explicit dimensions override intrinsic dimensions. 1523 final int resolvedW = layerW < 0 ? intrinsicW : layerW; 1524 final int resolvedH = layerH < 0 ? intrinsicH : layerH; 1525 Gravity.apply(gravity, resolvedW, resolvedH, container, outRect, layoutDirection); 1526 d.setBounds(outRect); 1527 1528 if (isPaddingNested) { 1529 paddingL += mPaddingL[i]; 1530 paddingR += mPaddingR[i]; 1531 paddingT += mPaddingT[i]; 1532 paddingB += mPaddingB[i]; 1533 } 1534 } 1535 } 1536 1537 /** 1538 * Resolves layer gravity given explicit gravity and dimensions. 1539 * <p> 1540 * If the client hasn't specified a gravity but has specified an explicit 1541 * dimension, defaults to START or TOP. Otherwise, defaults to FILL to 1542 * preserve legacy behavior. 1543 * 1544 * @param gravity layer gravity 1545 * @param width width of the layer if set, -1 otherwise 1546 * @param height height of the layer if set, -1 otherwise 1547 * @return the default gravity for the layer 1548 */ 1549 private static int resolveGravity(int gravity, int width, int height, 1550 int intrinsicWidth, int intrinsicHeight) { 1551 if (!Gravity.isHorizontal(gravity)) { 1552 if (width < 0) { 1553 gravity |= Gravity.FILL_HORIZONTAL; 1554 } else { 1555 gravity |= Gravity.START; 1556 } 1557 } 1558 1559 if (!Gravity.isVertical(gravity)) { 1560 if (height < 0) { 1561 gravity |= Gravity.FILL_VERTICAL; 1562 } else { 1563 gravity |= Gravity.TOP; 1564 } 1565 } 1566 1567 // If a dimension if not specified, either implicitly or explicitly, 1568 // force FILL for that dimension's gravity. This ensures that colors 1569 // are handled correctly and ensures backward compatibility. 1570 if (width < 0 && intrinsicWidth < 0) { 1571 gravity |= Gravity.FILL_HORIZONTAL; 1572 } 1573 1574 if (height < 0 && intrinsicHeight < 0) { 1575 gravity |= Gravity.FILL_VERTICAL; 1576 } 1577 1578 return gravity; 1579 } 1580 1581 @Override 1582 public int getIntrinsicWidth() { 1583 int width = -1; 1584 int padL = 0; 1585 int padR = 0; 1586 1587 final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST; 1588 final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL; 1589 final ChildDrawable[] array = mLayerState.mChildren; 1590 final int N = mLayerState.mNum; 1591 for (int i = 0; i < N; i++) { 1592 final ChildDrawable r = array[i]; 1593 if (r.mDrawable == null) { 1594 continue; 1595 } 1596 1597 // Take the resolved layout direction into account. If start / end 1598 // padding are defined, they will be resolved (hence overriding) to 1599 // left / right or right / left depending on the resolved layout 1600 // direction. If start / end padding are not defined, use the 1601 // left / right ones. 1602 final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS; 1603 final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE; 1604 final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL; 1605 final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR; 1606 1607 // Don't apply padding and insets for children that don't have 1608 // an intrinsic dimension. 1609 final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth; 1610 final int w = minWidth < 0 ? -1 : minWidth + insetL + insetR + padL + padR; 1611 if (w > width) { 1612 width = w; 1613 } 1614 1615 if (nest) { 1616 padL += mPaddingL[i]; 1617 padR += mPaddingR[i]; 1618 } 1619 } 1620 1621 return width; 1622 } 1623 1624 @Override 1625 public int getIntrinsicHeight() { 1626 int height = -1; 1627 int padT = 0; 1628 int padB = 0; 1629 1630 final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST; 1631 final ChildDrawable[] array = mLayerState.mChildren; 1632 final int N = mLayerState.mNum; 1633 for (int i = 0; i < N; i++) { 1634 final ChildDrawable r = array[i]; 1635 if (r.mDrawable == null) { 1636 continue; 1637 } 1638 1639 // Don't apply padding and insets for children that don't have 1640 // an intrinsic dimension. 1641 final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight; 1642 final int h = minHeight < 0 ? -1 : minHeight + r.mInsetT + r.mInsetB + padT + padB; 1643 if (h > height) { 1644 height = h; 1645 } 1646 1647 if (nest) { 1648 padT += mPaddingT[i]; 1649 padB += mPaddingB[i]; 1650 } 1651 } 1652 1653 return height; 1654 } 1655 1656 /** 1657 * Refreshes the cached padding values for the specified child. 1658 * 1659 * @return true if the child's padding has changed 1660 */ 1661 private boolean refreshChildPadding(int i, ChildDrawable r) { 1662 if (r.mDrawable != null) { 1663 final Rect rect = mTmpRect; 1664 r.mDrawable.getPadding(rect); 1665 if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] 1666 || rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) { 1667 mPaddingL[i] = rect.left; 1668 mPaddingT[i] = rect.top; 1669 mPaddingR[i] = rect.right; 1670 mPaddingB[i] = rect.bottom; 1671 return true; 1672 } 1673 } 1674 return false; 1675 } 1676 1677 /** 1678 * Ensures the child padding caches are large enough. 1679 */ 1680 void ensurePadding() { 1681 final int N = mLayerState.mNum; 1682 if (mPaddingL != null && mPaddingL.length >= N) { 1683 return; 1684 } 1685 1686 mPaddingL = new int[N]; 1687 mPaddingT = new int[N]; 1688 mPaddingR = new int[N]; 1689 mPaddingB = new int[N]; 1690 } 1691 1692 void refreshPadding() { 1693 final int N = mLayerState.mNum; 1694 final ChildDrawable[] array = mLayerState.mChildren; 1695 for (int i = 0; i < N; i++) { 1696 refreshChildPadding(i, array[i]); 1697 } 1698 } 1699 1700 @Override 1701 public ConstantState getConstantState() { 1702 if (mLayerState.canConstantState()) { 1703 mLayerState.mChangingConfigurations = getChangingConfigurations(); 1704 return mLayerState; 1705 } 1706 return null; 1707 } 1708 1709 @Override 1710 public Drawable mutate() { 1711 if (!mMutated && super.mutate() == this) { 1712 mLayerState = createConstantState(mLayerState, null); 1713 final ChildDrawable[] array = mLayerState.mChildren; 1714 final int N = mLayerState.mNum; 1715 for (int i = 0; i < N; i++) { 1716 final Drawable dr = array[i].mDrawable; 1717 if (dr != null) { 1718 dr.mutate(); 1719 } 1720 } 1721 mMutated = true; 1722 } 1723 return this; 1724 } 1725 1726 /** 1727 * @hide 1728 */ 1729 public void clearMutated() { 1730 super.clearMutated(); 1731 1732 final ChildDrawable[] array = mLayerState.mChildren; 1733 final int N = mLayerState.mNum; 1734 for (int i = 0; i < N; i++) { 1735 final Drawable dr = array[i].mDrawable; 1736 if (dr != null) { 1737 dr.clearMutated(); 1738 } 1739 } 1740 mMutated = false; 1741 } 1742 1743 @Override 1744 public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) { 1745 boolean changed = false; 1746 1747 final ChildDrawable[] array = mLayerState.mChildren; 1748 final int N = mLayerState.mNum; 1749 for (int i = 0; i < N; i++) { 1750 final Drawable dr = array[i].mDrawable; 1751 if (dr != null) { 1752 changed |= dr.setLayoutDirection(layoutDirection); 1753 } 1754 } 1755 1756 updateLayerBounds(getBounds()); 1757 return changed; 1758 } 1759 1760 static class ChildDrawable { 1761 public Drawable mDrawable; 1762 public int[] mThemeAttrs; 1763 public int mDensity = DisplayMetrics.DENSITY_DEFAULT; 1764 public int mInsetL, mInsetT, mInsetR, mInsetB; 1765 public int mInsetS = UNDEFINED_INSET; 1766 public int mInsetE = UNDEFINED_INSET; 1767 public int mWidth = -1; 1768 public int mHeight = -1; 1769 public int mGravity = Gravity.NO_GRAVITY; 1770 public int mId = View.NO_ID; 1771 1772 ChildDrawable(int density) { 1773 mDensity = density; 1774 } 1775 1776 ChildDrawable(@NonNull ChildDrawable orig, @NonNull LayerDrawable owner, 1777 @Nullable Resources res) { 1778 final Drawable dr = orig.mDrawable; 1779 final Drawable clone; 1780 if (dr != null) { 1781 final ConstantState cs = dr.getConstantState(); 1782 if (cs == null) { 1783 clone = dr; 1784 } else if (res != null) { 1785 clone = cs.newDrawable(res); 1786 } else { 1787 clone = cs.newDrawable(); 1788 } 1789 clone.setCallback(owner); 1790 clone.setLayoutDirection(dr.getLayoutDirection()); 1791 clone.setBounds(dr.getBounds()); 1792 clone.setLevel(dr.getLevel()); 1793 } else { 1794 clone = null; 1795 } 1796 1797 mDrawable = clone; 1798 mThemeAttrs = orig.mThemeAttrs; 1799 mInsetL = orig.mInsetL; 1800 mInsetT = orig.mInsetT; 1801 mInsetR = orig.mInsetR; 1802 mInsetB = orig.mInsetB; 1803 mInsetS = orig.mInsetS; 1804 mInsetE = orig.mInsetE; 1805 mWidth = orig.mWidth; 1806 mHeight = orig.mHeight; 1807 mGravity = orig.mGravity; 1808 mId = orig.mId; 1809 1810 mDensity = Drawable.resolveDensity(res, orig.mDensity); 1811 if (orig.mDensity != mDensity) { 1812 applyDensityScaling(orig.mDensity, mDensity); 1813 } 1814 } 1815 1816 public boolean canApplyTheme() { 1817 return mThemeAttrs != null 1818 || (mDrawable != null && mDrawable.canApplyTheme()); 1819 } 1820 1821 public final void setDensity(int targetDensity) { 1822 if (mDensity != targetDensity) { 1823 final int sourceDensity = mDensity; 1824 mDensity = targetDensity; 1825 1826 applyDensityScaling(sourceDensity, targetDensity); 1827 } 1828 } 1829 1830 private void applyDensityScaling(int sourceDensity, int targetDensity) { 1831 mInsetL = Drawable.scaleFromDensity(mInsetL, sourceDensity, targetDensity, false); 1832 mInsetT = Drawable.scaleFromDensity(mInsetT, sourceDensity, targetDensity, false); 1833 mInsetR = Drawable.scaleFromDensity(mInsetR, sourceDensity, targetDensity, false); 1834 mInsetB = Drawable.scaleFromDensity(mInsetB, sourceDensity, targetDensity, false); 1835 if (mInsetS != UNDEFINED_INSET) { 1836 mInsetS = Drawable.scaleFromDensity(mInsetS, sourceDensity, targetDensity, false); 1837 } 1838 if (mInsetE != UNDEFINED_INSET) { 1839 mInsetE = Drawable.scaleFromDensity(mInsetE, sourceDensity, targetDensity, false); 1840 } 1841 if (mWidth > 0) { 1842 mWidth = Drawable.scaleFromDensity(mWidth, sourceDensity, targetDensity, true); 1843 } 1844 if (mHeight > 0) { 1845 mHeight = Drawable.scaleFromDensity(mHeight, sourceDensity, targetDensity, true); 1846 } 1847 } 1848 } 1849 1850 static class LayerState extends ConstantState { 1851 private int[] mThemeAttrs; 1852 1853 int mNum; 1854 ChildDrawable[] mChildren; 1855 1856 int mDensity; 1857 1858 // These values all correspond to mDensity. 1859 int mPaddingTop = -1; 1860 int mPaddingBottom = -1; 1861 int mPaddingLeft = -1; 1862 int mPaddingRight = -1; 1863 int mPaddingStart = -1; 1864 int mPaddingEnd = -1; 1865 int mOpacityOverride = PixelFormat.UNKNOWN; 1866 1867 int mChangingConfigurations; 1868 int mChildrenChangingConfigurations; 1869 1870 private boolean mHaveOpacity; 1871 private int mOpacity; 1872 1873 private boolean mHaveIsStateful; 1874 private boolean mIsStateful; 1875 1876 private boolean mAutoMirrored = false; 1877 1878 private int mPaddingMode = PADDING_MODE_NEST; 1879 1880 LayerState(@Nullable LayerState orig, @NonNull LayerDrawable owner, 1881 @Nullable Resources res) { 1882 mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0); 1883 1884 if (orig != null) { 1885 final ChildDrawable[] origChildDrawable = orig.mChildren; 1886 final int N = orig.mNum; 1887 1888 mNum = N; 1889 mChildren = new ChildDrawable[N]; 1890 1891 mChangingConfigurations = orig.mChangingConfigurations; 1892 mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations; 1893 1894 for (int i = 0; i < N; i++) { 1895 final ChildDrawable or = origChildDrawable[i]; 1896 mChildren[i] = new ChildDrawable(or, owner, res); 1897 } 1898 1899 mHaveOpacity = orig.mHaveOpacity; 1900 mOpacity = orig.mOpacity; 1901 mHaveIsStateful = orig.mHaveIsStateful; 1902 mIsStateful = orig.mIsStateful; 1903 mAutoMirrored = orig.mAutoMirrored; 1904 mPaddingMode = orig.mPaddingMode; 1905 mThemeAttrs = orig.mThemeAttrs; 1906 mPaddingTop = orig.mPaddingTop; 1907 mPaddingBottom = orig.mPaddingBottom; 1908 mPaddingLeft = orig.mPaddingLeft; 1909 mPaddingRight = orig.mPaddingRight; 1910 mPaddingStart = orig.mPaddingStart; 1911 mPaddingEnd = orig.mPaddingEnd; 1912 mOpacityOverride = orig.mOpacityOverride; 1913 1914 if (orig.mDensity != mDensity) { 1915 applyDensityScaling(orig.mDensity, mDensity); 1916 } 1917 } else { 1918 mNum = 0; 1919 mChildren = null; 1920 } 1921 } 1922 1923 public final void setDensity(int targetDensity) { 1924 if (mDensity != targetDensity) { 1925 final int sourceDensity = mDensity; 1926 mDensity = targetDensity; 1927 1928 onDensityChanged(sourceDensity, targetDensity); 1929 } 1930 } 1931 1932 protected void onDensityChanged(int sourceDensity, int targetDensity) { 1933 applyDensityScaling(sourceDensity, targetDensity); 1934 } 1935 1936 private void applyDensityScaling(int sourceDensity, int targetDensity) { 1937 if (mPaddingLeft > 0) { 1938 mPaddingLeft = Drawable.scaleFromDensity( 1939 mPaddingLeft, sourceDensity, targetDensity, false); 1940 } 1941 if (mPaddingTop > 0) { 1942 mPaddingTop = Drawable.scaleFromDensity( 1943 mPaddingTop, sourceDensity, targetDensity, false); 1944 } 1945 if (mPaddingRight > 0) { 1946 mPaddingRight = Drawable.scaleFromDensity( 1947 mPaddingRight, sourceDensity, targetDensity, false); 1948 } 1949 if (mPaddingBottom > 0) { 1950 mPaddingBottom = Drawable.scaleFromDensity( 1951 mPaddingBottom, sourceDensity, targetDensity, false); 1952 } 1953 if (mPaddingStart > 0) { 1954 mPaddingStart = Drawable.scaleFromDensity( 1955 mPaddingStart, sourceDensity, targetDensity, false); 1956 } 1957 if (mPaddingEnd > 0) { 1958 mPaddingEnd = Drawable.scaleFromDensity( 1959 mPaddingEnd, sourceDensity, targetDensity, false); 1960 } 1961 } 1962 1963 @Override 1964 public boolean canApplyTheme() { 1965 if (mThemeAttrs != null || super.canApplyTheme()) { 1966 return true; 1967 } 1968 1969 final ChildDrawable[] array = mChildren; 1970 final int N = mNum; 1971 for (int i = 0; i < N; i++) { 1972 final ChildDrawable layer = array[i]; 1973 if (layer.canApplyTheme()) { 1974 return true; 1975 } 1976 } 1977 1978 return false; 1979 } 1980 1981 @Override 1982 public Drawable newDrawable() { 1983 return new LayerDrawable(this, null); 1984 } 1985 1986 @Override 1987 public Drawable newDrawable(@Nullable Resources res) { 1988 return new LayerDrawable(this, res); 1989 } 1990 1991 @Override 1992 public int getChangingConfigurations() { 1993 return mChangingConfigurations 1994 | mChildrenChangingConfigurations; 1995 } 1996 1997 public final int getOpacity() { 1998 if (mHaveOpacity) { 1999 return mOpacity; 2000 } 2001 2002 final ChildDrawable[] array = mChildren; 2003 final int N = mNum; 2004 2005 // Seek to the first non-null drawable. 2006 int firstIndex = -1; 2007 for (int i = 0; i < N; i++) { 2008 if (array[i].mDrawable != null) { 2009 firstIndex = i; 2010 break; 2011 } 2012 } 2013 2014 int op; 2015 if (firstIndex >= 0) { 2016 op = array[firstIndex].mDrawable.getOpacity(); 2017 } else { 2018 op = PixelFormat.TRANSPARENT; 2019 } 2020 2021 // Merge all remaining non-null drawables. 2022 for (int i = firstIndex + 1; i < N; i++) { 2023 final Drawable dr = array[i].mDrawable; 2024 if (dr != null) { 2025 op = Drawable.resolveOpacity(op, dr.getOpacity()); 2026 } 2027 } 2028 2029 mOpacity = op; 2030 mHaveOpacity = true; 2031 return op; 2032 } 2033 2034 public final boolean isStateful() { 2035 if (mHaveIsStateful) { 2036 return mIsStateful; 2037 } 2038 2039 final ChildDrawable[] array = mChildren; 2040 final int N = mNum; 2041 boolean isStateful = false; 2042 for (int i = 0; i < N; i++) { 2043 final Drawable dr = array[i].mDrawable; 2044 if (dr != null && dr.isStateful()) { 2045 isStateful = true; 2046 break; 2047 } 2048 } 2049 2050 mIsStateful = isStateful; 2051 mHaveIsStateful = true; 2052 return isStateful; 2053 } 2054 2055 public final boolean canConstantState() { 2056 final ChildDrawable[] array = mChildren; 2057 final int N = mNum; 2058 for (int i = 0; i < N; i++) { 2059 final Drawable dr = array[i].mDrawable; 2060 if (dr != null && dr.getConstantState() == null) { 2061 return false; 2062 } 2063 } 2064 2065 // Don't cache the result, this method is not called very often. 2066 return true; 2067 } 2068 2069 public void invalidateCache() { 2070 mHaveOpacity = false; 2071 mHaveIsStateful = false; 2072 } 2073 2074 @Override 2075 public int addAtlasableBitmaps(Collection<Bitmap> atlasList) { 2076 final ChildDrawable[] array = mChildren; 2077 final int N = mNum; 2078 int pixelCount = 0; 2079 for (int i = 0; i < N; i++) { 2080 final Drawable dr = array[i].mDrawable; 2081 if (dr != null) { 2082 final ConstantState state = dr.getConstantState(); 2083 if (state != null) { 2084 pixelCount += state.addAtlasableBitmaps(atlasList); 2085 } 2086 } 2087 } 2088 return pixelCount; 2089 } 2090 } 2091} 2092 2093