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