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