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