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