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