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.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.util.AttributeSet; 34 import android.util.DisplayMetrics; 35 import android.util.LayoutDirection; 36 import android.util.Log; 37 import android.view.Gravity; 38 import android.view.View; 39 40 import com.android.internal.R; 41 42 import org.xmlpull.v1.XmlPullParser; 43 import org.xmlpull.v1.XmlPullParserException; 44 45 import java.io.IOException; 46 47 /** 48 * A Drawable that manages an array of other Drawables. These are drawn in array 49 * order, so the element with the largest index will be drawn on top. 50 * <p> 51 * It can be defined in an XML file with the <code><layer-list></code> element. 52 * Each Drawable in the layer is defined in a nested <code><item></code>. 53 * <p> 54 * For more information, see the guide to 55 * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>. 56 * 57 * @attr ref android.R.styleable#LayerDrawable_paddingMode 58 * @attr ref android.R.styleable#LayerDrawableItem_left 59 * @attr ref android.R.styleable#LayerDrawableItem_top 60 * @attr ref android.R.styleable#LayerDrawableItem_right 61 * @attr ref android.R.styleable#LayerDrawableItem_bottom 62 * @attr ref android.R.styleable#LayerDrawableItem_start 63 * @attr ref android.R.styleable#LayerDrawableItem_end 64 * @attr ref android.R.styleable#LayerDrawableItem_width 65 * @attr ref android.R.styleable#LayerDrawableItem_height 66 * @attr ref android.R.styleable#LayerDrawableItem_gravity 67 * @attr ref android.R.styleable#LayerDrawableItem_drawable 68 * @attr ref android.R.styleable#LayerDrawableItem_id 69 */ 70 public class LayerDrawable extends Drawable implements Drawable.Callback { 71 private static final String LOG_TAG = "LayerDrawable"; 72 73 /** 74 * Padding mode used to nest each layer inside the padding of the previous 75 * layer. 76 * 77 * @see #setPaddingMode(int) 78 */ 79 public static final int PADDING_MODE_NEST = 0; 80 81 /** 82 * Padding mode used to stack each layer directly atop the previous layer. 83 * 84 * @see #setPaddingMode(int) 85 */ 86 public static final int PADDING_MODE_STACK = 1; 87 88 /** 89 * Value used for undefined start and end insets. 90 * 91 * @see #getLayerInsetStart(int) 92 * @see #getLayerInsetEnd(int) 93 */ 94 public static final int INSET_UNDEFINED = Integer.MIN_VALUE; 95 96 @NonNull 97 @UnsupportedAppUsage 98 LayerState mLayerState; 99 100 private int[] mPaddingL; 101 private int[] mPaddingT; 102 private int[] mPaddingR; 103 private int[] mPaddingB; 104 105 private final Rect mTmpRect = new Rect(); 106 private final Rect mTmpOutRect = new Rect(); 107 private final Rect mTmpContainer = new Rect(); 108 private Rect mHotspotBounds; 109 private boolean mMutated; 110 111 private boolean mSuspendChildInvalidation; 112 private boolean mChildRequestedInvalidation; 113 114 /** 115 * Creates a new layer drawable with the list of specified layers. 116 * 117 * @param layers a list of drawables to use as layers in this new drawable, 118 * must be non-null 119 */ LayerDrawable(@onNull Drawable[] layers)120 public LayerDrawable(@NonNull Drawable[] layers) { 121 this(layers, null); 122 } 123 124 /** 125 * Creates a new layer drawable with the specified list of layers and the 126 * specified constant state. 127 * 128 * @param layers The list of layers to add to this drawable. 129 * @param state The constant drawable state. 130 */ LayerDrawable(@onNull Drawable[] layers, @Nullable LayerState state)131 LayerDrawable(@NonNull Drawable[] layers, @Nullable LayerState state) { 132 this(state, null); 133 134 if (layers == null) { 135 throw new IllegalArgumentException("layers must be non-null"); 136 } 137 138 final int length = layers.length; 139 final ChildDrawable[] r = new ChildDrawable[length]; 140 for (int i = 0; i < length; i++) { 141 r[i] = new ChildDrawable(mLayerState.mDensity); 142 Drawable child = layers[i]; 143 r[i].mDrawable = child; 144 if (child != null) { 145 child.setCallback(this); 146 mLayerState.mChildrenChangingConfigurations |= child.getChangingConfigurations(); 147 } 148 } 149 mLayerState.mNumChildren = length; 150 mLayerState.mChildren = r; 151 152 ensurePadding(); 153 refreshPadding(); 154 } 155 LayerDrawable()156 LayerDrawable() { 157 this((LayerState) null, null); 158 } 159 160 /** 161 * The one constructor to rule them all. This is called by all public 162 * constructors to set the state and initialize local properties. 163 */ LayerDrawable(@ullable LayerState state, @Nullable Resources res)164 LayerDrawable(@Nullable LayerState state, @Nullable Resources res) { 165 mLayerState = createConstantState(state, res); 166 if (mLayerState.mNumChildren > 0) { 167 ensurePadding(); 168 refreshPadding(); 169 } 170 } 171 createConstantState(@ullable LayerState state, @Nullable Resources res)172 LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) { 173 return new LayerState(state, this, res); 174 } 175 176 @Override inflate(@onNull Resources r, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)177 public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, 178 @NonNull AttributeSet attrs, @Nullable Theme theme) 179 throws XmlPullParserException, IOException { 180 super.inflate(r, parser, attrs, theme); 181 182 // The density may have changed since the last update. This will 183 // apply scaling to any existing constant state properties. 184 final LayerState state = mLayerState; 185 final int density = Drawable.resolveDensity(r, 0); 186 state.setDensity(density); 187 188 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable); 189 updateStateFromTypedArray(a); 190 a.recycle(); 191 192 final ChildDrawable[] array = state.mChildren; 193 final int N = state.mNumChildren; 194 for (int i = 0; i < N; i++) { 195 final ChildDrawable layer = array[i]; 196 layer.setDensity(density); 197 } 198 199 inflateLayers(r, parser, attrs, theme); 200 201 ensurePadding(); 202 refreshPadding(); 203 } 204 205 @Override applyTheme(@onNull Theme t)206 public void applyTheme(@NonNull Theme t) { 207 super.applyTheme(t); 208 209 final LayerState state = mLayerState; 210 final int density = Drawable.resolveDensity(t.getResources(), 0); 211 state.setDensity(density); 212 213 if (state.mThemeAttrs != null) { 214 final TypedArray a = t.resolveAttributes( 215 state.mThemeAttrs, R.styleable.LayerDrawable); 216 updateStateFromTypedArray(a); 217 a.recycle(); 218 } 219 220 final ChildDrawable[] array = state.mChildren; 221 final int N = state.mNumChildren; 222 for (int i = 0; i < N; i++) { 223 final ChildDrawable layer = array[i]; 224 layer.setDensity(density); 225 226 if (layer.mThemeAttrs != null) { 227 final TypedArray a = t.resolveAttributes( 228 layer.mThemeAttrs, R.styleable.LayerDrawableItem); 229 updateLayerFromTypedArray(layer, a); 230 a.recycle(); 231 } 232 233 final Drawable d = layer.mDrawable; 234 if (d != null && d.canApplyTheme()) { 235 d.applyTheme(t); 236 237 // Update cached mask of child changing configurations. 238 state.mChildrenChangingConfigurations |= d.getChangingConfigurations(); 239 } 240 } 241 } 242 243 /** 244 * Inflates child layers using the specified parser. 245 */ inflateLayers(@onNull Resources r, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)246 private void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser, 247 @NonNull AttributeSet attrs, @Nullable Theme theme) 248 throws XmlPullParserException, IOException { 249 final LayerState state = mLayerState; 250 251 final int innerDepth = parser.getDepth() + 1; 252 int type; 253 int depth; 254 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 255 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { 256 if (type != XmlPullParser.START_TAG) { 257 continue; 258 } 259 260 if (depth > innerDepth || !parser.getName().equals("item")) { 261 continue; 262 } 263 264 final ChildDrawable layer = new ChildDrawable(state.mDensity); 265 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem); 266 updateLayerFromTypedArray(layer, a); 267 a.recycle(); 268 269 // If the layer doesn't have a drawable or unresolved theme 270 // attribute for a drawable, attempt to parse one from the child 271 // element. If multiple child elements exist, we'll only use the 272 // first one. 273 if (layer.mDrawable == null && (layer.mThemeAttrs == null || 274 layer.mThemeAttrs[R.styleable.LayerDrawableItem_drawable] == 0)) { 275 while ((type = parser.next()) == XmlPullParser.TEXT) { 276 } 277 if (type != XmlPullParser.START_TAG) { 278 throw new XmlPullParserException(parser.getPositionDescription() 279 + ": <item> tag requires a 'drawable' attribute or " 280 + "child tag defining a drawable"); 281 } 282 283 // We found a child drawable. Take ownership. 284 layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme); 285 layer.mDrawable.setCallback(this); 286 state.mChildrenChangingConfigurations |= 287 layer.mDrawable.getChangingConfigurations(); 288 } 289 290 addLayer(layer); 291 } 292 } 293 294 /** 295 * Initializes the constant state from the values in the typed array. 296 */ updateStateFromTypedArray(@onNull TypedArray a)297 private void updateStateFromTypedArray(@NonNull TypedArray a) { 298 final LayerState state = mLayerState; 299 300 // Account for any configuration changes. 301 state.mChangingConfigurations |= a.getChangingConfigurations(); 302 303 // Extract the theme attributes, if any. 304 state.mThemeAttrs = a.extractThemeAttrs(); 305 306 final int N = a.getIndexCount(); 307 for (int i = 0; i < N; i++) { 308 final int attr = a.getIndex(i); 309 switch (attr) { 310 case R.styleable.LayerDrawable_opacity: 311 state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride); 312 break; 313 case R.styleable.LayerDrawable_paddingTop: 314 state.mPaddingTop = a.getDimensionPixelOffset(attr, state.mPaddingTop); 315 break; 316 case R.styleable.LayerDrawable_paddingBottom: 317 state.mPaddingBottom = a.getDimensionPixelOffset(attr, state.mPaddingBottom); 318 break; 319 case R.styleable.LayerDrawable_paddingLeft: 320 state.mPaddingLeft = a.getDimensionPixelOffset(attr, state.mPaddingLeft); 321 break; 322 case R.styleable.LayerDrawable_paddingRight: 323 state.mPaddingRight = a.getDimensionPixelOffset(attr, state.mPaddingRight); 324 break; 325 case R.styleable.LayerDrawable_paddingStart: 326 state.mPaddingStart = a.getDimensionPixelOffset(attr, state.mPaddingStart); 327 break; 328 case R.styleable.LayerDrawable_paddingEnd: 329 state.mPaddingEnd = a.getDimensionPixelOffset(attr, state.mPaddingEnd); 330 break; 331 case R.styleable.LayerDrawable_autoMirrored: 332 state.mAutoMirrored = a.getBoolean(attr, state.mAutoMirrored); 333 break; 334 case R.styleable.LayerDrawable_paddingMode: 335 state.mPaddingMode = a.getInteger(attr, state.mPaddingMode); 336 break; 337 } 338 } 339 } 340 updateLayerFromTypedArray(@onNull ChildDrawable layer, @NonNull TypedArray a)341 private void updateLayerFromTypedArray(@NonNull ChildDrawable layer, @NonNull TypedArray a) { 342 final LayerState state = mLayerState; 343 344 // Account for any configuration changes. 345 state.mChildrenChangingConfigurations |= a.getChangingConfigurations(); 346 347 // Extract the theme attributes, if any. 348 layer.mThemeAttrs = a.extractThemeAttrs(); 349 350 final int N = a.getIndexCount(); 351 for (int i = 0; i < N; i++) { 352 final int attr = a.getIndex(i); 353 switch (attr) { 354 case R.styleable.LayerDrawableItem_left: 355 layer.mInsetL = a.getDimensionPixelOffset(attr, layer.mInsetL); 356 break; 357 case R.styleable.LayerDrawableItem_top: 358 layer.mInsetT = a.getDimensionPixelOffset(attr, layer.mInsetT); 359 break; 360 case R.styleable.LayerDrawableItem_right: 361 layer.mInsetR = a.getDimensionPixelOffset(attr, layer.mInsetR); 362 break; 363 case R.styleable.LayerDrawableItem_bottom: 364 layer.mInsetB = a.getDimensionPixelOffset(attr, layer.mInsetB); 365 break; 366 case R.styleable.LayerDrawableItem_start: 367 layer.mInsetS = a.getDimensionPixelOffset(attr, layer.mInsetS); 368 break; 369 case R.styleable.LayerDrawableItem_end: 370 layer.mInsetE = a.getDimensionPixelOffset(attr, layer.mInsetE); 371 break; 372 case R.styleable.LayerDrawableItem_width: 373 layer.mWidth = a.getDimensionPixelSize(attr, layer.mWidth); 374 break; 375 case R.styleable.LayerDrawableItem_height: 376 layer.mHeight = a.getDimensionPixelSize(attr, layer.mHeight); 377 break; 378 case R.styleable.LayerDrawableItem_gravity: 379 layer.mGravity = a.getInteger(attr, layer.mGravity); 380 break; 381 case R.styleable.LayerDrawableItem_id: 382 layer.mId = a.getResourceId(attr, layer.mId); 383 break; 384 } 385 } 386 387 final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable); 388 if (dr != null) { 389 if (layer.mDrawable != null) { 390 // It's possible that a drawable was already set, in which case 391 // we should clear the callback. We may have also integrated the 392 // drawable's changing configurations, but we don't have enough 393 // information to revert that change. 394 layer.mDrawable.setCallback(null); 395 } 396 397 // Take ownership of the new drawable. 398 layer.mDrawable = dr; 399 layer.mDrawable.setCallback(this); 400 state.mChildrenChangingConfigurations |= 401 layer.mDrawable.getChangingConfigurations(); 402 } 403 } 404 405 @Override canApplyTheme()406 public boolean canApplyTheme() { 407 return mLayerState.canApplyTheme() || super.canApplyTheme(); 408 } 409 410 /** 411 * @hide 412 */ 413 @Override isProjected()414 public boolean isProjected() { 415 if (super.isProjected()) { 416 return true; 417 } 418 419 final ChildDrawable[] layers = mLayerState.mChildren; 420 final int N = mLayerState.mNumChildren; 421 for (int i = 0; i < N; i++) { 422 Drawable childDrawable = layers[i].mDrawable; 423 if (childDrawable != null && childDrawable.isProjected()) { 424 return true; 425 } 426 } 427 428 return false; 429 } 430 431 /** 432 * Adds a new layer at the end of list of layers and returns its index. 433 * 434 * @param layer The layer to add. 435 * @return The index of the layer. 436 */ 437 @UnsupportedAppUsage addLayer(@onNull ChildDrawable layer)438 int addLayer(@NonNull ChildDrawable layer) { 439 final LayerState st = mLayerState; 440 final int N = st.mChildren != null ? st.mChildren.length : 0; 441 final int i = st.mNumChildren; 442 if (i >= N) { 443 final ChildDrawable[] nu = new ChildDrawable[N + 10]; 444 if (i > 0) { 445 System.arraycopy(st.mChildren, 0, nu, 0, i); 446 } 447 448 st.mChildren = nu; 449 } 450 451 st.mChildren[i] = layer; 452 st.mNumChildren++; 453 st.invalidateCache(); 454 return i; 455 } 456 457 /** 458 * Add a new layer to this drawable. The new layer is identified by an id. 459 * 460 * @param dr The drawable to add as a layer. 461 * @param themeAttrs Theme attributes extracted from the layer. 462 * @param id The id of the new layer. 463 * @param left The left padding of the new layer. 464 * @param top The top padding of the new layer. 465 * @param right The right padding of the new layer. 466 * @param bottom The bottom padding of the new layer. 467 */ addLayer(Drawable dr, int[] themeAttrs, int id, int left, int top, int right, int bottom)468 ChildDrawable addLayer(Drawable dr, int[] themeAttrs, int id, 469 int left, int top, int right, int bottom) { 470 final ChildDrawable childDrawable = createLayer(dr); 471 childDrawable.mId = id; 472 childDrawable.mThemeAttrs = themeAttrs; 473 childDrawable.mDrawable.setAutoMirrored(isAutoMirrored()); 474 childDrawable.mInsetL = left; 475 childDrawable.mInsetT = top; 476 childDrawable.mInsetR = right; 477 childDrawable.mInsetB = bottom; 478 479 addLayer(childDrawable); 480 481 mLayerState.mChildrenChangingConfigurations |= dr.getChangingConfigurations(); 482 dr.setCallback(this); 483 484 return childDrawable; 485 } 486 createLayer(Drawable dr)487 private ChildDrawable createLayer(Drawable dr) { 488 final ChildDrawable layer = new ChildDrawable(mLayerState.mDensity); 489 layer.mDrawable = dr; 490 return layer; 491 } 492 493 /** 494 * Adds a new layer containing the specified {@code drawable} to the end of 495 * the layer list and returns its index. 496 * 497 * @param dr The drawable to add as a new layer. 498 * @return The index of the new layer. 499 */ addLayer(Drawable dr)500 public int addLayer(Drawable dr) { 501 final ChildDrawable layer = createLayer(dr); 502 final int index = addLayer(layer); 503 ensurePadding(); 504 refreshChildPadding(index, layer); 505 return index; 506 } 507 508 /** 509 * Looks for a layer with the given ID and returns its {@link Drawable}. 510 * <p> 511 * If multiple layers are found for the given ID, returns the 512 * {@link Drawable} for the matching layer at the highest index. 513 * 514 * @param id The layer ID to search for. 515 * @return The {@link Drawable} for the highest-indexed layer that has the 516 * given ID, or null if not found. 517 */ findDrawableByLayerId(int id)518 public Drawable findDrawableByLayerId(int id) { 519 final ChildDrawable[] layers = mLayerState.mChildren; 520 for (int i = mLayerState.mNumChildren - 1; i >= 0; i--) { 521 if (layers[i].mId == id) { 522 return layers[i].mDrawable; 523 } 524 } 525 526 return null; 527 } 528 529 /** 530 * Sets the ID of a layer. 531 * 532 * @param index The index of the layer to modify, must be in the range 533 * {@code 0...getNumberOfLayers()-1}. 534 * @param id The id to assign to the layer. 535 * 536 * @see #getId(int) 537 * @attr ref android.R.styleable#LayerDrawableItem_id 538 */ setId(int index, int id)539 public void setId(int index, int id) { 540 mLayerState.mChildren[index].mId = id; 541 } 542 543 /** 544 * Returns the ID of the specified layer. 545 * 546 * @param index The index of the layer, must be in the range 547 * {@code 0...getNumberOfLayers()-1}. 548 * @return The id of the layer or {@link android.view.View#NO_ID} if the 549 * layer has no id. 550 * 551 * @see #setId(int, int) 552 * @attr ref android.R.styleable#LayerDrawableItem_id 553 */ getId(int index)554 public int getId(int index) { 555 if (index >= mLayerState.mNumChildren) { 556 throw new IndexOutOfBoundsException(); 557 } 558 return mLayerState.mChildren[index].mId; 559 } 560 561 /** 562 * Returns the number of layers contained within this layer drawable. 563 * 564 * @return The number of layers. 565 */ getNumberOfLayers()566 public int getNumberOfLayers() { 567 return mLayerState.mNumChildren; 568 } 569 570 /** 571 * Replaces the {@link Drawable} for the layer with the given id. 572 * 573 * @param id The layer ID to search for. 574 * @param drawable The replacement {@link Drawable}. 575 * @return Whether the {@link Drawable} was replaced (could return false if 576 * the id was not found). 577 */ setDrawableByLayerId(int id, Drawable drawable)578 public boolean setDrawableByLayerId(int id, Drawable drawable) { 579 final int index = findIndexByLayerId(id); 580 if (index < 0) { 581 return false; 582 } 583 584 setDrawable(index, drawable); 585 return true; 586 } 587 588 /** 589 * Returns the layer with the specified {@code id}. 590 * <p> 591 * If multiple layers have the same ID, returns the layer with the lowest 592 * index. 593 * 594 * @param id The ID of the layer to return. 595 * @return The index of the layer with the specified ID. 596 */ findIndexByLayerId(int id)597 public int findIndexByLayerId(int id) { 598 final ChildDrawable[] layers = mLayerState.mChildren; 599 final int N = mLayerState.mNumChildren; 600 for (int i = 0; i < N; i++) { 601 final ChildDrawable childDrawable = layers[i]; 602 if (childDrawable.mId == id) { 603 return i; 604 } 605 } 606 607 return -1; 608 } 609 610 /** 611 * Sets the drawable for the layer at the specified index. 612 * 613 * @param index The index of the layer to modify, must be in the range 614 * {@code 0...getNumberOfLayers()-1}. 615 * @param drawable The drawable to set for the layer. 616 * 617 * @see #getDrawable(int) 618 * @attr ref android.R.styleable#LayerDrawableItem_drawable 619 */ setDrawable(int index, Drawable drawable)620 public void setDrawable(int index, Drawable drawable) { 621 if (index >= mLayerState.mNumChildren) { 622 throw new IndexOutOfBoundsException(); 623 } 624 625 final ChildDrawable[] layers = mLayerState.mChildren; 626 final ChildDrawable childDrawable = layers[index]; 627 if (childDrawable.mDrawable != null) { 628 if (drawable != null) { 629 final Rect bounds = childDrawable.mDrawable.getBounds(); 630 drawable.setBounds(bounds); 631 } 632 633 childDrawable.mDrawable.setCallback(null); 634 } 635 636 if (drawable != null) { 637 drawable.setCallback(this); 638 } 639 640 childDrawable.mDrawable = drawable; 641 mLayerState.invalidateCache(); 642 643 refreshChildPadding(index, childDrawable); 644 } 645 646 /** 647 * Returns the drawable for the layer at the specified index. 648 * 649 * @param index The index of the layer, must be in the range 650 * {@code 0...getNumberOfLayers()-1}. 651 * @return The {@link Drawable} at the specified layer index. 652 * 653 * @see #setDrawable(int, Drawable) 654 * @attr ref android.R.styleable#LayerDrawableItem_drawable 655 */ getDrawable(int index)656 public Drawable getDrawable(int index) { 657 if (index >= mLayerState.mNumChildren) { 658 throw new IndexOutOfBoundsException(); 659 } 660 return mLayerState.mChildren[index].mDrawable; 661 } 662 663 /** 664 * Sets an explicit size for the specified layer. 665 * <p> 666 * <strong>Note:</strong> Setting an explicit layer size changes the 667 * default layer gravity behavior. See {@link #setLayerGravity(int, int)} 668 * for more information. 669 * 670 * @param index the index of the layer to adjust 671 * @param w width in pixels, or -1 to use the intrinsic width 672 * @param h height in pixels, or -1 to use the intrinsic height 673 * @see #getLayerWidth(int) 674 * @see #getLayerHeight(int) 675 * @attr ref android.R.styleable#LayerDrawableItem_width 676 * @attr ref android.R.styleable#LayerDrawableItem_height 677 */ setLayerSize(int index, int w, int h)678 public void setLayerSize(int index, int w, int h) { 679 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 680 childDrawable.mWidth = w; 681 childDrawable.mHeight = h; 682 } 683 684 /** 685 * @param index the index of the layer to adjust 686 * @param w width in pixels, or -1 to use the intrinsic width 687 * @attr ref android.R.styleable#LayerDrawableItem_width 688 */ setLayerWidth(int index, int w)689 public void setLayerWidth(int index, int w) { 690 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 691 childDrawable.mWidth = w; 692 } 693 694 /** 695 * @param index the index of the drawable to adjust 696 * @return the explicit width of the layer, or -1 if not specified 697 * @see #setLayerSize(int, int, int) 698 * @attr ref android.R.styleable#LayerDrawableItem_width 699 */ getLayerWidth(int index)700 public int getLayerWidth(int index) { 701 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 702 return childDrawable.mWidth; 703 } 704 705 /** 706 * @param index the index of the layer to adjust 707 * @param h height in pixels, or -1 to use the intrinsic height 708 * @attr ref android.R.styleable#LayerDrawableItem_height 709 */ setLayerHeight(int index, int h)710 public void setLayerHeight(int index, int h) { 711 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 712 childDrawable.mHeight = h; 713 } 714 715 /** 716 * @param index the index of the drawable to adjust 717 * @return the explicit height of the layer, or -1 if not specified 718 * @see #setLayerSize(int, int, int) 719 * @attr ref android.R.styleable#LayerDrawableItem_height 720 */ getLayerHeight(int index)721 public int getLayerHeight(int index) { 722 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 723 return childDrawable.mHeight; 724 } 725 726 /** 727 * Sets the gravity used to position or stretch the specified layer within 728 * its container. Gravity is applied after any layer insets (see 729 * {@link #setLayerInset(int, int, int, int, int)}) or padding (see 730 * {@link #setPaddingMode(int)}). 731 * <p> 732 * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default 733 * behavior depends on whether an explicit width or height has been set 734 * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set, 735 * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or 736 * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction 737 * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}. 738 * 739 * @param index the index of the drawable to adjust 740 * @param gravity the gravity to set for the layer 741 * 742 * @see #getLayerGravity(int) 743 * @attr ref android.R.styleable#LayerDrawableItem_gravity 744 */ setLayerGravity(int index, int gravity)745 public void setLayerGravity(int index, int gravity) { 746 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 747 childDrawable.mGravity = gravity; 748 } 749 750 /** 751 * @param index the index of the layer 752 * @return the gravity used to position or stretch the specified layer 753 * within its container 754 * 755 * @see #setLayerGravity(int, int) 756 * @attr ref android.R.styleable#LayerDrawableItem_gravity 757 */ getLayerGravity(int index)758 public int getLayerGravity(int index) { 759 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 760 return childDrawable.mGravity; 761 } 762 763 /** 764 * Specifies the insets in pixels for the drawable at the specified index. 765 * 766 * @param index the index of the drawable to adjust 767 * @param l number of pixels to add to the left bound 768 * @param t number of pixels to add to the top bound 769 * @param r number of pixels to subtract from the right bound 770 * @param b number of pixels to subtract from the bottom bound 771 * 772 * @attr ref android.R.styleable#LayerDrawableItem_left 773 * @attr ref android.R.styleable#LayerDrawableItem_top 774 * @attr ref android.R.styleable#LayerDrawableItem_right 775 * @attr ref android.R.styleable#LayerDrawableItem_bottom 776 */ setLayerInset(int index, int l, int t, int r, int b)777 public void setLayerInset(int index, int l, int t, int r, int b) { 778 setLayerInsetInternal(index, l, t, r, b, INSET_UNDEFINED, INSET_UNDEFINED); 779 } 780 781 /** 782 * Specifies the relative insets in pixels for the drawable at the 783 * specified index. 784 * 785 * @param index the index of the layer to adjust 786 * @param s number of pixels to inset from the start bound 787 * @param t number of pixels to inset from the top bound 788 * @param e number of pixels to inset from the end bound 789 * @param b number of pixels to inset from the bottom bound 790 * 791 * @attr ref android.R.styleable#LayerDrawableItem_start 792 * @attr ref android.R.styleable#LayerDrawableItem_top 793 * @attr ref android.R.styleable#LayerDrawableItem_end 794 * @attr ref android.R.styleable#LayerDrawableItem_bottom 795 */ setLayerInsetRelative(int index, int s, int t, int e, int b)796 public void setLayerInsetRelative(int index, int s, int t, int e, int b) { 797 setLayerInsetInternal(index, 0, t, 0, b, s, e); 798 } 799 800 /** 801 * @param index the index of the layer to adjust 802 * @param l number of pixels to inset from the left bound 803 * @attr ref android.R.styleable#LayerDrawableItem_left 804 */ setLayerInsetLeft(int index, int l)805 public void setLayerInsetLeft(int index, int l) { 806 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 807 childDrawable.mInsetL = l; 808 } 809 810 /** 811 * @param index the index of the layer 812 * @return number of pixels to inset from the left bound 813 * @attr ref android.R.styleable#LayerDrawableItem_left 814 */ getLayerInsetLeft(int index)815 public int getLayerInsetLeft(int index) { 816 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 817 return childDrawable.mInsetL; 818 } 819 820 /** 821 * @param index the index of the layer to adjust 822 * @param r number of pixels to inset from the right bound 823 * @attr ref android.R.styleable#LayerDrawableItem_right 824 */ setLayerInsetRight(int index, int r)825 public void setLayerInsetRight(int index, int r) { 826 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 827 childDrawable.mInsetR = r; 828 } 829 830 /** 831 * @param index the index of the layer 832 * @return number of pixels to inset from the right bound 833 * @attr ref android.R.styleable#LayerDrawableItem_right 834 */ getLayerInsetRight(int index)835 public int getLayerInsetRight(int index) { 836 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 837 return childDrawable.mInsetR; 838 } 839 840 /** 841 * @param index the index of the layer to adjust 842 * @param t number of pixels to inset from the top bound 843 * @attr ref android.R.styleable#LayerDrawableItem_top 844 */ setLayerInsetTop(int index, int t)845 public void setLayerInsetTop(int index, int t) { 846 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 847 childDrawable.mInsetT = t; 848 } 849 850 /** 851 * @param index the index of the layer 852 * @return number of pixels to inset from the top bound 853 * @attr ref android.R.styleable#LayerDrawableItem_top 854 */ getLayerInsetTop(int index)855 public int getLayerInsetTop(int index) { 856 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 857 return childDrawable.mInsetT; 858 } 859 860 /** 861 * @param index the index of the layer to adjust 862 * @param b number of pixels to inset from the bottom bound 863 * @attr ref android.R.styleable#LayerDrawableItem_bottom 864 */ setLayerInsetBottom(int index, int b)865 public void setLayerInsetBottom(int index, int b) { 866 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 867 childDrawable.mInsetB = b; 868 } 869 870 /** 871 * @param index the index of the layer 872 * @return number of pixels to inset from the bottom bound 873 * @attr ref android.R.styleable#LayerDrawableItem_bottom 874 */ getLayerInsetBottom(int index)875 public int getLayerInsetBottom(int index) { 876 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 877 return childDrawable.mInsetB; 878 } 879 880 /** 881 * @param index the index of the layer to adjust 882 * @param s number of pixels to inset from the start bound 883 * @attr ref android.R.styleable#LayerDrawableItem_start 884 */ setLayerInsetStart(int index, int s)885 public void setLayerInsetStart(int index, int s) { 886 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 887 childDrawable.mInsetS = s; 888 } 889 890 /** 891 * @param index the index of the layer 892 * @return the number of pixels to inset from the start bound, or 893 * {@link #INSET_UNDEFINED} if not specified 894 * @attr ref android.R.styleable#LayerDrawableItem_start 895 */ getLayerInsetStart(int index)896 public int getLayerInsetStart(int index) { 897 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 898 return childDrawable.mInsetS; 899 } 900 901 /** 902 * @param index the index of the layer to adjust 903 * @param e number of pixels to inset from the end bound, or 904 * {@link #INSET_UNDEFINED} if not specified 905 * @attr ref android.R.styleable#LayerDrawableItem_end 906 */ setLayerInsetEnd(int index, int e)907 public void setLayerInsetEnd(int index, int e) { 908 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 909 childDrawable.mInsetE = e; 910 } 911 912 /** 913 * @param index the index of the layer 914 * @return number of pixels to inset from the end bound 915 * @attr ref android.R.styleable#LayerDrawableItem_end 916 */ getLayerInsetEnd(int index)917 public int getLayerInsetEnd(int index) { 918 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 919 return childDrawable.mInsetE; 920 } 921 setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e)922 private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) { 923 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 924 childDrawable.mInsetL = l; 925 childDrawable.mInsetT = t; 926 childDrawable.mInsetR = r; 927 childDrawable.mInsetB = b; 928 childDrawable.mInsetS = s; 929 childDrawable.mInsetE = e; 930 } 931 932 /** 933 * Specifies how layer padding should affect the bounds of subsequent 934 * layers. The default value is {@link #PADDING_MODE_NEST}. 935 * 936 * @param mode padding mode, one of: 937 * <ul> 938 * <li>{@link #PADDING_MODE_NEST} to nest each layer inside the 939 * padding of the previous layer 940 * <li>{@link #PADDING_MODE_STACK} to stack each layer directly 941 * atop the previous layer 942 * </ul> 943 * 944 * @see #getPaddingMode() 945 * @attr ref android.R.styleable#LayerDrawable_paddingMode 946 */ setPaddingMode(int mode)947 public void setPaddingMode(int mode) { 948 if (mLayerState.mPaddingMode != mode) { 949 mLayerState.mPaddingMode = mode; 950 } 951 } 952 953 /** 954 * @return the current padding mode 955 * 956 * @see #setPaddingMode(int) 957 * @attr ref android.R.styleable#LayerDrawable_paddingMode 958 */ getPaddingMode()959 public int getPaddingMode() { 960 return mLayerState.mPaddingMode; 961 } 962 963 /** 964 * Temporarily suspends child invalidation. 965 * 966 * @see #resumeChildInvalidation() 967 */ suspendChildInvalidation()968 private void suspendChildInvalidation() { 969 mSuspendChildInvalidation = true; 970 } 971 972 /** 973 * Resumes child invalidation after suspension, immediately performing an 974 * invalidation if one was requested by a child during suspension. 975 * 976 * @see #suspendChildInvalidation() 977 */ resumeChildInvalidation()978 private void resumeChildInvalidation() { 979 mSuspendChildInvalidation = false; 980 981 if (mChildRequestedInvalidation) { 982 mChildRequestedInvalidation = false; 983 invalidateSelf(); 984 } 985 } 986 987 @Override invalidateDrawable(@onNull Drawable who)988 public void invalidateDrawable(@NonNull Drawable who) { 989 if (mSuspendChildInvalidation) { 990 mChildRequestedInvalidation = true; 991 } else { 992 // This may have been called as the result of a tint changing, in 993 // which case we may need to refresh the cached statefulness or 994 // opacity. 995 mLayerState.invalidateCache(); 996 997 invalidateSelf(); 998 } 999 } 1000 1001 @Override scheduleDrawable(@onNull Drawable who, @NonNull Runnable what, long when)1002 public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { 1003 scheduleSelf(what, when); 1004 } 1005 1006 @Override unscheduleDrawable(@onNull Drawable who, @NonNull Runnable what)1007 public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { 1008 unscheduleSelf(what); 1009 } 1010 1011 @Override draw(Canvas canvas)1012 public void draw(Canvas canvas) { 1013 final ChildDrawable[] array = mLayerState.mChildren; 1014 final int N = mLayerState.mNumChildren; 1015 for (int i = 0; i < N; i++) { 1016 final Drawable dr = array[i].mDrawable; 1017 if (dr != null) { 1018 dr.draw(canvas); 1019 } 1020 } 1021 } 1022 1023 @Override getChangingConfigurations()1024 public @Config int getChangingConfigurations() { 1025 return super.getChangingConfigurations() | mLayerState.getChangingConfigurations(); 1026 } 1027 1028 @Override getPadding(Rect padding)1029 public boolean getPadding(Rect padding) { 1030 final LayerState layerState = mLayerState; 1031 if (layerState.mPaddingMode == PADDING_MODE_NEST) { 1032 computeNestedPadding(padding); 1033 } else { 1034 computeStackedPadding(padding); 1035 } 1036 1037 final int paddingT = layerState.mPaddingTop; 1038 final int paddingB = layerState.mPaddingBottom; 1039 1040 // Resolve padding for RTL. Relative padding overrides absolute 1041 // padding. 1042 final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL; 1043 final int paddingRtlL = isLayoutRtl ? layerState.mPaddingEnd : layerState.mPaddingStart; 1044 final int paddingRtlR = isLayoutRtl ? layerState.mPaddingStart : layerState.mPaddingEnd; 1045 final int paddingL = paddingRtlL >= 0 ? paddingRtlL : layerState.mPaddingLeft; 1046 final int paddingR = paddingRtlR >= 0 ? paddingRtlR : layerState.mPaddingRight; 1047 1048 // If padding was explicitly specified (e.g. not -1) then override the 1049 // computed padding in that dimension. 1050 if (paddingL >= 0) { 1051 padding.left = paddingL; 1052 } 1053 1054 if (paddingT >= 0) { 1055 padding.top = paddingT; 1056 } 1057 1058 if (paddingR >= 0) { 1059 padding.right = paddingR; 1060 } 1061 1062 if (paddingB >= 0) { 1063 padding.bottom = paddingB; 1064 } 1065 1066 return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0; 1067 } 1068 1069 /** 1070 * Sets the absolute padding. 1071 * <p> 1072 * If padding in a dimension is specified as {@code -1}, the resolved 1073 * padding will use the value computed according to the padding mode (see 1074 * {@link #setPaddingMode(int)}). 1075 * <p> 1076 * Calling this method clears any relative padding values previously set 1077 * using {@link #setPaddingRelative(int, int, int, int)}. 1078 * 1079 * @param left the left padding in pixels, or -1 to use computed padding 1080 * @param top the top padding in pixels, or -1 to use computed padding 1081 * @param right the right padding in pixels, or -1 to use computed padding 1082 * @param bottom the bottom padding in pixels, or -1 to use computed 1083 * padding 1084 * @attr ref android.R.styleable#LayerDrawable_paddingLeft 1085 * @attr ref android.R.styleable#LayerDrawable_paddingTop 1086 * @attr ref android.R.styleable#LayerDrawable_paddingRight 1087 * @attr ref android.R.styleable#LayerDrawable_paddingBottom 1088 * @see #setPaddingRelative(int, int, int, int) 1089 */ setPadding(int left, int top, int right, int bottom)1090 public void setPadding(int left, int top, int right, int bottom) { 1091 final LayerState layerState = mLayerState; 1092 layerState.mPaddingLeft = left; 1093 layerState.mPaddingTop = top; 1094 layerState.mPaddingRight = right; 1095 layerState.mPaddingBottom = bottom; 1096 1097 // Clear relative padding values. 1098 layerState.mPaddingStart = -1; 1099 layerState.mPaddingEnd = -1; 1100 } 1101 1102 /** 1103 * Sets the relative padding. 1104 * <p> 1105 * If padding in a dimension is specified as {@code -1}, the resolved 1106 * padding will use the value computed according to the padding mode (see 1107 * {@link #setPaddingMode(int)}). 1108 * <p> 1109 * Calling this method clears any absolute padding values previously set 1110 * using {@link #setPadding(int, int, int, int)}. 1111 * 1112 * @param start the start padding in pixels, or -1 to use computed padding 1113 * @param top the top padding in pixels, or -1 to use computed padding 1114 * @param end the end padding in pixels, or -1 to use computed padding 1115 * @param bottom the bottom padding in pixels, or -1 to use computed 1116 * padding 1117 * @attr ref android.R.styleable#LayerDrawable_paddingStart 1118 * @attr ref android.R.styleable#LayerDrawable_paddingTop 1119 * @attr ref android.R.styleable#LayerDrawable_paddingEnd 1120 * @attr ref android.R.styleable#LayerDrawable_paddingBottom 1121 * @see #setPadding(int, int, int, int) 1122 */ setPaddingRelative(int start, int top, int end, int bottom)1123 public void setPaddingRelative(int start, int top, int end, int bottom) { 1124 final LayerState layerState = mLayerState; 1125 layerState.mPaddingStart = start; 1126 layerState.mPaddingTop = top; 1127 layerState.mPaddingEnd = end; 1128 layerState.mPaddingBottom = bottom; 1129 1130 // Clear absolute padding values. 1131 layerState.mPaddingLeft = -1; 1132 layerState.mPaddingRight = -1; 1133 } 1134 1135 /** 1136 * Returns the left padding in pixels. 1137 * <p> 1138 * A return value of {@code -1} means there is no explicit padding set for 1139 * this dimension. As a result, the value for this dimension returned by 1140 * {@link #getPadding(Rect)} will be computed from the child layers 1141 * according to the padding mode (see {@link #getPaddingMode()}. 1142 * 1143 * @return the left padding in pixels, or -1 if not explicitly specified 1144 * @see #setPadding(int, int, int, int) 1145 * @see #getPadding(Rect) 1146 */ getLeftPadding()1147 public int getLeftPadding() { 1148 return mLayerState.mPaddingLeft; 1149 } 1150 1151 /** 1152 * Returns the right padding in pixels. 1153 * <p> 1154 * A return value of {@code -1} means there is no explicit padding set for 1155 * this dimension. As a result, the value for this dimension returned by 1156 * {@link #getPadding(Rect)} will be computed from the child layers 1157 * according to the padding mode (see {@link #getPaddingMode()}. 1158 * 1159 * @return the right padding in pixels, or -1 if not explicitly specified 1160 * @see #setPadding(int, int, int, int) 1161 * @see #getPadding(Rect) 1162 */ getRightPadding()1163 public int getRightPadding() { 1164 return mLayerState.mPaddingRight; 1165 } 1166 1167 /** 1168 * Returns the start padding in pixels. 1169 * <p> 1170 * A return value of {@code -1} means there is no explicit padding set for 1171 * this dimension. As a result, the value for this dimension returned by 1172 * {@link #getPadding(Rect)} will be computed from the child layers 1173 * according to the padding mode (see {@link #getPaddingMode()}. 1174 * 1175 * @return the start padding in pixels, or -1 if not explicitly specified 1176 * @see #setPaddingRelative(int, int, int, int) 1177 * @see #getPadding(Rect) 1178 */ getStartPadding()1179 public int getStartPadding() { 1180 return mLayerState.mPaddingStart; 1181 } 1182 1183 /** 1184 * Returns the end padding in pixels. 1185 * <p> 1186 * A return value of {@code -1} means there is no explicit padding set for 1187 * this dimension. As a result, the value for this dimension returned by 1188 * {@link #getPadding(Rect)} will be computed from the child layers 1189 * according to the padding mode (see {@link #getPaddingMode()}. 1190 * 1191 * @return the end padding in pixels, or -1 if not explicitly specified 1192 * @see #setPaddingRelative(int, int, int, int) 1193 * @see #getPadding(Rect) 1194 */ getEndPadding()1195 public int getEndPadding() { 1196 return mLayerState.mPaddingEnd; 1197 } 1198 1199 /** 1200 * Returns the top padding in pixels. 1201 * <p> 1202 * A return value of {@code -1} means there is no explicit padding set for 1203 * this dimension. As a result, the value for this dimension returned by 1204 * {@link #getPadding(Rect)} will be computed from the child layers 1205 * according to the padding mode (see {@link #getPaddingMode()}. 1206 * 1207 * @return the top padding in pixels, or -1 if not explicitly specified 1208 * @see #setPadding(int, int, int, int) 1209 * @see #setPaddingRelative(int, int, int, int) 1210 * @see #getPadding(Rect) 1211 */ getTopPadding()1212 public int getTopPadding() { 1213 return mLayerState.mPaddingTop; 1214 } 1215 1216 /** 1217 * Returns the bottom padding in pixels. 1218 * <p> 1219 * A return value of {@code -1} means there is no explicit padding set for 1220 * this dimension. As a result, the value for this dimension returned by 1221 * {@link #getPadding(Rect)} will be computed from the child layers 1222 * according to the padding mode (see {@link #getPaddingMode()}. 1223 * 1224 * @return the bottom padding in pixels, or -1 if not explicitly specified 1225 * @see #setPadding(int, int, int, int) 1226 * @see #setPaddingRelative(int, int, int, int) 1227 * @see #getPadding(Rect) 1228 */ getBottomPadding()1229 public int getBottomPadding() { 1230 return mLayerState.mPaddingBottom; 1231 } 1232 computeNestedPadding(Rect padding)1233 private void computeNestedPadding(Rect padding) { 1234 padding.left = 0; 1235 padding.top = 0; 1236 padding.right = 0; 1237 padding.bottom = 0; 1238 1239 // Add all the padding. 1240 final ChildDrawable[] array = mLayerState.mChildren; 1241 final int N = mLayerState.mNumChildren; 1242 for (int i = 0; i < N; i++) { 1243 refreshChildPadding(i, array[i]); 1244 1245 padding.left += mPaddingL[i]; 1246 padding.top += mPaddingT[i]; 1247 padding.right += mPaddingR[i]; 1248 padding.bottom += mPaddingB[i]; 1249 } 1250 } 1251 computeStackedPadding(Rect padding)1252 private void computeStackedPadding(Rect padding) { 1253 padding.left = 0; 1254 padding.top = 0; 1255 padding.right = 0; 1256 padding.bottom = 0; 1257 1258 // Take the max padding. 1259 final ChildDrawable[] array = mLayerState.mChildren; 1260 final int N = mLayerState.mNumChildren; 1261 for (int i = 0; i < N; i++) { 1262 refreshChildPadding(i, array[i]); 1263 1264 padding.left = Math.max(padding.left, mPaddingL[i]); 1265 padding.top = Math.max(padding.top, mPaddingT[i]); 1266 padding.right = Math.max(padding.right, mPaddingR[i]); 1267 padding.bottom = Math.max(padding.bottom, mPaddingB[i]); 1268 } 1269 } 1270 1271 /** 1272 * Populates <code>outline</code> with the first available (non-empty) layer outline. 1273 * 1274 * @param outline Outline in which to place the first available layer outline 1275 */ 1276 @Override getOutline(@onNull Outline outline)1277 public void getOutline(@NonNull Outline outline) { 1278 final ChildDrawable[] array = mLayerState.mChildren; 1279 final int N = mLayerState.mNumChildren; 1280 for (int i = 0; i < N; i++) { 1281 final Drawable dr = array[i].mDrawable; 1282 if (dr != null) { 1283 dr.getOutline(outline); 1284 if (!outline.isEmpty()) { 1285 return; 1286 } 1287 } 1288 } 1289 } 1290 1291 @Override setHotspot(float x, float y)1292 public void setHotspot(float x, float y) { 1293 final ChildDrawable[] array = mLayerState.mChildren; 1294 final int N = mLayerState.mNumChildren; 1295 for (int i = 0; i < N; i++) { 1296 final Drawable dr = array[i].mDrawable; 1297 if (dr != null) { 1298 dr.setHotspot(x, y); 1299 } 1300 } 1301 } 1302 1303 @Override setHotspotBounds(int left, int top, int right, int bottom)1304 public void setHotspotBounds(int left, int top, int right, int bottom) { 1305 final ChildDrawable[] array = mLayerState.mChildren; 1306 final int N = mLayerState.mNumChildren; 1307 for (int i = 0; i < N; i++) { 1308 final Drawable dr = array[i].mDrawable; 1309 if (dr != null) { 1310 dr.setHotspotBounds(left, top, right, bottom); 1311 } 1312 } 1313 1314 if (mHotspotBounds == null) { 1315 mHotspotBounds = new Rect(left, top, right, bottom); 1316 } else { 1317 mHotspotBounds.set(left, top, right, bottom); 1318 } 1319 } 1320 1321 @Override getHotspotBounds(Rect outRect)1322 public void getHotspotBounds(Rect outRect) { 1323 if (mHotspotBounds != null) { 1324 outRect.set(mHotspotBounds); 1325 } else { 1326 super.getHotspotBounds(outRect); 1327 } 1328 } 1329 1330 @Override setVisible(boolean visible, boolean restart)1331 public boolean setVisible(boolean visible, boolean restart) { 1332 final boolean changed = super.setVisible(visible, restart); 1333 final ChildDrawable[] array = mLayerState.mChildren; 1334 final int N = mLayerState.mNumChildren; 1335 for (int i = 0; i < N; i++) { 1336 final Drawable dr = array[i].mDrawable; 1337 if (dr != null) { 1338 dr.setVisible(visible, restart); 1339 } 1340 } 1341 1342 return changed; 1343 } 1344 1345 @Override setDither(boolean dither)1346 public void setDither(boolean dither) { 1347 final ChildDrawable[] array = mLayerState.mChildren; 1348 final int N = mLayerState.mNumChildren; 1349 for (int i = 0; i < N; i++) { 1350 final Drawable dr = array[i].mDrawable; 1351 if (dr != null) { 1352 dr.setDither(dither); 1353 } 1354 } 1355 } 1356 1357 @Override setAlpha(int alpha)1358 public void setAlpha(int alpha) { 1359 final ChildDrawable[] array = mLayerState.mChildren; 1360 final int N = mLayerState.mNumChildren; 1361 for (int i = 0; i < N; i++) { 1362 final Drawable dr = array[i].mDrawable; 1363 if (dr != null) { 1364 dr.setAlpha(alpha); 1365 } 1366 } 1367 } 1368 1369 @Override getAlpha()1370 public int getAlpha() { 1371 final Drawable dr = getFirstNonNullDrawable(); 1372 if (dr != null) { 1373 return dr.getAlpha(); 1374 } else { 1375 return super.getAlpha(); 1376 } 1377 } 1378 1379 @Override setColorFilter(ColorFilter colorFilter)1380 public void setColorFilter(ColorFilter colorFilter) { 1381 final ChildDrawable[] array = mLayerState.mChildren; 1382 final int N = mLayerState.mNumChildren; 1383 for (int i = 0; i < N; i++) { 1384 final Drawable dr = array[i].mDrawable; 1385 if (dr != null) { 1386 dr.setColorFilter(colorFilter); 1387 } 1388 } 1389 } 1390 1391 @Override setTintList(ColorStateList tint)1392 public void setTintList(ColorStateList tint) { 1393 final ChildDrawable[] array = mLayerState.mChildren; 1394 final int N = mLayerState.mNumChildren; 1395 for (int i = 0; i < N; i++) { 1396 final Drawable dr = array[i].mDrawable; 1397 if (dr != null) { 1398 dr.setTintList(tint); 1399 } 1400 } 1401 } 1402 1403 @Override setTintBlendMode(@onNull BlendMode blendMode)1404 public void setTintBlendMode(@NonNull BlendMode blendMode) { 1405 final ChildDrawable[] array = mLayerState.mChildren; 1406 final int N = mLayerState.mNumChildren; 1407 for (int i = 0; i < N; i++) { 1408 final Drawable dr = array[i].mDrawable; 1409 if (dr != null) { 1410 dr.setTintBlendMode(blendMode); 1411 } 1412 } 1413 } 1414 getFirstNonNullDrawable()1415 private Drawable getFirstNonNullDrawable() { 1416 final ChildDrawable[] array = mLayerState.mChildren; 1417 final int N = mLayerState.mNumChildren; 1418 for (int i = 0; i < N; i++) { 1419 final Drawable dr = array[i].mDrawable; 1420 if (dr != null) { 1421 return dr; 1422 } 1423 } 1424 return null; 1425 } 1426 1427 /** 1428 * Sets the opacity of this drawable directly instead of collecting the 1429 * states from the layers. 1430 * 1431 * @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN 1432 * PixelFormat.UNKNOWN} for the default behavior 1433 * @see PixelFormat#UNKNOWN 1434 * @see PixelFormat#TRANSLUCENT 1435 * @see PixelFormat#TRANSPARENT 1436 * @see PixelFormat#OPAQUE 1437 */ setOpacity(int opacity)1438 public void setOpacity(int opacity) { 1439 mLayerState.mOpacityOverride = opacity; 1440 } 1441 1442 @Override getOpacity()1443 public int getOpacity() { 1444 if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) { 1445 return mLayerState.mOpacityOverride; 1446 } 1447 return mLayerState.getOpacity(); 1448 } 1449 1450 @Override setAutoMirrored(boolean mirrored)1451 public void setAutoMirrored(boolean mirrored) { 1452 mLayerState.mAutoMirrored = mirrored; 1453 1454 final ChildDrawable[] array = mLayerState.mChildren; 1455 final int N = mLayerState.mNumChildren; 1456 for (int i = 0; i < N; i++) { 1457 final Drawable dr = array[i].mDrawable; 1458 if (dr != null) { 1459 dr.setAutoMirrored(mirrored); 1460 } 1461 } 1462 } 1463 1464 @Override isAutoMirrored()1465 public boolean isAutoMirrored() { 1466 return mLayerState.mAutoMirrored; 1467 } 1468 1469 @Override jumpToCurrentState()1470 public void jumpToCurrentState() { 1471 final ChildDrawable[] array = mLayerState.mChildren; 1472 final int N = mLayerState.mNumChildren; 1473 for (int i = 0; i < N; i++) { 1474 final Drawable dr = array[i].mDrawable; 1475 if (dr != null) { 1476 dr.jumpToCurrentState(); 1477 } 1478 } 1479 } 1480 1481 @Override isStateful()1482 public boolean isStateful() { 1483 return mLayerState.isStateful(); 1484 } 1485 1486 /** @hide */ 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