LayerDrawable.java revision 6f9626a1c27d29c67881aa22de3c19804b535ddb
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[] array = mLayerState.mChildren; 1423 final int N = mLayerState.mNum; 1424 for (int i = 0; i < N; i++) { 1425 final Drawable dr = array[i].mDrawable; 1426 if (dr != null) { 1427 dr.jumpToCurrentState(); 1428 } 1429 } 1430 } 1431 1432 @Override 1433 public boolean isStateful() { 1434 return mLayerState.isStateful(); 1435 } 1436 1437 @Override 1438 protected boolean onStateChange(int[] state) { 1439 boolean changed = false; 1440 1441 final ChildDrawable[] array = mLayerState.mChildren; 1442 final int N = mLayerState.mNum; 1443 for (int i = 0; i < N; i++) { 1444 final Drawable dr = array[i].mDrawable; 1445 if (dr != null && dr.isStateful() && dr.setState(state)) { 1446 refreshChildPadding(i, array[i]); 1447 changed = true; 1448 } 1449 } 1450 1451 if (changed) { 1452 updateLayerBounds(getBounds()); 1453 } 1454 1455 return changed; 1456 } 1457 1458 @Override 1459 protected boolean onLevelChange(int level) { 1460 boolean changed = false; 1461 1462 final ChildDrawable[] array = mLayerState.mChildren; 1463 final int N = mLayerState.mNum; 1464 for (int i = 0; i < N; i++) { 1465 final Drawable dr = array[i].mDrawable; 1466 if (dr != null && dr.setLevel(level)) { 1467 refreshChildPadding(i, array[i]); 1468 changed = true; 1469 } 1470 } 1471 1472 if (changed) { 1473 updateLayerBounds(getBounds()); 1474 } 1475 1476 return changed; 1477 } 1478 1479 @Override 1480 protected void onBoundsChange(Rect bounds) { 1481 updateLayerBounds(bounds); 1482 } 1483 1484 private void updateLayerBounds(Rect bounds) { 1485 int paddingL = 0; 1486 int paddingT = 0; 1487 int paddingR = 0; 1488 int paddingB = 0; 1489 1490 final Rect outRect = mTmpOutRect; 1491 final int layoutDirection = getLayoutDirection(); 1492 final boolean isLayoutRtl = layoutDirection == LayoutDirection.RTL; 1493 final boolean isPaddingNested = mLayerState.mPaddingMode == PADDING_MODE_NEST; 1494 final ChildDrawable[] array = mLayerState.mChildren; 1495 1496 for (int i = 0, count = mLayerState.mNum; i < count; i++) { 1497 final ChildDrawable r = array[i]; 1498 final Drawable d = r.mDrawable; 1499 if (d == null) { 1500 continue; 1501 } 1502 1503 final int insetT = r.mInsetT; 1504 final int insetB = r.mInsetB; 1505 1506 // Resolve insets for RTL. Relative insets override absolute 1507 // insets. 1508 final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS; 1509 final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE; 1510 final int insetL = insetRtlL == INSET_UNDEFINED ? r.mInsetL : insetRtlL; 1511 final int insetR = insetRtlR == INSET_UNDEFINED ? r.mInsetR : insetRtlR; 1512 1513 // Establish containing region based on aggregate padding and 1514 // requested insets for the current layer. 1515 final Rect container = mTmpContainer; 1516 container.set(bounds.left + insetL + paddingL, bounds.top + insetT + paddingT, 1517 bounds.right - insetR - paddingR, bounds.bottom - insetB - paddingB); 1518 1519 // Compute a reasonable default gravity based on the intrinsic and 1520 // explicit dimensions, if specified. 1521 final int intrinsicW = d.getIntrinsicWidth(); 1522 final int intrinsicH = d.getIntrinsicHeight(); 1523 final int layerW = r.mWidth; 1524 final int layerH = r.mHeight; 1525 final int gravity = resolveGravity(r.mGravity, layerW, layerH, intrinsicW, intrinsicH); 1526 1527 // Explicit dimensions override intrinsic dimensions. 1528 final int resolvedW = layerW < 0 ? intrinsicW : layerW; 1529 final int resolvedH = layerH < 0 ? intrinsicH : layerH; 1530 Gravity.apply(gravity, resolvedW, resolvedH, container, outRect, layoutDirection); 1531 d.setBounds(outRect); 1532 1533 if (isPaddingNested) { 1534 paddingL += mPaddingL[i]; 1535 paddingR += mPaddingR[i]; 1536 paddingT += mPaddingT[i]; 1537 paddingB += mPaddingB[i]; 1538 } 1539 } 1540 } 1541 1542 /** 1543 * Resolves layer gravity given explicit gravity and dimensions. 1544 * <p> 1545 * If the client hasn't specified a gravity but has specified an explicit 1546 * dimension, defaults to START or TOP. Otherwise, defaults to FILL to 1547 * preserve legacy behavior. 1548 * 1549 * @param gravity layer gravity 1550 * @param width width of the layer if set, -1 otherwise 1551 * @param height height of the layer if set, -1 otherwise 1552 * @return the default gravity for the layer 1553 */ 1554 private static int resolveGravity(int gravity, int width, int height, 1555 int intrinsicWidth, int intrinsicHeight) { 1556 if (!Gravity.isHorizontal(gravity)) { 1557 if (width < 0) { 1558 gravity |= Gravity.FILL_HORIZONTAL; 1559 } else { 1560 gravity |= Gravity.START; 1561 } 1562 } 1563 1564 if (!Gravity.isVertical(gravity)) { 1565 if (height < 0) { 1566 gravity |= Gravity.FILL_VERTICAL; 1567 } else { 1568 gravity |= Gravity.TOP; 1569 } 1570 } 1571 1572 // If a dimension if not specified, either implicitly or explicitly, 1573 // force FILL for that dimension's gravity. This ensures that colors 1574 // are handled correctly and ensures backward compatibility. 1575 if (width < 0 && intrinsicWidth < 0) { 1576 gravity |= Gravity.FILL_HORIZONTAL; 1577 } 1578 1579 if (height < 0 && intrinsicHeight < 0) { 1580 gravity |= Gravity.FILL_VERTICAL; 1581 } 1582 1583 return gravity; 1584 } 1585 1586 @Override 1587 public int getIntrinsicWidth() { 1588 int width = -1; 1589 int padL = 0; 1590 int padR = 0; 1591 1592 final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST; 1593 final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL; 1594 final ChildDrawable[] array = mLayerState.mChildren; 1595 final int N = mLayerState.mNum; 1596 for (int i = 0; i < N; i++) { 1597 final ChildDrawable r = array[i]; 1598 if (r.mDrawable == null) { 1599 continue; 1600 } 1601 1602 // Take the resolved layout direction into account. If start / end 1603 // padding are defined, they will be resolved (hence overriding) to 1604 // left / right or right / left depending on the resolved layout 1605 // direction. If start / end padding are not defined, use the 1606 // left / right ones. 1607 final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS; 1608 final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE; 1609 final int insetL = insetRtlL == INSET_UNDEFINED ? r.mInsetL : insetRtlL; 1610 final int insetR = insetRtlR == INSET_UNDEFINED ? r.mInsetR : insetRtlR; 1611 1612 // Don't apply padding and insets for children that don't have 1613 // an intrinsic dimension. 1614 final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth; 1615 final int w = minWidth < 0 ? -1 : minWidth + insetL + insetR + padL + padR; 1616 if (w > width) { 1617 width = w; 1618 } 1619 1620 if (nest) { 1621 padL += mPaddingL[i]; 1622 padR += mPaddingR[i]; 1623 } 1624 } 1625 1626 return width; 1627 } 1628 1629 @Override 1630 public int getIntrinsicHeight() { 1631 int height = -1; 1632 int padT = 0; 1633 int padB = 0; 1634 1635 final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST; 1636 final ChildDrawable[] array = mLayerState.mChildren; 1637 final int N = mLayerState.mNum; 1638 for (int i = 0; i < N; i++) { 1639 final ChildDrawable r = array[i]; 1640 if (r.mDrawable == null) { 1641 continue; 1642 } 1643 1644 // Don't apply padding and insets for children that don't have 1645 // an intrinsic dimension. 1646 final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight; 1647 final int h = minHeight < 0 ? -1 : minHeight + r.mInsetT + r.mInsetB + padT + padB; 1648 if (h > height) { 1649 height = h; 1650 } 1651 1652 if (nest) { 1653 padT += mPaddingT[i]; 1654 padB += mPaddingB[i]; 1655 } 1656 } 1657 1658 return height; 1659 } 1660 1661 /** 1662 * Refreshes the cached padding values for the specified child. 1663 * 1664 * @return true if the child's padding has changed 1665 */ 1666 private boolean refreshChildPadding(int i, ChildDrawable r) { 1667 if (r.mDrawable != null) { 1668 final Rect rect = mTmpRect; 1669 r.mDrawable.getPadding(rect); 1670 if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] 1671 || rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) { 1672 mPaddingL[i] = rect.left; 1673 mPaddingT[i] = rect.top; 1674 mPaddingR[i] = rect.right; 1675 mPaddingB[i] = rect.bottom; 1676 return true; 1677 } 1678 } 1679 return false; 1680 } 1681 1682 /** 1683 * Ensures the child padding caches are large enough. 1684 */ 1685 void ensurePadding() { 1686 final int N = mLayerState.mNum; 1687 if (mPaddingL != null && mPaddingL.length >= N) { 1688 return; 1689 } 1690 1691 mPaddingL = new int[N]; 1692 mPaddingT = new int[N]; 1693 mPaddingR = new int[N]; 1694 mPaddingB = new int[N]; 1695 } 1696 1697 void refreshPadding() { 1698 final int N = mLayerState.mNum; 1699 final ChildDrawable[] array = mLayerState.mChildren; 1700 for (int i = 0; i < N; i++) { 1701 refreshChildPadding(i, array[i]); 1702 } 1703 } 1704 1705 @Override 1706 public ConstantState getConstantState() { 1707 if (mLayerState.canConstantState()) { 1708 mLayerState.mChangingConfigurations = getChangingConfigurations(); 1709 return mLayerState; 1710 } 1711 return null; 1712 } 1713 1714 @Override 1715 public Drawable mutate() { 1716 if (!mMutated && super.mutate() == this) { 1717 mLayerState = createConstantState(mLayerState, null); 1718 final ChildDrawable[] array = mLayerState.mChildren; 1719 final int N = mLayerState.mNum; 1720 for (int i = 0; i < N; i++) { 1721 final Drawable dr = array[i].mDrawable; 1722 if (dr != null) { 1723 dr.mutate(); 1724 } 1725 } 1726 mMutated = true; 1727 } 1728 return this; 1729 } 1730 1731 /** 1732 * @hide 1733 */ 1734 public void clearMutated() { 1735 super.clearMutated(); 1736 1737 final ChildDrawable[] array = mLayerState.mChildren; 1738 final int N = mLayerState.mNum; 1739 for (int i = 0; i < N; i++) { 1740 final Drawable dr = array[i].mDrawable; 1741 if (dr != null) { 1742 dr.clearMutated(); 1743 } 1744 } 1745 mMutated = false; 1746 } 1747 1748 @Override 1749 public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) { 1750 boolean changed = false; 1751 1752 final ChildDrawable[] array = mLayerState.mChildren; 1753 final int N = mLayerState.mNum; 1754 for (int i = 0; i < N; i++) { 1755 final Drawable dr = array[i].mDrawable; 1756 if (dr != null) { 1757 changed |= dr.setLayoutDirection(layoutDirection); 1758 } 1759 } 1760 1761 updateLayerBounds(getBounds()); 1762 return changed; 1763 } 1764 1765 static class ChildDrawable { 1766 public Drawable mDrawable; 1767 public int[] mThemeAttrs; 1768 public int mDensity = DisplayMetrics.DENSITY_DEFAULT; 1769 public int mInsetL, mInsetT, mInsetR, mInsetB; 1770 public int mInsetS = INSET_UNDEFINED; 1771 public int mInsetE = INSET_UNDEFINED; 1772 public int mWidth = -1; 1773 public int mHeight = -1; 1774 public int mGravity = Gravity.NO_GRAVITY; 1775 public int mId = View.NO_ID; 1776 1777 ChildDrawable(int density) { 1778 mDensity = density; 1779 } 1780 1781 ChildDrawable(@NonNull ChildDrawable orig, @NonNull LayerDrawable owner, 1782 @Nullable Resources res) { 1783 final Drawable dr = orig.mDrawable; 1784 final Drawable clone; 1785 if (dr != null) { 1786 final ConstantState cs = dr.getConstantState(); 1787 if (cs == null) { 1788 clone = dr; 1789 } else if (res != null) { 1790 clone = cs.newDrawable(res); 1791 } else { 1792 clone = cs.newDrawable(); 1793 } 1794 clone.setCallback(owner); 1795 clone.setLayoutDirection(dr.getLayoutDirection()); 1796 clone.setBounds(dr.getBounds()); 1797 clone.setLevel(dr.getLevel()); 1798 } else { 1799 clone = null; 1800 } 1801 1802 mDrawable = clone; 1803 mThemeAttrs = orig.mThemeAttrs; 1804 mInsetL = orig.mInsetL; 1805 mInsetT = orig.mInsetT; 1806 mInsetR = orig.mInsetR; 1807 mInsetB = orig.mInsetB; 1808 mInsetS = orig.mInsetS; 1809 mInsetE = orig.mInsetE; 1810 mWidth = orig.mWidth; 1811 mHeight = orig.mHeight; 1812 mGravity = orig.mGravity; 1813 mId = orig.mId; 1814 1815 mDensity = Drawable.resolveDensity(res, orig.mDensity); 1816 if (orig.mDensity != mDensity) { 1817 applyDensityScaling(orig.mDensity, mDensity); 1818 } 1819 } 1820 1821 public boolean canApplyTheme() { 1822 return mThemeAttrs != null 1823 || (mDrawable != null && mDrawable.canApplyTheme()); 1824 } 1825 1826 public final void setDensity(int targetDensity) { 1827 if (mDensity != targetDensity) { 1828 final int sourceDensity = mDensity; 1829 mDensity = targetDensity; 1830 1831 applyDensityScaling(sourceDensity, targetDensity); 1832 } 1833 } 1834 1835 private void applyDensityScaling(int sourceDensity, int targetDensity) { 1836 mInsetL = Drawable.scaleFromDensity(mInsetL, sourceDensity, targetDensity, false); 1837 mInsetT = Drawable.scaleFromDensity(mInsetT, sourceDensity, targetDensity, false); 1838 mInsetR = Drawable.scaleFromDensity(mInsetR, sourceDensity, targetDensity, false); 1839 mInsetB = Drawable.scaleFromDensity(mInsetB, sourceDensity, targetDensity, false); 1840 if (mInsetS != INSET_UNDEFINED) { 1841 mInsetS = Drawable.scaleFromDensity(mInsetS, sourceDensity, targetDensity, false); 1842 } 1843 if (mInsetE != INSET_UNDEFINED) { 1844 mInsetE = Drawable.scaleFromDensity(mInsetE, sourceDensity, targetDensity, false); 1845 } 1846 if (mWidth > 0) { 1847 mWidth = Drawable.scaleFromDensity(mWidth, sourceDensity, targetDensity, true); 1848 } 1849 if (mHeight > 0) { 1850 mHeight = Drawable.scaleFromDensity(mHeight, sourceDensity, targetDensity, true); 1851 } 1852 } 1853 } 1854 1855 static class LayerState extends ConstantState { 1856 private int[] mThemeAttrs; 1857 1858 int mNum; 1859 ChildDrawable[] mChildren; 1860 1861 int mDensity; 1862 1863 // These values all correspond to mDensity. 1864 int mPaddingTop = -1; 1865 int mPaddingBottom = -1; 1866 int mPaddingLeft = -1; 1867 int mPaddingRight = -1; 1868 int mPaddingStart = -1; 1869 int mPaddingEnd = -1; 1870 int mOpacityOverride = PixelFormat.UNKNOWN; 1871 1872 @Config int mChangingConfigurations; 1873 @Config int mChildrenChangingConfigurations; 1874 1875 private boolean mHaveOpacity; 1876 private int mOpacity; 1877 1878 private boolean mHaveIsStateful; 1879 private boolean mIsStateful; 1880 1881 private boolean mAutoMirrored = false; 1882 1883 private int mPaddingMode = PADDING_MODE_NEST; 1884 1885 LayerState(@Nullable LayerState orig, @NonNull LayerDrawable owner, 1886 @Nullable Resources res) { 1887 mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0); 1888 1889 if (orig != null) { 1890 final ChildDrawable[] origChildDrawable = orig.mChildren; 1891 final int N = orig.mNum; 1892 1893 mNum = N; 1894 mChildren = new ChildDrawable[N]; 1895 1896 mChangingConfigurations = orig.mChangingConfigurations; 1897 mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations; 1898 1899 for (int i = 0; i < N; i++) { 1900 final ChildDrawable or = origChildDrawable[i]; 1901 mChildren[i] = new ChildDrawable(or, owner, res); 1902 } 1903 1904 mHaveOpacity = orig.mHaveOpacity; 1905 mOpacity = orig.mOpacity; 1906 mHaveIsStateful = orig.mHaveIsStateful; 1907 mIsStateful = orig.mIsStateful; 1908 mAutoMirrored = orig.mAutoMirrored; 1909 mPaddingMode = orig.mPaddingMode; 1910 mThemeAttrs = orig.mThemeAttrs; 1911 mPaddingTop = orig.mPaddingTop; 1912 mPaddingBottom = orig.mPaddingBottom; 1913 mPaddingLeft = orig.mPaddingLeft; 1914 mPaddingRight = orig.mPaddingRight; 1915 mPaddingStart = orig.mPaddingStart; 1916 mPaddingEnd = orig.mPaddingEnd; 1917 mOpacityOverride = orig.mOpacityOverride; 1918 1919 if (orig.mDensity != mDensity) { 1920 applyDensityScaling(orig.mDensity, mDensity); 1921 } 1922 } else { 1923 mNum = 0; 1924 mChildren = null; 1925 } 1926 } 1927 1928 public final void setDensity(int targetDensity) { 1929 if (mDensity != targetDensity) { 1930 final int sourceDensity = mDensity; 1931 mDensity = targetDensity; 1932 1933 onDensityChanged(sourceDensity, targetDensity); 1934 } 1935 } 1936 1937 protected void onDensityChanged(int sourceDensity, int targetDensity) { 1938 applyDensityScaling(sourceDensity, targetDensity); 1939 } 1940 1941 private void applyDensityScaling(int sourceDensity, int targetDensity) { 1942 if (mPaddingLeft > 0) { 1943 mPaddingLeft = Drawable.scaleFromDensity( 1944 mPaddingLeft, sourceDensity, targetDensity, false); 1945 } 1946 if (mPaddingTop > 0) { 1947 mPaddingTop = Drawable.scaleFromDensity( 1948 mPaddingTop, sourceDensity, targetDensity, false); 1949 } 1950 if (mPaddingRight > 0) { 1951 mPaddingRight = Drawable.scaleFromDensity( 1952 mPaddingRight, sourceDensity, targetDensity, false); 1953 } 1954 if (mPaddingBottom > 0) { 1955 mPaddingBottom = Drawable.scaleFromDensity( 1956 mPaddingBottom, sourceDensity, targetDensity, false); 1957 } 1958 if (mPaddingStart > 0) { 1959 mPaddingStart = Drawable.scaleFromDensity( 1960 mPaddingStart, sourceDensity, targetDensity, false); 1961 } 1962 if (mPaddingEnd > 0) { 1963 mPaddingEnd = Drawable.scaleFromDensity( 1964 mPaddingEnd, sourceDensity, targetDensity, false); 1965 } 1966 } 1967 1968 @Override 1969 public boolean canApplyTheme() { 1970 if (mThemeAttrs != null || super.canApplyTheme()) { 1971 return true; 1972 } 1973 1974 final ChildDrawable[] array = mChildren; 1975 final int N = mNum; 1976 for (int i = 0; i < N; i++) { 1977 final ChildDrawable layer = array[i]; 1978 if (layer.canApplyTheme()) { 1979 return true; 1980 } 1981 } 1982 1983 return false; 1984 } 1985 1986 @Override 1987 public Drawable newDrawable() { 1988 return new LayerDrawable(this, null); 1989 } 1990 1991 @Override 1992 public Drawable newDrawable(@Nullable Resources res) { 1993 return new LayerDrawable(this, res); 1994 } 1995 1996 @Override 1997 public @Config int getChangingConfigurations() { 1998 return mChangingConfigurations 1999 | mChildrenChangingConfigurations; 2000 } 2001 2002 public final int getOpacity() { 2003 if (mHaveOpacity) { 2004 return mOpacity; 2005 } 2006 2007 final ChildDrawable[] array = mChildren; 2008 final int N = mNum; 2009 2010 // Seek to the first non-null drawable. 2011 int firstIndex = -1; 2012 for (int i = 0; i < N; i++) { 2013 if (array[i].mDrawable != null) { 2014 firstIndex = i; 2015 break; 2016 } 2017 } 2018 2019 int op; 2020 if (firstIndex >= 0) { 2021 op = array[firstIndex].mDrawable.getOpacity(); 2022 } else { 2023 op = PixelFormat.TRANSPARENT; 2024 } 2025 2026 // Merge all remaining non-null drawables. 2027 for (int i = firstIndex + 1; i < N; i++) { 2028 final Drawable dr = array[i].mDrawable; 2029 if (dr != null) { 2030 op = Drawable.resolveOpacity(op, dr.getOpacity()); 2031 } 2032 } 2033 2034 mOpacity = op; 2035 mHaveOpacity = true; 2036 return op; 2037 } 2038 2039 public final boolean isStateful() { 2040 if (mHaveIsStateful) { 2041 return mIsStateful; 2042 } 2043 2044 final ChildDrawable[] array = mChildren; 2045 final int N = mNum; 2046 boolean isStateful = false; 2047 for (int i = 0; i < N; i++) { 2048 final Drawable dr = array[i].mDrawable; 2049 if (dr != null && dr.isStateful()) { 2050 isStateful = true; 2051 break; 2052 } 2053 } 2054 2055 mIsStateful = isStateful; 2056 mHaveIsStateful = true; 2057 return isStateful; 2058 } 2059 2060 public final boolean canConstantState() { 2061 final ChildDrawable[] array = mChildren; 2062 final int N = mNum; 2063 for (int i = 0; i < N; i++) { 2064 final Drawable dr = array[i].mDrawable; 2065 if (dr != null && dr.getConstantState() == null) { 2066 return false; 2067 } 2068 } 2069 2070 // Don't cache the result, this method is not called very often. 2071 return true; 2072 } 2073 2074 public void invalidateCache() { 2075 mHaveOpacity = false; 2076 mHaveIsStateful = false; 2077 } 2078 2079 @Override 2080 public int addAtlasableBitmaps(Collection<Bitmap> atlasList) { 2081 final ChildDrawable[] array = mChildren; 2082 final int N = mNum; 2083 int pixelCount = 0; 2084 for (int i = 0; i < N; i++) { 2085 final Drawable dr = array[i].mDrawable; 2086 if (dr != null) { 2087 final ConstantState state = dr.getConstantState(); 2088 if (state != null) { 2089 pixelCount += state.addAtlasableBitmaps(atlasList); 2090 } 2091 } 2092 } 2093 return pixelCount; 2094 } 2095 } 2096} 2097 2098