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