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