2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.graphics.drawable;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.pm.ActivityInfo.Config;
22 import android.content.res.ColorStateList;
23 import android.content.res.Resources;
24 import android.content.res.Resources.Theme;
25 import android.content.res.TypedArray;
26 import android.graphics.Bitmap;
27 import android.graphics.Canvas;
28 import android.graphics.ColorFilter;
29 import android.graphics.Outline;
30 import android.graphics.PixelFormat;
31 import android.graphics.PorterDuff.Mode;
32 import android.graphics.Rect;
33 import android.util.AttributeSet;
34 import android.util.DisplayMetrics;
35 import android.util.LayoutDirection;
36 import android.view.Gravity;
37 import android.view.View;
39 import com.android.internal.R;
41 import org.xmlpull.v1.XmlPullParser;
42 import org.xmlpull.v1.XmlPullParserException;
44 import java.io.IOException;
45 import java.util.Collection;
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.
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>.
54 * For more information, see the guide to
55 * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
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
70 public class LayerDrawable extends Drawable implements Drawable.Callback {
72 * Padding mode used to nest each layer inside the padding of the previous
75 * @see #setPaddingMode(int)
77 public static final int PADDING_MODE_NEST = 0;
80 * Padding mode used to stack each layer directly atop the previous layer.
82 * @see #setPaddingMode(int)
84 public static final int PADDING_MODE_STACK = 1;
87 * Value used for undefined start and end insets.
89 * @see #getLayerInsetStart(int)
90 * @see #getLayerInsetEnd(int)
92 public static final int INSET_UNDEFINED = Integer.MIN_VALUE;
94 LayerState mLayerState;
96 private int[] mPaddingL;
97 private int[] mPaddingT;
98 private int[] mPaddingR;
99 private int[] mPaddingB;
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;
108 * Creates a new layer drawable with the list of specified layers.
110 * @param layers a list of drawables to use as layers in this new drawable,
113 public LayerDrawable(@NonNull Drawable[] layers) {
118 * Creates a new layer drawable with the specified list of layers and the
119 * specified constant state.
121 * @param layers The list of layers to add to this drawable.
122 * @param state The constant drawable state.
124 LayerDrawable(@NonNull Drawable[] layers, @Nullable LayerState state) {
127 if (layers == null) {
128 throw new IllegalArgumentException("layers must be non-null");
131 final int length = layers.length;
132 final ChildDrawable[] r = new ChildDrawable[length];
133 for (int i = 0; i < length; i++) {
134 r[i] = new ChildDrawable(mLayerState.mDensity);
135 r[i].mDrawable = layers[i];
136 layers[i].setCallback(this);
137 mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations();
139 mLayerState.mNum = length;
140 mLayerState.mChildren = r;
147 this((LayerState) null, null);
151 * The one constructor to rule them all. This is called by all public
152 * constructors to set the state and initialize local properties.
154 LayerDrawable(@Nullable LayerState state, @Nullable Resources res) {
155 mLayerState = createConstantState(state, res);
156 if (mLayerState.mNum > 0) {
162 LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) {
163 return new LayerState(state, this, res);
167 public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
168 @NonNull AttributeSet attrs, @Nullable Theme theme)
169 throws XmlPullParserException, IOException {
170 super.inflate(r, parser, attrs, theme);
172 final LayerState state = mLayerState;
177 // The density may have changed since the last update. This will
178 // apply scaling to any existing constant state properties.
179 final int density = Drawable.resolveDensity(r, 0);
180 state.setDensity(density);
182 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable);
183 updateStateFromTypedArray(a);
186 final ChildDrawable[] array = state.mChildren;
187 final int N = state.mNum;
188 for (int i = 0; i < N; i++) {
189 final ChildDrawable layer = array[i];
190 layer.setDensity(density);
193 inflateLayers(r, parser, attrs, theme);
200 public void applyTheme(@NonNull Theme t) {
203 final LayerState state = mLayerState;
208 final int density = Drawable.resolveDensity(t.getResources(), 0);
209 state.setDensity(density);
211 if (state.mThemeAttrs != null) {
212 final TypedArray a = t.resolveAttributes(
213 state.mThemeAttrs, R.styleable.LayerDrawable);
214 updateStateFromTypedArray(a);
218 final ChildDrawable[] array = state.mChildren;
219 final int N = state.mNum;
220 for (int i = 0; i < N; i++) {
221 final ChildDrawable layer = array[i];
222 layer.setDensity(density);
224 if (layer.mThemeAttrs != null) {
225 final TypedArray a = t.resolveAttributes(
226 layer.mThemeAttrs, R.styleable.LayerDrawableItem);
227 updateLayerFromTypedArray(layer, a);
231 final Drawable d = layer.mDrawable;
232 if (d != null && d.canApplyTheme()) {
235 // Update cached mask of child changing configurations.
236 state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
242 * Inflates child layers using the specified parser.
244 private void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser,
245 @NonNull AttributeSet attrs, @Nullable Theme theme)
246 throws XmlPullParserException, IOException {
247 final LayerState state = mLayerState;
249 final int innerDepth = parser.getDepth() + 1;
252 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
253 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
254 if (type != XmlPullParser.START_TAG) {
258 if (depth > innerDepth || !parser.getName().equals("item")) {
262 final ChildDrawable layer = new ChildDrawable(state.mDensity);
263 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
264 updateLayerFromTypedArray(layer, a);
267 // If the layer doesn't have a drawable or unresolved theme
268 // attribute for a drawable, attempt to parse one from the child
270 if (layer.mDrawable == null && (layer.mThemeAttrs == null ||
271 layer.mThemeAttrs[R.styleable.LayerDrawableItem_drawable] == 0)) {
272 while ((type = parser.next()) == XmlPullParser.TEXT) {
274 if (type != XmlPullParser.START_TAG) {
275 throw new XmlPullParserException(parser.getPositionDescription()
276 + ": <item> tag requires a 'drawable' attribute or "
277 + "child tag defining a drawable");
279 layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
282 if (layer.mDrawable != null) {
283 state.mChildrenChangingConfigurations |=
284 layer.mDrawable.getChangingConfigurations();
285 layer.mDrawable.setCallback(this);
293 * Initializes the constant state from the values in the typed array.
295 private void updateStateFromTypedArray(@NonNull TypedArray a) {
296 final LayerState state = mLayerState;
298 // Account for any configuration changes.
299 state.mChangingConfigurations |= a.getChangingConfigurations();
301 // Extract the theme attributes, if any.
302 state.mThemeAttrs = a.extractThemeAttrs();
304 final int N = a.getIndexCount();
305 for (int i = 0; i < N; i++) {
306 final int attr = a.getIndex(i);
308 case R.styleable.LayerDrawable_opacity:
309 state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride);
311 case R.styleable.LayerDrawable_paddingTop:
312 state.mPaddingTop = a.getDimensionPixelOffset(attr, state.mPaddingTop);
314 case R.styleable.LayerDrawable_paddingBottom:
315 state.mPaddingBottom = a.getDimensionPixelOffset(attr, state.mPaddingBottom);
317 case R.styleable.LayerDrawable_paddingLeft:
318 state.mPaddingLeft = a.getDimensionPixelOffset(attr, state.mPaddingLeft);
320 case R.styleable.LayerDrawable_paddingRight:
321 state.mPaddingRight = a.getDimensionPixelOffset(attr, state.mPaddingRight);
323 case R.styleable.LayerDrawable_paddingStart:
324 state.mPaddingStart = a.getDimensionPixelOffset(attr, state.mPaddingStart);
326 case R.styleable.LayerDrawable_paddingEnd:
327 state.mPaddingEnd = a.getDimensionPixelOffset(attr, state.mPaddingEnd);
329 case R.styleable.LayerDrawable_autoMirrored:
330 state.mAutoMirrored = a.getBoolean(attr, state.mAutoMirrored);
332 case R.styleable.LayerDrawable_paddingMode:
333 state.mPaddingMode = a.getInteger(attr, state.mPaddingMode);
339 private void updateLayerFromTypedArray(@NonNull ChildDrawable layer, @NonNull TypedArray a) {
340 final LayerState state = mLayerState;
342 // Account for any configuration changes.
343 state.mChildrenChangingConfigurations |= a.getChangingConfigurations();
345 // Extract the theme attributes, if any.
346 layer.mThemeAttrs = a.extractThemeAttrs();
348 final int N = a.getIndexCount();
349 for (int i = 0; i < N; i++) {
350 final int attr = a.getIndex(i);
352 case R.styleable.LayerDrawableItem_left:
353 layer.mInsetL = a.getDimensionPixelOffset(attr, layer.mInsetL);
355 case R.styleable.LayerDrawableItem_top:
356 layer.mInsetT = a.getDimensionPixelOffset(attr, layer.mInsetT);
358 case R.styleable.LayerDrawableItem_right:
359 layer.mInsetR = a.getDimensionPixelOffset(attr, layer.mInsetR);
361 case R.styleable.LayerDrawableItem_bottom:
362 layer.mInsetB = a.getDimensionPixelOffset(attr, layer.mInsetB);
364 case R.styleable.LayerDrawableItem_start:
365 layer.mInsetS = a.getDimensionPixelOffset(attr, layer.mInsetS);
367 case R.styleable.LayerDrawableItem_end:
368 layer.mInsetE = a.getDimensionPixelOffset(attr, layer.mInsetE);
370 case R.styleable.LayerDrawableItem_width:
371 layer.mWidth = a.getDimensionPixelSize(attr, layer.mWidth);
373 case R.styleable.LayerDrawableItem_height:
374 layer.mHeight = a.getDimensionPixelSize(attr, layer.mHeight);
376 case R.styleable.LayerDrawableItem_gravity:
377 layer.mGravity = a.getInteger(attr, layer.mGravity);
379 case R.styleable.LayerDrawableItem_id:
380 layer.mId = a.getResourceId(attr, layer.mId);
385 final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
387 layer.mDrawable = dr;
392 public boolean canApplyTheme() {
393 return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme();
400 public boolean isProjected() {
401 if (super.isProjected()) {
405 final ChildDrawable[] layers = mLayerState.mChildren;
406 final int N = mLayerState.mNum;
407 for (int i = 0; i < N; i++) {
408 if (layers[i].mDrawable.isProjected()) {
417 * Adds a new layer at the end of list of layers and returns its index.
419 * @param layer The layer to add.
420 * @return The index of the layer.
422 int addLayer(@NonNull ChildDrawable layer) {
423 final LayerState st = mLayerState;
424 final int N = st.mChildren != null ? st.mChildren.length : 0;
425 final int i = st.mNum;
427 final ChildDrawable[] nu = new ChildDrawable[N + 10];
429 System.arraycopy(st.mChildren, 0, nu, 0, i);
435 st.mChildren[i] = layer;
437 st.invalidateCache();
442 * Add a new layer to this drawable. The new layer is identified by an id.
444 * @param dr The drawable to add as a layer.
445 * @param themeAttrs Theme attributes extracted from the layer.
446 * @param id The id of the new layer.
447 * @param left The left padding of the new layer.
448 * @param top The top padding of the new layer.
449 * @param right The right padding of the new layer.
450 * @param bottom The bottom padding of the new layer.
452 ChildDrawable addLayer(Drawable dr, int[] themeAttrs, int id,
453 int left, int top, int right, int bottom) {
454 final ChildDrawable childDrawable = createLayer(dr);
455 childDrawable.mId = id;
456 childDrawable.mThemeAttrs = themeAttrs;
457 childDrawable.mDrawable.setAutoMirrored(isAutoMirrored());
458 childDrawable.mInsetL = left;
459 childDrawable.mInsetT = top;
460 childDrawable.mInsetR = right;
461 childDrawable.mInsetB = bottom;
463 addLayer(childDrawable);
465 mLayerState.mChildrenChangingConfigurations |= dr.getChangingConfigurations();
466 dr.setCallback(this);
468 return childDrawable;
471 private ChildDrawable createLayer(Drawable dr) {
472 final ChildDrawable layer = new ChildDrawable(mLayerState.mDensity);
473 layer.mDrawable = dr;
478 * Adds a new layer containing the specified {@code drawable} to the end of
479 * the layer list and returns its index.
481 * @param dr The drawable to add as a new layer.
482 * @return The index of the new layer.
484 public int addLayer(Drawable dr) {
485 final ChildDrawable layer = createLayer(dr);
486 final int index = addLayer(layer);
488 refreshChildPadding(index, layer);
493 * Looks for a layer with the given ID and returns its {@link Drawable}.
495 * If multiple layers are found for the given ID, returns the
496 * {@link Drawable} for the matching layer at the highest index.
498 * @param id The layer ID to search for.
499 * @return The {@link Drawable} for the highest-indexed layer that has the
500 * given ID, or null if not found.
502 public Drawable findDrawableByLayerId(int id) {
503 final ChildDrawable[] layers = mLayerState.mChildren;
504 for (int i = mLayerState.mNum - 1; i >= 0; i--) {
505 if (layers[i].mId == id) {
506 return layers[i].mDrawable;
514 * Sets the ID of a layer.
516 * @param index The index of the layer to modify, must be in the range
517 * {@code 0...getNumberOfLayers()-1}.
518 * @param id The id to assign to the layer.
521 * @attr ref android.R.styleable#LayerDrawableItem_id
523 public void setId(int index, int id) {
524 mLayerState.mChildren[index].mId = id;
528 * Returns the ID of the specified layer.
530 * @param index The index of the layer, must be in the range
531 * {@code 0...getNumberOfLayers()-1}.
532 * @return The id of the layer or {@link android.view.View#NO_ID} if the
535 * @see #setId(int, int)
536 * @attr ref android.R.styleable#LayerDrawableItem_id
538 public int getId(int index) {
539 if (index >= mLayerState.mNum) {
540 throw new IndexOutOfBoundsException();
542 return mLayerState.mChildren[index].mId;
546 * Returns the number of layers contained within this layer drawable.
548 * @return The number of layers.
550 public int getNumberOfLayers() {
551 return mLayerState.mNum;
555 * Replaces the {@link Drawable} for the layer with the given id.
557 * @param id The layer ID to search for.
558 * @param drawable The replacement {@link Drawable}.
559 * @return Whether the {@link Drawable} was replaced (could return false if
560 * the id was not found).
562 public boolean setDrawableByLayerId(int id, Drawable drawable) {
563 final int index = findIndexByLayerId(id);
568 setDrawable(index, drawable);
573 * Returns the layer with the specified {@code id}.
575 * If multiple layers have the same ID, returns the layer with the lowest
578 * @param id The ID of the layer to return.
579 * @return The index of the layer with the specified ID.
581 public int findIndexByLayerId(int id) {
582 final ChildDrawable[] layers = mLayerState.mChildren;
583 final int N = mLayerState.mNum;
584 for (int i = 0; i < N; i++) {
585 final ChildDrawable childDrawable = layers[i];
586 if (childDrawable.mId == id) {
595 * Sets the drawable for the layer at the specified index.
597 * @param index The index of the layer to modify, must be in the range
598 * {@code 0...getNumberOfLayers()-1}.
599 * @param drawable The drawable to set for the layer.
601 * @see #getDrawable(int)
602 * @attr ref android.R.styleable#LayerDrawableItem_drawable
604 public void setDrawable(int index, Drawable drawable) {
605 if (index >= mLayerState.mNum) {
606 throw new IndexOutOfBoundsException();
609 final ChildDrawable[] layers = mLayerState.mChildren;
610 final ChildDrawable childDrawable = layers[index];
611 if (childDrawable.mDrawable != null) {
612 if (drawable != null) {
613 final Rect bounds = childDrawable.mDrawable.getBounds();
614 drawable.setBounds(bounds);
617 childDrawable.mDrawable.setCallback(null);
620 if (drawable != null) {
621 drawable.setCallback(this);
624 childDrawable.mDrawable = drawable;
625 mLayerState.invalidateCache();
627 refreshChildPadding(index, childDrawable);
631 * Returns the drawable for the layer at the specified index.
633 * @param index The index of the layer, must be in the range
634 * {@code 0...getNumberOfLayers()-1}.
635 * @return The {@link Drawable} at the specified layer index.
637 * @see #setDrawable(int, Drawable)
638 * @attr ref android.R.styleable#LayerDrawableItem_drawable
640 public Drawable getDrawable(int index) {
641 if (index >= mLayerState.mNum) {
642 throw new IndexOutOfBoundsException();
644 return mLayerState.mChildren[index].mDrawable;
648 * Sets an explicit size for the specified layer.
650 * <strong>Note:</strong> Setting an explicit layer size changes the
651 * default layer gravity behavior. See {@link #setLayerGravity(int, int)}
652 * for more information.
654 * @param index the index of the layer to adjust
655 * @param w width in pixels, or -1 to use the intrinsic width
656 * @param h height in pixels, or -1 to use the intrinsic height
657 * @see #getLayerWidth(int)
658 * @see #getLayerHeight(int)
659 * @attr ref android.R.styleable#LayerDrawableItem_width
660 * @attr ref android.R.styleable#LayerDrawableItem_height
662 public void setLayerSize(int index, int w, int h) {
663 final ChildDrawable childDrawable = mLayerState.mChildren[index];
664 childDrawable.mWidth = w;
665 childDrawable.mHeight = h;
669 * @param index the index of the layer to adjust
670 * @param w width in pixels, or -1 to use the intrinsic width
671 * @attr ref android.R.styleable#LayerDrawableItem_width
673 public void setLayerWidth(int index, int w) {
674 final ChildDrawable childDrawable = mLayerState.mChildren[index];
675 childDrawable.mWidth = w;
679 * @param index the index of the drawable to adjust
680 * @return the explicit width of the layer, or -1 if not specified
681 * @see #setLayerSize(int, int, int)
682 * @attr ref android.R.styleable#LayerDrawableItem_width
684 public int getLayerWidth(int index) {
685 final ChildDrawable childDrawable = mLayerState.mChildren[index];
686 return childDrawable.mWidth;
690 * @param index the index of the layer to adjust
691 * @param h height in pixels, or -1 to use the intrinsic height
692 * @attr ref android.R.styleable#LayerDrawableItem_height
694 public void setLayerHeight(int index, int h) {
695 final ChildDrawable childDrawable = mLayerState.mChildren[index];
696 childDrawable.mHeight = h;
700 * @param index the index of the drawable to adjust
701 * @return the explicit height of the layer, or -1 if not specified
702 * @see #setLayerSize(int, int, int)
703 * @attr ref android.R.styleable#LayerDrawableItem_height
705 public int getLayerHeight(int index) {
706 final ChildDrawable childDrawable = mLayerState.mChildren[index];
707 return childDrawable.mHeight;
711 * Sets the gravity used to position or stretch the specified layer within
712 * its container. Gravity is applied after any layer insets (see
713 * {@link #setLayerInset(int, int, int, int, int)}) or padding (see
714 * {@link #setPaddingMode(int)}).
716 * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default
717 * behavior depends on whether an explicit width or height has been set
718 * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set,
719 * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or
720 * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction
721 * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}.
723 * @param index the index of the drawable to adjust
724 * @param gravity the gravity to set for the layer
726 * @see #getLayerGravity(int)
727 * @attr ref android.R.styleable#LayerDrawableItem_gravity
729 public void setLayerGravity(int index, int gravity) {
730 final ChildDrawable childDrawable = mLayerState.mChildren[index];
731 childDrawable.mGravity = gravity;
735 * @param index the index of the layer
736 * @return the gravity used to position or stretch the specified layer
737 * within its container
739 * @see #setLayerGravity(int, int)
740 * @attr ref android.R.styleable#LayerDrawableItem_gravity
742 public int getLayerGravity(int index) {
743 final ChildDrawable childDrawable = mLayerState.mChildren[index];
744 return childDrawable.mGravity;
748 * Specifies the insets in pixels for the drawable at the specified index.
750 * @param index the index of the drawable to adjust
751 * @param l number of pixels to add to the left bound
752 * @param t number of pixels to add to the top bound
753 * @param r number of pixels to subtract from the right bound
754 * @param b number of pixels to subtract from the bottom bound
756 * @attr ref android.R.styleable#LayerDrawableItem_left
757 * @attr ref android.R.styleable#LayerDrawableItem_top
758 * @attr ref android.R.styleable#LayerDrawableItem_right
759 * @attr ref android.R.styleable#LayerDrawableItem_bottom
761 public void setLayerInset(int index, int l, int t, int r, int b) {
762 setLayerInsetInternal(index, l, t, r, b, INSET_UNDEFINED, INSET_UNDEFINED);
766 * Specifies the relative insets in pixels for the drawable at the
769 * @param index the index of the layer to adjust
770 * @param s number of pixels to inset from the start bound
771 * @param t number of pixels to inset from the top bound
772 * @param e number of pixels to inset from the end bound
773 * @param b number of pixels to inset from the bottom bound
775 * @attr ref android.R.styleable#LayerDrawableItem_start
776 * @attr ref android.R.styleable#LayerDrawableItem_top
777 * @attr ref android.R.styleable#LayerDrawableItem_end
778 * @attr ref android.R.styleable#LayerDrawableItem_bottom
780 public void setLayerInsetRelative(int index, int s, int t, int e, int b) {
781 setLayerInsetInternal(index, 0, t, 0, b, s, e);
785 * @param index the index of the layer to adjust
786 * @param l number of pixels to inset from the left bound
787 * @attr ref android.R.styleable#LayerDrawableItem_left
789 public void setLayerInsetLeft(int index, int l) {
790 final ChildDrawable childDrawable = mLayerState.mChildren[index];
791 childDrawable.mInsetL = l;
795 * @param index the index of the layer
796 * @return number of pixels to inset from the left bound
797 * @attr ref android.R.styleable#LayerDrawableItem_left
799 public int getLayerInsetLeft(int index) {
800 final ChildDrawable childDrawable = mLayerState.mChildren[index];
801 return childDrawable.mInsetL;
805 * @param index the index of the layer to adjust
806 * @param r number of pixels to inset from the right bound
807 * @attr ref android.R.styleable#LayerDrawableItem_right
809 public void setLayerInsetRight(int index, int r) {
810 final ChildDrawable childDrawable = mLayerState.mChildren[index];
811 childDrawable.mInsetR = r;
815 * @param index the index of the layer
816 * @return number of pixels to inset from the right bound
817 * @attr ref android.R.styleable#LayerDrawableItem_right
819 public int getLayerInsetRight(int index) {
820 final ChildDrawable childDrawable = mLayerState.mChildren[index];
821 return childDrawable.mInsetR;
825 * @param index the index of the layer to adjust
826 * @param t number of pixels to inset from the top bound
827 * @attr ref android.R.styleable#LayerDrawableItem_top
829 public void setLayerInsetTop(int index, int t) {
830 final ChildDrawable childDrawable = mLayerState.mChildren[index];
831 childDrawable.mInsetT = t;
835 * @param index the index of the layer
836 * @return number of pixels to inset from the top bound
837 * @attr ref android.R.styleable#LayerDrawableItem_top
839 public int getLayerInsetTop(int index) {
840 final ChildDrawable childDrawable = mLayerState.mChildren[index];
841 return childDrawable.mInsetT;
845 * @param index the index of the layer to adjust
846 * @param b number of pixels to inset from the bottom bound
847 * @attr ref android.R.styleable#LayerDrawableItem_bottom
849 public void setLayerInsetBottom(int index, int b) {
850 final ChildDrawable childDrawable = mLayerState.mChildren[index];
851 childDrawable.mInsetB = b;
855 * @param index the index of the layer
856 * @return number of pixels to inset from the bottom bound
857 * @attr ref android.R.styleable#LayerDrawableItem_bottom
859 public int getLayerInsetBottom(int index) {
860 final ChildDrawable childDrawable = mLayerState.mChildren[index];
861 return childDrawable.mInsetB;
865 * @param index the index of the layer to adjust
866 * @param s number of pixels to inset from the start bound
867 * @attr ref android.R.styleable#LayerDrawableItem_start
869 public void setLayerInsetStart(int index, int s) {
870 final ChildDrawable childDrawable = mLayerState.mChildren[index];
871 childDrawable.mInsetS = s;
875 * @param index the index of the layer
876 * @return the number of pixels to inset from the start bound, or
877 * {@link #INSET_UNDEFINED} if not specified
878 * @attr ref android.R.styleable#LayerDrawableItem_start
880 public int getLayerInsetStart(int index) {
881 final ChildDrawable childDrawable = mLayerState.mChildren[index];
882 return childDrawable.mInsetS;
886 * @param index the index of the layer to adjust
887 * @param e number of pixels to inset from the end bound, or
888 * {@link #INSET_UNDEFINED} if not specified
889 * @attr ref android.R.styleable#LayerDrawableItem_end
891 public void setLayerInsetEnd(int index, int e) {
892 final ChildDrawable childDrawable = mLayerState.mChildren[index];
893 childDrawable.mInsetE = e;
897 * @param index the index of the layer
898 * @return number of pixels to inset from the end bound
899 * @attr ref android.R.styleable#LayerDrawableItem_end
901 public int getLayerInsetEnd(int index) {
902 final ChildDrawable childDrawable = mLayerState.mChildren[index];
903 return childDrawable.mInsetE;
906 private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) {
907 final ChildDrawable childDrawable = mLayerState.mChildren[index];
908 childDrawable.mInsetL = l;
909 childDrawable.mInsetT = t;
910 childDrawable.mInsetR = r;
911 childDrawable.mInsetB = b;
912 childDrawable.mInsetS = s;
913 childDrawable.mInsetE = e;
917 * Specifies how layer padding should affect the bounds of subsequent
918 * layers. The default value is {@link #PADDING_MODE_NEST}.
920 * @param mode padding mode, one of:
922 * <li>{@link #PADDING_MODE_NEST} to nest each layer inside the
923 * padding of the previous layer
924 * <li>{@link #PADDING_MODE_STACK} to stack each layer directly
925 * atop the previous layer
928 * @see #getPaddingMode()
929 * @attr ref android.R.styleable#LayerDrawable_paddingMode
931 public void setPaddingMode(int mode) {
932 if (mLayerState.mPaddingMode != mode) {
933 mLayerState.mPaddingMode = mode;
938 * @return the current padding mode
940 * @see #setPaddingMode(int)
941 * @attr ref android.R.styleable#LayerDrawable_paddingMode
943 public int getPaddingMode() {
944 return mLayerState.mPaddingMode;
948 public void invalidateDrawable(@NonNull Drawable who) {
953 public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
954 scheduleSelf(what, when);
958 public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
959 unscheduleSelf(what);
963 public void draw(Canvas canvas) {
964 final ChildDrawable[] array = mLayerState.mChildren;
965 final int N = mLayerState.mNum;
966 for (int i = 0; i < N; i++) {
967 final Drawable dr = array[i].mDrawable;
975 public @Config int getChangingConfigurations() {
976 return super.getChangingConfigurations() | mLayerState.getChangingConfigurations();
980 public boolean getPadding(Rect padding) {
981 final LayerState layerState = mLayerState;
982 if (layerState.mPaddingMode == PADDING_MODE_NEST) {
983 computeNestedPadding(padding);
985 computeStackedPadding(padding);
988 final int paddingT = layerState.mPaddingTop;
989 final int paddingB = layerState.mPaddingBottom;
991 // Resolve padding for RTL. Relative padding overrides absolute
993 final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
994 final int paddingRtlL = isLayoutRtl ? layerState.mPaddingEnd : layerState.mPaddingStart;
995 final int paddingRtlR = isLayoutRtl ? layerState.mPaddingStart : layerState.mPaddingEnd;
996 final int paddingL = paddingRtlL >= 0 ? paddingRtlL : layerState.mPaddingLeft;
997 final int paddingR = paddingRtlR >= 0 ? paddingRtlR : layerState.mPaddingRight;
999 // If padding was explicitly specified (e.g. not -1) then override the
1000 // computed padding in that dimension.
1001 if (paddingL >= 0) {
1002 padding.left = paddingL;
1005 if (paddingT >= 0) {
1006 padding.top = paddingT;
1009 if (paddingR >= 0) {
1010 padding.right = paddingR;
1013 if (paddingB >= 0) {
1014 padding.bottom = paddingB;
1017 return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
1021 * Sets the absolute padding.
1023 * If padding in a dimension is specified as {@code -1}, the resolved
1024 * padding will use the value computed according to the padding mode (see
1025 * {@link #setPaddingMode(int)}).
1027 * Calling this method clears any relative padding values previously set
1028 * using {@link #setPaddingRelative(int, int, int, int)}.
1030 * @param left the left padding in pixels, or -1 to use computed padding
1031 * @param top the top padding in pixels, or -1 to use computed padding
1032 * @param right the right padding in pixels, or -1 to use computed padding
1033 * @param bottom the bottom padding in pixels, or -1 to use computed
1035 * @attr ref android.R.styleable#LayerDrawable_paddingLeft
1036 * @attr ref android.R.styleable#LayerDrawable_paddingTop
1037 * @attr ref android.R.styleable#LayerDrawable_paddingRight
1038 * @attr ref android.R.styleable#LayerDrawable_paddingBottom
1039 * @see #setPaddingRelative(int, int, int, int)
1041 public void setPadding(int left, int top, int right, int bottom) {
1042 final LayerState layerState = mLayerState;
1043 layerState.mPaddingLeft = left;
1044 layerState.mPaddingTop = top;
1045 layerState.mPaddingRight = right;
1046 layerState.mPaddingBottom = bottom;
1048 // Clear relative padding values.
1049 layerState.mPaddingStart = -1;
1050 layerState.mPaddingEnd = -1;
1054 * Sets the relative padding.
1056 * If padding in a dimension is specified as {@code -1}, the resolved
1057 * padding will use the value computed according to the padding mode (see
1058 * {@link #setPaddingMode(int)}).
1060 * Calling this method clears any absolute padding values previously set
1061 * using {@link #setPadding(int, int, int, int)}.
1063 * @param start the start padding in pixels, or -1 to use computed padding
1064 * @param top the top padding in pixels, or -1 to use computed padding
1065 * @param end the end padding in pixels, or -1 to use computed padding
1066 * @param bottom the bottom padding in pixels, or -1 to use computed
1068 * @attr ref android.R.styleable#LayerDrawable_paddingStart
1069 * @attr ref android.R.styleable#LayerDrawable_paddingTop
1070 * @attr ref android.R.styleable#LayerDrawable_paddingEnd
1071 * @attr ref android.R.styleable#LayerDrawable_paddingBottom
1072 * @see #setPadding(int, int, int, int)
1074 public void setPaddingRelative(int start, int top, int end, int bottom) {
1075 final LayerState layerState = mLayerState;
1076 layerState.mPaddingStart = start;
1077 layerState.mPaddingTop = top;
1078 layerState.mPaddingEnd = end;
1079 layerState.mPaddingBottom = bottom;
1081 // Clear absolute padding values.
1082 layerState.mPaddingLeft = -1;
1083 layerState.mPaddingRight = -1;
1087 * Returns the left padding in pixels.
1089 * A return value of {@code -1} means there is no explicit padding set for
1090 * this dimension. As a result, the value for this dimension returned by
1091 * {@link #getPadding(Rect)} will be computed from the child layers
1092 * according to the padding mode (see {@link #getPaddingMode()}.
1094 * @return the left padding in pixels, or -1 if not explicitly specified
1095 * @see #setPadding(int, int, int, int)
1096 * @see #getPadding(Rect)
1098 public int getLeftPadding() {
1099 return mLayerState.mPaddingLeft;
1103 * Returns the right padding in pixels.
1105 * A return value of {@code -1} means there is no explicit padding set for
1106 * this dimension. As a result, the value for this dimension returned by
1107 * {@link #getPadding(Rect)} will be computed from the child layers
1108 * according to the padding mode (see {@link #getPaddingMode()}.
1110 * @return the right padding in pixels, or -1 if not explicitly specified
1111 * @see #setPadding(int, int, int, int)
1112 * @see #getPadding(Rect)
1114 public int getRightPadding() {
1115 return mLayerState.mPaddingRight;
1119 * Returns the start padding in pixels.
1121 * A return value of {@code -1} means there is no explicit padding set for
1122 * this dimension. As a result, the value for this dimension returned by
1123 * {@link #getPadding(Rect)} will be computed from the child layers
1124 * according to the padding mode (see {@link #getPaddingMode()}.
1126 * @return the start padding in pixels, or -1 if not explicitly specified
1127 * @see #setPaddingRelative(int, int, int, int)
1128 * @see #getPadding(Rect)
1130 public int getStartPadding() {
1131 return mLayerState.mPaddingStart;
1135 * Returns the end padding in pixels.
1137 * A return value of {@code -1} means there is no explicit padding set for
1138 * this dimension. As a result, the value for this dimension returned by
1139 * {@link #getPadding(Rect)} will be computed from the child layers
1140 * according to the padding mode (see {@link #getPaddingMode()}.
1142 * @return the end padding in pixels, or -1 if not explicitly specified
1143 * @see #setPaddingRelative(int, int, int, int)
1144 * @see #getPadding(Rect)
1146 public int getEndPadding() {
1147 return mLayerState.mPaddingEnd;
1151 * Returns the top padding in pixels.
1153 * A return value of {@code -1} means there is no explicit padding set for
1154 * this dimension. As a result, the value for this dimension returned by
1155 * {@link #getPadding(Rect)} will be computed from the child layers
1156 * according to the padding mode (see {@link #getPaddingMode()}.
1158 * @return the top padding in pixels, or -1 if not explicitly specified
1159 * @see #setPadding(int, int, int, int)
1160 * @see #setPaddingRelative(int, int, int, int)
1161 * @see #getPadding(Rect)
1163 public int getTopPadding() {
1164 return mLayerState.mPaddingTop;
1168 * Returns the bottom padding in pixels.
1170 * A return value of {@code -1} means there is no explicit padding set for
1171 * this dimension. As a result, the value for this dimension returned by
1172 * {@link #getPadding(Rect)} will be computed from the child layers
1173 * according to the padding mode (see {@link #getPaddingMode()}.
1175 * @return the bottom padding in pixels, or -1 if not explicitly specified
1176 * @see #setPadding(int, int, int, int)
1177 * @see #setPaddingRelative(int, int, int, int)
1178 * @see #getPadding(Rect)
1180 public int getBottomPadding() {
1181 return mLayerState.mPaddingBottom;
1184 private void computeNestedPadding(Rect padding) {
1190 // Add all the padding.
1191 final ChildDrawable[] array = mLayerState.mChildren;
1192 final int N = mLayerState.mNum;
1193 for (int i = 0; i < N; i++) {
1194 refreshChildPadding(i, array[i]);
1196 padding.left += mPaddingL[i];
1197 padding.top += mPaddingT[i];
1198 padding.right += mPaddingR[i];
1199 padding.bottom += mPaddingB[i];
1203 private void computeStackedPadding(Rect padding) {
1209 // Take the max padding.
1210 final ChildDrawable[] array = mLayerState.mChildren;
1211 final int N = mLayerState.mNum;
1212 for (int i = 0; i < N; i++) {
1213 refreshChildPadding(i, array[i]);
1215 padding.left = Math.max(padding.left, mPaddingL[i]);
1216 padding.top = Math.max(padding.top, mPaddingT[i]);
1217 padding.right = Math.max(padding.right, mPaddingR[i]);
1218 padding.bottom = Math.max(padding.bottom, mPaddingB[i]);
1223 * Populates <code>outline</code> with the first available (non-empty) layer outline.
1225 * @param outline Outline in which to place the first available layer outline
1228 public void getOutline(@NonNull Outline outline) {
1229 final ChildDrawable[] array = mLayerState.mChildren;
1230 final int N = mLayerState.mNum;
1231 for (int i = 0; i < N; i++) {
1232 final Drawable dr = array[i].mDrawable;
1234 dr.getOutline(outline);
1235 if (!outline.isEmpty()) {
1243 public void setHotspot(float x, float y) {
1244 final ChildDrawable[] array = mLayerState.mChildren;
1245 final int N = mLayerState.mNum;
1246 for (int i = 0; i < N; i++) {
1247 final Drawable dr = array[i].mDrawable;
1249 dr.setHotspot(x, y);
1255 public void setHotspotBounds(int left, int top, int right, int bottom) {
1256 final ChildDrawable[] array = mLayerState.mChildren;
1257 final int N = mLayerState.mNum;
1258 for (int i = 0; i < N; i++) {
1259 final Drawable dr = array[i].mDrawable;
1261 dr.setHotspotBounds(left, top, right, bottom);
1265 if (mHotspotBounds == null) {
1266 mHotspotBounds = new Rect(left, top, right, bottom);
1268 mHotspotBounds.set(left, top, right, bottom);
1273 public void getHotspotBounds(Rect outRect) {
1274 if (mHotspotBounds != null) {
1275 outRect.set(mHotspotBounds);
1277 super.getHotspotBounds(outRect);
1282 public boolean setVisible(boolean visible, boolean restart) {
1283 final boolean changed = super.setVisible(visible, restart);
1284 final ChildDrawable[] array = mLayerState.mChildren;
1285 final int N = mLayerState.mNum;
1286 for (int i = 0; i < N; i++) {
1287 final Drawable dr = array[i].mDrawable;
1289 dr.setVisible(visible, restart);
1297 public void setDither(boolean dither) {
1298 final ChildDrawable[] array = mLayerState.mChildren;
1299 final int N = mLayerState.mNum;
1300 for (int i = 0; i < N; i++) {
1301 final Drawable dr = array[i].mDrawable;
1303 dr.setDither(dither);
1309 public void setAlpha(int alpha) {
1310 final ChildDrawable[] array = mLayerState.mChildren;
1311 final int N = mLayerState.mNum;
1312 for (int i = 0; i < N; i++) {
1313 final Drawable dr = array[i].mDrawable;
1321 public int getAlpha() {
1322 final Drawable dr = getFirstNonNullDrawable();
1324 return dr.getAlpha();
1326 return super.getAlpha();
1331 public void setColorFilter(ColorFilter colorFilter) {
1332 final ChildDrawable[] array = mLayerState.mChildren;
1333 final int N = mLayerState.mNum;
1334 for (int i = 0; i < N; i++) {
1335 final Drawable dr = array[i].mDrawable;
1337 dr.setColorFilter(colorFilter);
1343 public void setTintList(ColorStateList tint) {
1344 final ChildDrawable[] array = mLayerState.mChildren;
1345 final int N = mLayerState.mNum;
1346 for (int i = 0; i < N; i++) {
1347 final Drawable dr = array[i].mDrawable;
1349 dr.setTintList(tint);
1355 public void setTintMode(Mode tintMode) {
1356 final ChildDrawable[] array = mLayerState.mChildren;
1357 final int N = mLayerState.mNum;
1358 for (int i = 0; i < N; i++) {
1359 final Drawable dr = array[i].mDrawable;
1361 dr.setTintMode(tintMode);
1366 private Drawable getFirstNonNullDrawable() {
1367 final ChildDrawable[] array = mLayerState.mChildren;
1368 final int N = mLayerState.mNum;
1369 for (int i = 0; i < N; i++) {
1370 final Drawable dr = array[i].mDrawable;
1379 * Sets the opacity of this drawable directly instead of collecting the
1380 * states from the layers.
1382 * @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN
1383 * PixelFormat.UNKNOWN} for the default behavior
1384 * @see PixelFormat#UNKNOWN
1385 * @see PixelFormat#TRANSLUCENT
1386 * @see PixelFormat#TRANSPARENT
1387 * @see PixelFormat#OPAQUE
1389 public void setOpacity(int opacity) {
1390 mLayerState.mOpacityOverride = opacity;
1394 public int getOpacity() {
1395 if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
1396 return mLayerState.mOpacityOverride;
1398 return mLayerState.getOpacity();
1402 public void setAutoMirrored(boolean mirrored) {
1403 mLayerState.mAutoMirrored = mirrored;
1405 final ChildDrawable[] array = mLayerState.mChildren;
1406 final int N = mLayerState.mNum;
1407 for (int i = 0; i < N; i++) {
1408 final Drawable dr = array[i].mDrawable;
1410 dr.setAutoMirrored(mirrored);
1416 public boolean isAutoMirrored() {
1417 return mLayerState.mAutoMirrored;
1421 public void jumpToCurrentState() {
1422 final ChildDrawable[] array = mLayerState.mChildren;
1423 final int N = mLayerState.mNum;
1424 for (int i = 0; i < N; i++) {
1425 final Drawable dr = array[i].mDrawable;
1427 dr.jumpToCurrentState();
1433 public boolean isStateful() {
1434 return mLayerState.isStateful();
1438 protected boolean onStateChange(int[] state) {
1439 boolean changed = false;
1441 final ChildDrawable[] array = mLayerState.mChildren;
1442 final int N = mLayerState.mNum;
1443 for (int i = 0; i < N; i++) {
1444 final Drawable dr = array[i].mDrawable;
1445 if (dr != null && dr.isStateful() && dr.setState(state)) {
1446 refreshChildPadding(i, array[i]);
1452 updateLayerBounds(getBounds());
1459 protected boolean onLevelChange(int level) {
1460 boolean changed = false;
1462 final ChildDrawable[] array = mLayerState.mChildren;
1463 final int N = mLayerState.mNum;
1464 for (int i = 0; i < N; i++) {
1465 final Drawable dr = array[i].mDrawable;
1466 if (dr != null && dr.setLevel(level)) {
1467 refreshChildPadding(i, array[i]);
1473 updateLayerBounds(getBounds());
1480 protected void onBoundsChange(Rect bounds) {
1481 updateLayerBounds(bounds);
1484 private void updateLayerBounds(Rect bounds) {
1490 final Rect outRect = mTmpOutRect;
1491 final int layoutDirection = getLayoutDirection();
1492 final boolean isLayoutRtl = layoutDirection == LayoutDirection.RTL;
1493 final boolean isPaddingNested = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1494 final ChildDrawable[] array = mLayerState.mChildren;
1496 for (int i = 0, count = mLayerState.mNum; i < count; i++) {
1497 final ChildDrawable r = array[i];
1498 final Drawable d = r.mDrawable;
1503 final int insetT = r.mInsetT;
1504 final int insetB = r.mInsetB;
1506 // Resolve insets for RTL. Relative insets override absolute
1508 final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS;
1509 final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE;
1510 final int insetL = insetRtlL == INSET_UNDEFINED ? r.mInsetL : insetRtlL;
1511 final int insetR = insetRtlR == INSET_UNDEFINED ? r.mInsetR : insetRtlR;
1513 // Establish containing region based on aggregate padding and
1514 // requested insets for the current layer.
1515 final Rect container = mTmpContainer;
1516 container.set(bounds.left + insetL + paddingL, bounds.top + insetT + paddingT,
1517 bounds.right - insetR - paddingR, bounds.bottom - insetB - paddingB);
1519 // Compute a reasonable default gravity based on the intrinsic and
1520 // explicit dimensions, if specified.
1521 final int intrinsicW = d.getIntrinsicWidth();
1522 final int intrinsicH = d.getIntrinsicHeight();
1523 final int layerW = r.mWidth;
1524 final int layerH = r.mHeight;
1525 final int gravity = resolveGravity(r.mGravity, layerW, layerH, intrinsicW, intrinsicH);
1527 // Explicit dimensions override intrinsic dimensions.
1528 final int resolvedW = layerW < 0 ? intrinsicW : layerW;
1529 final int resolvedH = layerH < 0 ? intrinsicH : layerH;
1530 Gravity.apply(gravity, resolvedW, resolvedH, container, outRect, layoutDirection);
1531 d.setBounds(outRect);
1533 if (isPaddingNested) {
1534 paddingL += mPaddingL[i];
1535 paddingR += mPaddingR[i];
1536 paddingT += mPaddingT[i];
1537 paddingB += mPaddingB[i];
1543 * Resolves layer gravity given explicit gravity and dimensions.
1545 * If the client hasn't specified a gravity but has specified an explicit
1546 * dimension, defaults to START or TOP. Otherwise, defaults to FILL to
1547 * preserve legacy behavior.
1549 * @param gravity layer gravity
1550 * @param width width of the layer if set, -1 otherwise
1551 * @param height height of the layer if set, -1 otherwise
1552 * @return the default gravity for the layer
1554 private static int resolveGravity(int gravity, int width, int height,
1555 int intrinsicWidth, int intrinsicHeight) {
1556 if (!Gravity.isHorizontal(gravity)) {
1558 gravity |= Gravity.FILL_HORIZONTAL;
1560 gravity |= Gravity.START;
1564 if (!Gravity.isVertical(gravity)) {
1566 gravity |= Gravity.FILL_VERTICAL;
1568 gravity |= Gravity.TOP;
1572 // If a dimension if not specified, either implicitly or explicitly,
1573 // force FILL for that dimension's gravity. This ensures that colors
1574 // are handled correctly and ensures backward compatibility.
1575 if (width < 0 && intrinsicWidth < 0) {
1576 gravity |= Gravity.FILL_HORIZONTAL;
1579 if (height < 0 && intrinsicHeight < 0) {
1580 gravity |= Gravity.FILL_VERTICAL;
1587 public int getIntrinsicWidth() {
1592 final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1593 final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
1594 final ChildDrawable[] array = mLayerState.mChildren;
1595 final int N = mLayerState.mNum;
1596 for (int i = 0; i < N; i++) {
1597 final ChildDrawable r = array[i];
1598 if (r.mDrawable == null) {
1602 // Take the resolved layout direction into account. If start / end
1603 // padding are defined, they will be resolved (hence overriding) to
1604 // left / right or right / left depending on the resolved layout
1605 // direction. If start / end padding are not defined, use the
1606 // left / right ones.
1607 final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS;
1608 final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE;
1609 final int insetL = insetRtlL == INSET_UNDEFINED ? r.mInsetL : insetRtlL;
1610 final int insetR = insetRtlR == INSET_UNDEFINED ? r.mInsetR : insetRtlR;
1612 // Don't apply padding and insets for children that don't have
1613 // an intrinsic dimension.
1614 final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth;
1615 final int w = minWidth < 0 ? -1 : minWidth + insetL + insetR + padL + padR;
1621 padL += mPaddingL[i];
1622 padR += mPaddingR[i];
1630 public int getIntrinsicHeight() {
1635 final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1636 final ChildDrawable[] array = mLayerState.mChildren;
1637 final int N = mLayerState.mNum;
1638 for (int i = 0; i < N; i++) {
1639 final ChildDrawable r = array[i];
1640 if (r.mDrawable == null) {
1644 // Don't apply padding and insets for children that don't have
1645 // an intrinsic dimension.
1646 final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight;
1647 final int h = minHeight < 0 ? -1 : minHeight + r.mInsetT + r.mInsetB + padT + padB;
1653 padT += mPaddingT[i];
1654 padB += mPaddingB[i];
1662 * Refreshes the cached padding values for the specified child.
1664 * @return true if the child's padding has changed
1666 private boolean refreshChildPadding(int i, ChildDrawable r) {
1667 if (r.mDrawable != null) {
1668 final Rect rect = mTmpRect;
1669 r.mDrawable.getPadding(rect);
1670 if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i]
1671 || rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
1672 mPaddingL[i] = rect.left;
1673 mPaddingT[i] = rect.top;
1674 mPaddingR[i] = rect.right;
1675 mPaddingB[i] = rect.bottom;
1683 * Ensures the child padding caches are large enough.
1685 void ensurePadding() {
1686 final int N = mLayerState.mNum;
1687 if (mPaddingL != null && mPaddingL.length >= N) {
1691 mPaddingL = new int[N];
1692 mPaddingT = new int[N];
1693 mPaddingR = new int[N];
1694 mPaddingB = new int[N];
1697 void refreshPadding() {
1698 final int N = mLayerState.mNum;
1699 final ChildDrawable[] array = mLayerState.mChildren;
1700 for (int i = 0; i < N; i++) {
1701 refreshChildPadding(i, array[i]);
1706 public ConstantState getConstantState() {
1707 if (mLayerState.canConstantState()) {
1708 mLayerState.mChangingConfigurations = getChangingConfigurations();
1715 public Drawable mutate() {
1716 if (!mMutated && super.mutate() == this) {
1717 mLayerState = createConstantState(mLayerState, null);
1718 final ChildDrawable[] array = mLayerState.mChildren;
1719 final int N = mLayerState.mNum;
1720 for (int i = 0; i < N; i++) {
1721 final Drawable dr = array[i].mDrawable;
1734 public void clearMutated() {
1735 super.clearMutated();
1737 final ChildDrawable[] array = mLayerState.mChildren;
1738 final int N = mLayerState.mNum;
1739 for (int i = 0; i < N; i++) {
1740 final Drawable dr = array[i].mDrawable;
1749 public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) {
1750 boolean changed = false;
1752 final ChildDrawable[] array = mLayerState.mChildren;
1753 final int N = mLayerState.mNum;
1754 for (int i = 0; i < N; i++) {
1755 final Drawable dr = array[i].mDrawable;
1757 changed |= dr.setLayoutDirection(layoutDirection);
1761 updateLayerBounds(getBounds());
1765 static class ChildDrawable {
1766 public Drawable mDrawable;
1767 public int[] mThemeAttrs;
1768 public int mDensity = DisplayMetrics.DENSITY_DEFAULT;
1769 public int mInsetL, mInsetT, mInsetR, mInsetB;
1770 public int mInsetS = INSET_UNDEFINED;
1771 public int mInsetE = INSET_UNDEFINED;
1772 public int mWidth = -1;
1773 public int mHeight = -1;
1774 public int mGravity = Gravity.NO_GRAVITY;
1775 public int mId = View.NO_ID;
1777 ChildDrawable(int density) {
1781 ChildDrawable(@NonNull ChildDrawable orig, @NonNull LayerDrawable owner,
1782 @Nullable Resources res) {
1783 final Drawable dr = orig.mDrawable;
1784 final Drawable clone;
1786 final ConstantState cs = dr.getConstantState();
1789 } else if (res != null) {
1790 clone = cs.newDrawable(res);
1792 clone = cs.newDrawable();
1794 clone.setCallback(owner);
1795 clone.setLayoutDirection(dr.getLayoutDirection());
1796 clone.setBounds(dr.getBounds());
1797 clone.setLevel(dr.getLevel());
1803 mThemeAttrs = orig.mThemeAttrs;
1804 mInsetL = orig.mInsetL;
1805 mInsetT = orig.mInsetT;
1806 mInsetR = orig.mInsetR;
1807 mInsetB = orig.mInsetB;
1808 mInsetS = orig.mInsetS;
1809 mInsetE = orig.mInsetE;
1810 mWidth = orig.mWidth;
1811 mHeight = orig.mHeight;
1812 mGravity = orig.mGravity;
1815 mDensity = Drawable.resolveDensity(res, orig.mDensity);
1816 if (orig.mDensity != mDensity) {
1817 applyDensityScaling(orig.mDensity, mDensity);
1821 public boolean canApplyTheme() {
1822 return mThemeAttrs != null
1823 || (mDrawable != null && mDrawable.canApplyTheme());
1826 public final void setDensity(int targetDensity) {
1827 if (mDensity != targetDensity) {
1828 final int sourceDensity = mDensity;
1829 mDensity = targetDensity;
1831 applyDensityScaling(sourceDensity, targetDensity);
1835 private void applyDensityScaling(int sourceDensity, int targetDensity) {
1836 mInsetL = Drawable.scaleFromDensity(mInsetL, sourceDensity, targetDensity, false);
1837 mInsetT = Drawable.scaleFromDensity(mInsetT, sourceDensity, targetDensity, false);
1838 mInsetR = Drawable.scaleFromDensity(mInsetR, sourceDensity, targetDensity, false);
1839 mInsetB = Drawable.scaleFromDensity(mInsetB, sourceDensity, targetDensity, false);
1840 if (mInsetS != INSET_UNDEFINED) {
1841 mInsetS = Drawable.scaleFromDensity(mInsetS, sourceDensity, targetDensity, false);
1843 if (mInsetE != INSET_UNDEFINED) {
1844 mInsetE = Drawable.scaleFromDensity(mInsetE, sourceDensity, targetDensity, false);
1847 mWidth = Drawable.scaleFromDensity(mWidth, sourceDensity, targetDensity, true);
1850 mHeight = Drawable.scaleFromDensity(mHeight, sourceDensity, targetDensity, true);
1855 static class LayerState extends ConstantState {
1856 private int[] mThemeAttrs;
1859 ChildDrawable[] mChildren;
1863 // These values all correspond to mDensity.
1864 int mPaddingTop = -1;
1865 int mPaddingBottom = -1;
1866 int mPaddingLeft = -1;
1867 int mPaddingRight = -1;
1868 int mPaddingStart = -1;
1869 int mPaddingEnd = -1;
1870 int mOpacityOverride = PixelFormat.UNKNOWN;
1872 @Config int mChangingConfigurations;
1873 @Config int mChildrenChangingConfigurations;
1875 private boolean mHaveOpacity;
1876 private int mOpacity;
1878 private boolean mHaveIsStateful;
1879 private boolean mIsStateful;
1881 private boolean mAutoMirrored = false;
1883 private int mPaddingMode = PADDING_MODE_NEST;
1885 LayerState(@Nullable LayerState orig, @NonNull LayerDrawable owner,
1886 @Nullable Resources res) {
1887 mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0);
1890 final ChildDrawable[] origChildDrawable = orig.mChildren;
1891 final int N = orig.mNum;
1894 mChildren = new ChildDrawable[N];
1896 mChangingConfigurations = orig.mChangingConfigurations;
1897 mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
1899 for (int i = 0; i < N; i++) {
1900 final ChildDrawable or = origChildDrawable[i];
1901 mChildren[i] = new ChildDrawable(or, owner, res);
1904 mHaveOpacity = orig.mHaveOpacity;
1905 mOpacity = orig.mOpacity;
1906 mHaveIsStateful = orig.mHaveIsStateful;
1907 mIsStateful = orig.mIsStateful;
1908 mAutoMirrored = orig.mAutoMirrored;
1909 mPaddingMode = orig.mPaddingMode;
1910 mThemeAttrs = orig.mThemeAttrs;
1911 mPaddingTop = orig.mPaddingTop;
1912 mPaddingBottom = orig.mPaddingBottom;
1913 mPaddingLeft = orig.mPaddingLeft;
1914 mPaddingRight = orig.mPaddingRight;
1915 mPaddingStart = orig.mPaddingStart;
1916 mPaddingEnd = orig.mPaddingEnd;
1917 mOpacityOverride = orig.mOpacityOverride;
1919 if (orig.mDensity != mDensity) {
1920 applyDensityScaling(orig.mDensity, mDensity);
1928 public final void setDensity(int targetDensity) {
1929 if (mDensity != targetDensity) {
1930 final int sourceDensity = mDensity;
1931 mDensity = targetDensity;
1933 onDensityChanged(sourceDensity, targetDensity);
1937 protected void onDensityChanged(int sourceDensity, int targetDensity) {
1938 applyDensityScaling(sourceDensity, targetDensity);
1941 private void applyDensityScaling(int sourceDensity, int targetDensity) {
1942 if (mPaddingLeft > 0) {
1943 mPaddingLeft = Drawable.scaleFromDensity(
1944 mPaddingLeft, sourceDensity, targetDensity, false);
1946 if (mPaddingTop > 0) {
1947 mPaddingTop = Drawable.scaleFromDensity(
1948 mPaddingTop, sourceDensity, targetDensity, false);
1950 if (mPaddingRight > 0) {
1951 mPaddingRight = Drawable.scaleFromDensity(
1952 mPaddingRight, sourceDensity, targetDensity, false);
1954 if (mPaddingBottom > 0) {
1955 mPaddingBottom = Drawable.scaleFromDensity(
1956 mPaddingBottom, sourceDensity, targetDensity, false);
1958 if (mPaddingStart > 0) {
1959 mPaddingStart = Drawable.scaleFromDensity(
1960 mPaddingStart, sourceDensity, targetDensity, false);
1962 if (mPaddingEnd > 0) {
1963 mPaddingEnd = Drawable.scaleFromDensity(
1964 mPaddingEnd, sourceDensity, targetDensity, false);
1969 public boolean canApplyTheme() {
1970 if (mThemeAttrs != null || super.canApplyTheme()) {
1974 final ChildDrawable[] array = mChildren;
1976 for (int i = 0; i < N; i++) {
1977 final ChildDrawable layer = array[i];
1978 if (layer.canApplyTheme()) {
1987 public Drawable newDrawable() {
1988 return new LayerDrawable(this, null);
1992 public Drawable newDrawable(@Nullable Resources res) {
1993 return new LayerDrawable(this, res);
1997 public @Config int getChangingConfigurations() {
1998 return mChangingConfigurations
1999 | mChildrenChangingConfigurations;
2002 public final int getOpacity() {
2007 final ChildDrawable[] array = mChildren;
2010 // Seek to the first non-null drawable.
2011 int firstIndex = -1;
2012 for (int i = 0; i < N; i++) {
2013 if (array[i].mDrawable != null) {
2020 if (firstIndex >= 0) {
2021 op = array[firstIndex].mDrawable.getOpacity();
2023 op = PixelFormat.TRANSPARENT;
2026 // Merge all remaining non-null drawables.
2027 for (int i = firstIndex + 1; i < N; i++) {
2028 final Drawable dr = array[i].mDrawable;
2030 op = Drawable.resolveOpacity(op, dr.getOpacity());
2035 mHaveOpacity = true;
2039 public final boolean isStateful() {
2040 if (mHaveIsStateful) {
2044 final ChildDrawable[] array = mChildren;
2046 boolean isStateful = false;
2047 for (int i = 0; i < N; i++) {
2048 final Drawable dr = array[i].mDrawable;
2049 if (dr != null && dr.isStateful()) {
2055 mIsStateful = isStateful;
2056 mHaveIsStateful = true;
2060 public final boolean canConstantState() {
2061 final ChildDrawable[] array = mChildren;
2063 for (int i = 0; i < N; i++) {
2064 final Drawable dr = array[i].mDrawable;
2065 if (dr != null && dr.getConstantState() == null) {
2070 // Don't cache the result, this method is not called very often.
2074 public void invalidateCache() {
2075 mHaveOpacity = false;
2076 mHaveIsStateful = false;
2080 public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
2081 final ChildDrawable[] array = mChildren;
2084 for (int i = 0; i < N; i++) {
2085 final Drawable dr = array[i].mDrawable;
2087 final ConstantState state = dr.getConstantState();
2088 if (state != null) {
2089 pixelCount += state.addAtlasableBitmaps(atlasList);