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