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