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