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