LayerDrawable.java revision 06ff2af68aa1041eeb26778e994e0fe196bf8b1e
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 17package android.graphics.drawable; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.content.res.ColorStateList; 22import android.content.res.Resources; 23import android.content.res.Resources.Theme; 24import android.content.res.TypedArray; 25import android.graphics.Bitmap; 26import android.graphics.Canvas; 27import android.graphics.ColorFilter; 28import android.graphics.Outline; 29import android.graphics.PixelFormat; 30import android.graphics.PorterDuff.Mode; 31import android.graphics.Rect; 32import android.util.AttributeSet; 33import android.util.LayoutDirection; 34import android.view.Gravity; 35import android.view.View; 36 37import com.android.internal.R; 38 39import org.xmlpull.v1.XmlPullParser; 40import org.xmlpull.v1.XmlPullParserException; 41 42import java.io.IOException; 43import 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*/ 68public 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 */ 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 */ 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 139 LayerDrawable() { 140 this((LayerState) null, null); 141 } 142 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 151 LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) { 152 return new LayerState(state, this, res); 153 } 154 155 @Override 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 */ 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 */ 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 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 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 341 public boolean canApplyTheme() { 342 return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme(); 343 } 344 345 /** 346 * @hide 347 */ 348 @Override 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 848 public int getLayerInsetEnd(int index) { 849 final ChildDrawable childDrawable = mLayerState.mChildren[index]; 850 return childDrawable.mInsetE; 851 } 852 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 */ 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 */ 890 public int getPaddingMode() { 891 return mLayerState.mPaddingMode; 892 } 893 894 @Override 895 public void invalidateDrawable(Drawable who) { 896 invalidateSelf(); 897 } 898 899 @Override 900 public void scheduleDrawable(Drawable who, Runnable what, long when) { 901 scheduleSelf(what, when); 902 } 903 904 @Override 905 public void unscheduleDrawable(Drawable who, Runnable what) { 906 unscheduleSelf(what); 907 } 908 909 @Override 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 922 public int getChangingConfigurations() { 923 return super.getChangingConfigurations() | mLayerState.getChangingConfigurations(); 924 } 925 926 @Override 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1128 public int getBottomPadding() { 1129 return mLayerState.mPaddingBottom; 1130 } 1131 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 1337 public void setOpacity(int opacity) { 1338 mLayerState.mOpacityOverride = opacity; 1339 } 1340 1341 @Override 1342 public int getOpacity() { 1343 if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) { 1344 return mLayerState.mOpacityOverride; 1345 } 1346 return mLayerState.getOpacity(); 1347 } 1348 1349 @Override 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 1364 public boolean isAutoMirrored() { 1365 return mLayerState.mAutoMirrored; 1366 } 1367 1368 @Override 1369 public boolean isStateful() { 1370 return mLayerState.isStateful(); 1371 } 1372 1373 @Override 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 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 1416 protected void onBoundsChange(Rect bounds) { 1417 updateLayerBounds(bounds); 1418 } 1419 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