OSDN Git Service

Fix a translate issue with saveLayer
[android-x86/frameworks-base.git] / graphics / java / android / graphics / drawable / LayerDrawable.java
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
17 package android.graphics.drawable;
18
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;
38
39 import com.android.internal.R;
40
41 import org.xmlpull.v1.XmlPullParser;
42 import org.xmlpull.v1.XmlPullParserException;
43
44 import java.io.IOException;
45 import java.util.Collection;
46
47 /**
48  * A Drawable that manages an array of other Drawables. These are drawn in array
49  * order, so the element with the largest index will be drawn on top.
50  * <p>
51  * It can be defined in an XML file with the <code>&lt;layer-list></code> element.
52  * Each Drawable in the layer is defined in a nested <code>&lt;item></code>.
53  * <p>
54  * For more information, see the guide to
55  * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
56  *
57  * @attr ref android.R.styleable#LayerDrawable_paddingMode
58  * @attr ref android.R.styleable#LayerDrawableItem_left
59  * @attr ref android.R.styleable#LayerDrawableItem_top
60  * @attr ref android.R.styleable#LayerDrawableItem_right
61  * @attr ref android.R.styleable#LayerDrawableItem_bottom
62  * @attr ref android.R.styleable#LayerDrawableItem_start
63  * @attr ref android.R.styleable#LayerDrawableItem_end
64  * @attr ref android.R.styleable#LayerDrawableItem_width
65  * @attr ref android.R.styleable#LayerDrawableItem_height
66  * @attr ref android.R.styleable#LayerDrawableItem_gravity
67  * @attr ref android.R.styleable#LayerDrawableItem_drawable
68  * @attr ref android.R.styleable#LayerDrawableItem_id
69 */
70 public class LayerDrawable extends Drawable implements Drawable.Callback {
71     /**
72      * Padding mode used to nest each layer inside the padding of the previous
73      * layer.
74      *
75      * @see #setPaddingMode(int)
76      */
77     public static final int PADDING_MODE_NEST = 0;
78
79     /**
80      * Padding mode used to stack each layer directly atop the previous layer.
81      *
82      * @see #setPaddingMode(int)
83      */
84     public static final int PADDING_MODE_STACK = 1;
85
86     /**
87      * Value used for undefined start and end insets.
88      *
89      * @see #getLayerInsetStart(int)
90      * @see #getLayerInsetEnd(int)
91      */
92     public static final int INSET_UNDEFINED = Integer.MIN_VALUE;
93
94     LayerState mLayerState;
95
96     private int[] mPaddingL;
97     private int[] mPaddingT;
98     private int[] mPaddingR;
99     private int[] mPaddingB;
100
101     private final Rect mTmpRect = new Rect();
102     private final Rect mTmpOutRect = new Rect();
103     private final Rect mTmpContainer = new Rect();
104     private Rect mHotspotBounds;
105     private boolean mMutated;
106
107     /**
108      * Creates a new layer drawable with the list of specified layers.
109      *
110      * @param layers a list of drawables to use as layers in this new drawable,
111      *               must be non-null
112      */
113     public LayerDrawable(@NonNull Drawable[] layers) {
114         this(layers, null);
115     }
116
117     /**
118      * Creates a new layer drawable with the specified list of layers and the
119      * specified constant state.
120      *
121      * @param layers The list of layers to add to this drawable.
122      * @param state The constant drawable state.
123      */
124     LayerDrawable(@NonNull Drawable[] layers, @Nullable LayerState state) {
125         this(state, null);
126
127         if (layers == null) {
128             throw new IllegalArgumentException("layers must be non-null");
129         }
130
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();
138         }
139         mLayerState.mNum = length;
140         mLayerState.mChildren = r;
141
142         ensurePadding();
143         refreshPadding();
144     }
145
146     LayerDrawable() {
147         this((LayerState) null, null);
148     }
149
150     /**
151      * The one constructor to rule them all. This is called by all public
152      * constructors to set the state and initialize local properties.
153      */
154     LayerDrawable(@Nullable LayerState state, @Nullable Resources res) {
155         mLayerState = createConstantState(state, res);
156         if (mLayerState.mNum > 0) {
157             ensurePadding();
158             refreshPadding();
159         }
160     }
161
162     LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) {
163         return new LayerState(state, this, res);
164     }
165
166     @Override
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);
171
172         final LayerState state = mLayerState;
173         if (state == null) {
174             return;
175         }
176
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);
181
182         final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable);
183         updateStateFromTypedArray(a);
184         a.recycle();
185
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);
191         }
192
193         inflateLayers(r, parser, attrs, theme);
194
195         ensurePadding();
196         refreshPadding();
197     }
198
199     @Override
200     public void applyTheme(@NonNull Theme t) {
201         super.applyTheme(t);
202
203         final LayerState state = mLayerState;
204         if (state == null) {
205             return;
206         }
207
208         final int density = Drawable.resolveDensity(t.getResources(), 0);
209         state.setDensity(density);
210
211         if (state.mThemeAttrs != null) {
212             final TypedArray a = t.resolveAttributes(
213                     state.mThemeAttrs, R.styleable.LayerDrawable);
214             updateStateFromTypedArray(a);
215             a.recycle();
216         }
217
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);
223
224             if (layer.mThemeAttrs != null) {
225                 final TypedArray a = t.resolveAttributes(
226                         layer.mThemeAttrs, R.styleable.LayerDrawableItem);
227                 updateLayerFromTypedArray(layer, a);
228                 a.recycle();
229             }
230
231             final Drawable d = layer.mDrawable;
232             if (d != null && d.canApplyTheme()) {
233                 d.applyTheme(t);
234
235                 // Update cached mask of child changing configurations.
236                 state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
237             }
238         }
239     }
240
241     /**
242      * Inflates child layers using the specified parser.
243      */
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;
248
249         final int innerDepth = parser.getDepth() + 1;
250         int type;
251         int depth;
252         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
253                 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
254             if (type != XmlPullParser.START_TAG) {
255                 continue;
256             }
257
258             if (depth > innerDepth || !parser.getName().equals("item")) {
259                 continue;
260             }
261
262             final ChildDrawable layer = new ChildDrawable(state.mDensity);
263             final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
264             updateLayerFromTypedArray(layer, a);
265             a.recycle();
266
267             // If the layer doesn't have a drawable or unresolved theme
268             // attribute for a drawable, attempt to parse one from the child
269             // element.
270             if (layer.mDrawable == null && (layer.mThemeAttrs == null ||
271                     layer.mThemeAttrs[R.styleable.LayerDrawableItem_drawable] == 0)) {
272                 while ((type = parser.next()) == XmlPullParser.TEXT) {
273                 }
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");
278                 }
279                 layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
280             }
281
282             if (layer.mDrawable != null) {
283                 state.mChildrenChangingConfigurations |=
284                         layer.mDrawable.getChangingConfigurations();
285                 layer.mDrawable.setCallback(this);
286             }
287
288             addLayer(layer);
289         }
290     }
291
292     /**
293      * Initializes the constant state from the values in the typed array.
294      */
295     private void updateStateFromTypedArray(@NonNull TypedArray a) {
296         final LayerState state = mLayerState;
297
298         // Account for any configuration changes.
299         state.mChangingConfigurations |= a.getChangingConfigurations();
300
301         // Extract the theme attributes, if any.
302         state.mThemeAttrs = a.extractThemeAttrs();
303
304         final int N = a.getIndexCount();
305         for (int i = 0; i < N; i++) {
306             final int attr = a.getIndex(i);
307             switch (attr) {
308                 case R.styleable.LayerDrawable_opacity:
309                     state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride);
310                     break;
311                 case R.styleable.LayerDrawable_paddingTop:
312                     state.mPaddingTop = a.getDimensionPixelOffset(attr, state.mPaddingTop);
313                     break;
314                 case R.styleable.LayerDrawable_paddingBottom:
315                     state.mPaddingBottom = a.getDimensionPixelOffset(attr, state.mPaddingBottom);
316                     break;
317                 case R.styleable.LayerDrawable_paddingLeft:
318                     state.mPaddingLeft = a.getDimensionPixelOffset(attr, state.mPaddingLeft);
319                     break;
320                 case R.styleable.LayerDrawable_paddingRight:
321                     state.mPaddingRight = a.getDimensionPixelOffset(attr, state.mPaddingRight);
322                     break;
323                 case R.styleable.LayerDrawable_paddingStart:
324                     state.mPaddingStart = a.getDimensionPixelOffset(attr, state.mPaddingStart);
325                     break;
326                 case R.styleable.LayerDrawable_paddingEnd:
327                     state.mPaddingEnd = a.getDimensionPixelOffset(attr, state.mPaddingEnd);
328                     break;
329                 case R.styleable.LayerDrawable_autoMirrored:
330                     state.mAutoMirrored = a.getBoolean(attr, state.mAutoMirrored);
331                     break;
332                 case R.styleable.LayerDrawable_paddingMode:
333                     state.mPaddingMode = a.getInteger(attr, state.mPaddingMode);
334                     break;
335             }
336         }
337     }
338
339     private void updateLayerFromTypedArray(@NonNull ChildDrawable layer, @NonNull TypedArray a) {
340         final LayerState state = mLayerState;
341
342         // Account for any configuration changes.
343         state.mChildrenChangingConfigurations |= a.getChangingConfigurations();
344
345         // Extract the theme attributes, if any.
346         layer.mThemeAttrs = a.extractThemeAttrs();
347
348         final int N = a.getIndexCount();
349         for (int i = 0; i < N; i++) {
350             final int attr = a.getIndex(i);
351             switch (attr) {
352                 case R.styleable.LayerDrawableItem_left:
353                     layer.mInsetL = a.getDimensionPixelOffset(attr, layer.mInsetL);
354                     break;
355                 case R.styleable.LayerDrawableItem_top:
356                     layer.mInsetT = a.getDimensionPixelOffset(attr, layer.mInsetT);
357                     break;
358                 case R.styleable.LayerDrawableItem_right:
359                     layer.mInsetR = a.getDimensionPixelOffset(attr, layer.mInsetR);
360                     break;
361                 case R.styleable.LayerDrawableItem_bottom:
362                     layer.mInsetB = a.getDimensionPixelOffset(attr, layer.mInsetB);
363                     break;
364                 case R.styleable.LayerDrawableItem_start:
365                     layer.mInsetS = a.getDimensionPixelOffset(attr, layer.mInsetS);
366                     break;
367                 case R.styleable.LayerDrawableItem_end:
368                     layer.mInsetE = a.getDimensionPixelOffset(attr, layer.mInsetE);
369                     break;
370                 case R.styleable.LayerDrawableItem_width:
371                     layer.mWidth = a.getDimensionPixelSize(attr, layer.mWidth);
372                     break;
373                 case R.styleable.LayerDrawableItem_height:
374                     layer.mHeight = a.getDimensionPixelSize(attr, layer.mHeight);
375                     break;
376                 case R.styleable.LayerDrawableItem_gravity:
377                     layer.mGravity = a.getInteger(attr, layer.mGravity);
378                     break;
379                 case R.styleable.LayerDrawableItem_id:
380                     layer.mId = a.getResourceId(attr, layer.mId);
381                     break;
382             }
383         }
384
385         final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
386         if (dr != null) {
387             layer.mDrawable = dr;
388         }
389     }
390
391     @Override
392     public boolean canApplyTheme() {
393         return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme();
394     }
395
396     /**
397      * @hide
398      */
399     @Override
400     public boolean isProjected() {
401         if (super.isProjected()) {
402             return true;
403         }
404
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()) {
409                 return true;
410             }
411         }
412
413         return false;
414     }
415
416     /**
417      * Adds a new layer at the end of list of layers and returns its index.
418      *
419      * @param layer The layer to add.
420      * @return The index of the layer.
421      */
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;
426         if (i >= N) {
427             final ChildDrawable[] nu = new ChildDrawable[N + 10];
428             if (i > 0) {
429                 System.arraycopy(st.mChildren, 0, nu, 0, i);
430             }
431
432             st.mChildren = nu;
433         }
434
435         st.mChildren[i] = layer;
436         st.mNum++;
437         st.invalidateCache();
438         return i;
439     }
440
441     /**
442      * Add a new layer to this drawable. The new layer is identified by an id.
443      *
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.
451      */
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;
462
463         addLayer(childDrawable);
464
465         mLayerState.mChildrenChangingConfigurations |= dr.getChangingConfigurations();
466         dr.setCallback(this);
467
468         return childDrawable;
469     }
470
471     private ChildDrawable createLayer(Drawable dr) {
472         final ChildDrawable layer = new ChildDrawable(mLayerState.mDensity);
473         layer.mDrawable = dr;
474         return layer;
475     }
476
477     /**
478      * Adds a new layer containing the specified {@code drawable} to the end of
479      * the layer list and returns its index.
480      *
481      * @param dr The drawable to add as a new layer.
482      * @return The index of the new layer.
483      */
484     public int addLayer(Drawable dr) {
485         final ChildDrawable layer = createLayer(dr);
486         final int index = addLayer(layer);
487         ensurePadding();
488         refreshChildPadding(index, layer);
489         return index;
490     }
491
492     /**
493      * Looks for a layer with the given ID and returns its {@link Drawable}.
494      * <p>
495      * If multiple layers are found for the given ID, returns the
496      * {@link Drawable} for the matching layer at the highest index.
497      *
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.
501      */
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;
507             }
508         }
509
510         return null;
511     }
512
513     /**
514      * Sets the ID of a layer.
515      *
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.
519      *
520      * @see #getId(int)
521      * @attr ref android.R.styleable#LayerDrawableItem_id
522      */
523     public void setId(int index, int id) {
524         mLayerState.mChildren[index].mId = id;
525     }
526
527     /**
528      * Returns the ID of the specified layer.
529      *
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
533      *         layer has no id.
534      *
535      * @see #setId(int, int)
536      * @attr ref android.R.styleable#LayerDrawableItem_id
537      */
538     public int getId(int index) {
539         if (index >= mLayerState.mNum) {
540             throw new IndexOutOfBoundsException();
541         }
542         return mLayerState.mChildren[index].mId;
543     }
544
545     /**
546      * Returns the number of layers contained within this layer drawable.
547      *
548      * @return The number of layers.
549      */
550     public int getNumberOfLayers() {
551         return mLayerState.mNum;
552     }
553
554     /**
555      * Replaces the {@link Drawable} for the layer with the given id.
556      *
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).
561      */
562     public boolean setDrawableByLayerId(int id, Drawable drawable) {
563         final int index = findIndexByLayerId(id);
564         if (index < 0) {
565             return false;
566         }
567
568         setDrawable(index, drawable);
569         return true;
570     }
571
572     /**
573      * Returns the layer with the specified {@code id}.
574      * <p>
575      * If multiple layers have the same ID, returns the layer with the lowest
576      * index.
577      *
578      * @param id The ID of the layer to return.
579      * @return The index of the layer with the specified ID.
580      */
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) {
587                 return i;
588             }
589         }
590
591         return -1;
592     }
593
594     /**
595      * Sets the drawable for the layer at the specified index.
596      *
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.
600      *
601      * @see #getDrawable(int)
602      * @attr ref android.R.styleable#LayerDrawableItem_drawable
603      */
604     public void setDrawable(int index, Drawable drawable) {
605         if (index >= mLayerState.mNum) {
606             throw new IndexOutOfBoundsException();
607         }
608
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);
615             }
616
617             childDrawable.mDrawable.setCallback(null);
618         }
619
620         if (drawable != null) {
621             drawable.setCallback(this);
622         }
623
624         childDrawable.mDrawable = drawable;
625         mLayerState.invalidateCache();
626
627         refreshChildPadding(index, childDrawable);
628     }
629
630     /**
631      * Returns the drawable for the layer at the specified index.
632      *
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.
636      *
637      * @see #setDrawable(int, Drawable)
638      * @attr ref android.R.styleable#LayerDrawableItem_drawable
639      */
640     public Drawable getDrawable(int index) {
641         if (index >= mLayerState.mNum) {
642             throw new IndexOutOfBoundsException();
643         }
644         return mLayerState.mChildren[index].mDrawable;
645     }
646
647     /**
648      * Sets an explicit size for the specified layer.
649      * <p>
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.
653      *
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
661      */
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;
666     }
667
668     /**
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
672      */
673     public void setLayerWidth(int index, int w) {
674         final ChildDrawable childDrawable = mLayerState.mChildren[index];
675         childDrawable.mWidth = w;
676     }
677
678     /**
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
683      */
684     public int getLayerWidth(int index) {
685         final ChildDrawable childDrawable = mLayerState.mChildren[index];
686         return childDrawable.mWidth;
687     }
688
689     /**
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
693      */
694     public void setLayerHeight(int index, int h) {
695         final ChildDrawable childDrawable = mLayerState.mChildren[index];
696         childDrawable.mHeight = h;
697     }
698
699     /**
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
704      */
705     public int getLayerHeight(int index) {
706         final ChildDrawable childDrawable = mLayerState.mChildren[index];
707         return childDrawable.mHeight;
708     }
709
710     /**
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)}).
715      * <p>
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}.
722      *
723      * @param index the index of the drawable to adjust
724      * @param gravity the gravity to set for the layer
725      *
726      * @see #getLayerGravity(int)
727      * @attr ref android.R.styleable#LayerDrawableItem_gravity
728      */
729     public void setLayerGravity(int index, int gravity) {
730         final ChildDrawable childDrawable = mLayerState.mChildren[index];
731         childDrawable.mGravity = gravity;
732     }
733
734     /**
735      * @param index the index of the layer
736      * @return the gravity used to position or stretch the specified layer
737      *         within its container
738      *
739      * @see #setLayerGravity(int, int)
740      * @attr ref android.R.styleable#LayerDrawableItem_gravity
741      */
742     public int getLayerGravity(int index) {
743         final ChildDrawable childDrawable = mLayerState.mChildren[index];
744         return childDrawable.mGravity;
745     }
746
747     /**
748      * Specifies the insets in pixels for the drawable at the specified index.
749      *
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
755      *
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
760      */
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);
763     }
764
765     /**
766      * Specifies the relative insets in pixels for the drawable at the
767      * specified index.
768      *
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
774      *
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
779      */
780     public void setLayerInsetRelative(int index, int s, int t, int e, int b) {
781         setLayerInsetInternal(index, 0, t, 0, b, s, e);
782     }
783
784     /**
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
788      */
789     public void setLayerInsetLeft(int index, int l) {
790         final ChildDrawable childDrawable = mLayerState.mChildren[index];
791         childDrawable.mInsetL = l;
792     }
793
794     /**
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
798      */
799     public int getLayerInsetLeft(int index) {
800         final ChildDrawable childDrawable = mLayerState.mChildren[index];
801         return childDrawable.mInsetL;
802     }
803
804     /**
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
808      */
809     public void setLayerInsetRight(int index, int r) {
810         final ChildDrawable childDrawable = mLayerState.mChildren[index];
811         childDrawable.mInsetR = r;
812     }
813
814     /**
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
818      */
819     public int getLayerInsetRight(int index) {
820         final ChildDrawable childDrawable = mLayerState.mChildren[index];
821         return childDrawable.mInsetR;
822     }
823
824     /**
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
828      */
829     public void setLayerInsetTop(int index, int t) {
830         final ChildDrawable childDrawable = mLayerState.mChildren[index];
831         childDrawable.mInsetT = t;
832     }
833
834     /**
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
838      */
839     public int getLayerInsetTop(int index) {
840         final ChildDrawable childDrawable = mLayerState.mChildren[index];
841         return childDrawable.mInsetT;
842     }
843
844     /**
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
848      */
849     public void setLayerInsetBottom(int index, int b) {
850         final ChildDrawable childDrawable = mLayerState.mChildren[index];
851         childDrawable.mInsetB = b;
852     }
853
854     /**
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
858      */
859     public int getLayerInsetBottom(int index) {
860         final ChildDrawable childDrawable = mLayerState.mChildren[index];
861         return childDrawable.mInsetB;
862     }
863
864     /**
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
868      */
869     public void setLayerInsetStart(int index, int s) {
870         final ChildDrawable childDrawable = mLayerState.mChildren[index];
871         childDrawable.mInsetS = s;
872     }
873
874     /**
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
879      */
880     public int getLayerInsetStart(int index) {
881         final ChildDrawable childDrawable = mLayerState.mChildren[index];
882         return childDrawable.mInsetS;
883     }
884
885     /**
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
890      */
891     public void setLayerInsetEnd(int index, int e) {
892         final ChildDrawable childDrawable = mLayerState.mChildren[index];
893         childDrawable.mInsetE = e;
894     }
895
896     /**
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
900      */
901     public int getLayerInsetEnd(int index) {
902         final ChildDrawable childDrawable = mLayerState.mChildren[index];
903         return childDrawable.mInsetE;
904     }
905
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;
914     }
915
916     /**
917      * Specifies how layer padding should affect the bounds of subsequent
918      * layers. The default value is {@link #PADDING_MODE_NEST}.
919      *
920      * @param mode padding mode, one of:
921      *            <ul>
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
926      *            </ul>
927      *
928      * @see #getPaddingMode()
929      * @attr ref android.R.styleable#LayerDrawable_paddingMode
930      */
931     public void setPaddingMode(int mode) {
932         if (mLayerState.mPaddingMode != mode) {
933             mLayerState.mPaddingMode = mode;
934         }
935     }
936
937     /**
938      * @return the current padding mode
939      *
940      * @see #setPaddingMode(int)
941      * @attr ref android.R.styleable#LayerDrawable_paddingMode
942      */
943     public int getPaddingMode() {
944       return mLayerState.mPaddingMode;
945     }
946
947     @Override
948     public void invalidateDrawable(@NonNull Drawable who) {
949         invalidateSelf();
950     }
951
952     @Override
953     public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
954         scheduleSelf(what, when);
955     }
956
957     @Override
958     public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
959         unscheduleSelf(what);
960     }
961
962     @Override
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;
968             if (dr != null) {
969                 dr.draw(canvas);
970             }
971         }
972     }
973
974     @Override
975     public @Config int getChangingConfigurations() {
976         return super.getChangingConfigurations() | mLayerState.getChangingConfigurations();
977     }
978
979     @Override
980     public boolean getPadding(Rect padding) {
981         final LayerState layerState = mLayerState;
982         if (layerState.mPaddingMode == PADDING_MODE_NEST) {
983             computeNestedPadding(padding);
984         } else {
985             computeStackedPadding(padding);
986         }
987
988         final int paddingT = layerState.mPaddingTop;
989         final int paddingB = layerState.mPaddingBottom;
990
991         // Resolve padding for RTL. Relative padding overrides absolute
992         // padding.
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;
998
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;
1003         }
1004
1005         if (paddingT >= 0) {
1006             padding.top = paddingT;
1007         }
1008
1009         if (paddingR >= 0) {
1010             padding.right = paddingR;
1011         }
1012
1013         if (paddingB >= 0) {
1014             padding.bottom = paddingB;
1015         }
1016
1017         return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
1018     }
1019
1020     /**
1021      * Sets the absolute padding.
1022      * <p>
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)}).
1026      * <p>
1027      * Calling this method clears any relative padding values previously set
1028      * using {@link #setPaddingRelative(int, int, int, int)}.
1029      *
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
1034      *               padding
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)
1040      */
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;
1047
1048         // Clear relative padding values.
1049         layerState.mPaddingStart = -1;
1050         layerState.mPaddingEnd = -1;
1051     }
1052
1053     /**
1054      * Sets the relative padding.
1055      * <p>
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)}).
1059      * <p>
1060      * Calling this method clears any absolute padding values previously set
1061      * using {@link #setPadding(int, int, int, int)}.
1062      *
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
1067      *               padding
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)
1073      */
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;
1080
1081         // Clear absolute padding values.
1082         layerState.mPaddingLeft = -1;
1083         layerState.mPaddingRight = -1;
1084     }
1085
1086     /**
1087      * Returns the left padding in pixels.
1088      * <p>
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()}.
1093      *
1094      * @return the left padding in pixels, or -1 if not explicitly specified
1095      * @see #setPadding(int, int, int, int)
1096      * @see #getPadding(Rect)
1097      */
1098     public int getLeftPadding() {
1099         return mLayerState.mPaddingLeft;
1100     }
1101
1102     /**
1103      * Returns the right padding in pixels.
1104      * <p>
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()}.
1109      *
1110      * @return the right padding in pixels, or -1 if not explicitly specified
1111      * @see #setPadding(int, int, int, int)
1112      * @see #getPadding(Rect)
1113      */
1114     public int getRightPadding() {
1115         return mLayerState.mPaddingRight;
1116     }
1117
1118     /**
1119      * Returns the start padding in pixels.
1120      * <p>
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()}.
1125      *
1126      * @return the start padding in pixels, or -1 if not explicitly specified
1127      * @see #setPaddingRelative(int, int, int, int)
1128      * @see #getPadding(Rect)
1129      */
1130     public int getStartPadding() {
1131         return mLayerState.mPaddingStart;
1132     }
1133
1134     /**
1135      * Returns the end padding in pixels.
1136      * <p>
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()}.
1141      *
1142      * @return the end padding in pixels, or -1 if not explicitly specified
1143      * @see #setPaddingRelative(int, int, int, int)
1144      * @see #getPadding(Rect)
1145      */
1146     public int getEndPadding() {
1147         return mLayerState.mPaddingEnd;
1148     }
1149
1150     /**
1151      * Returns the top padding in pixels.
1152      * <p>
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()}.
1157      *
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)
1162      */
1163     public int getTopPadding() {
1164         return mLayerState.mPaddingTop;
1165     }
1166
1167     /**
1168      * Returns the bottom padding in pixels.
1169      * <p>
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()}.
1174      *
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)
1179      */
1180     public int getBottomPadding() {
1181         return mLayerState.mPaddingBottom;
1182     }
1183
1184     private void computeNestedPadding(Rect padding) {
1185         padding.left = 0;
1186         padding.top = 0;
1187         padding.right = 0;
1188         padding.bottom = 0;
1189
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]);
1195
1196             padding.left += mPaddingL[i];
1197             padding.top += mPaddingT[i];
1198             padding.right += mPaddingR[i];
1199             padding.bottom += mPaddingB[i];
1200         }
1201     }
1202
1203     private void computeStackedPadding(Rect padding) {
1204         padding.left = 0;
1205         padding.top = 0;
1206         padding.right = 0;
1207         padding.bottom = 0;
1208
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]);
1214
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]);
1219         }
1220     }
1221
1222     /**
1223      * Populates <code>outline</code> with the first available (non-empty) layer outline.
1224      *
1225      * @param outline Outline in which to place the first available layer outline
1226      */
1227     @Override
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;
1233             if (dr != null) {
1234                 dr.getOutline(outline);
1235                 if (!outline.isEmpty()) {
1236                     return;
1237                 }
1238             }
1239         }
1240     }
1241
1242     @Override
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;
1248             if (dr != null) {
1249                 dr.setHotspot(x, y);
1250             }
1251         }
1252     }
1253
1254     @Override
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;
1260             if (dr != null) {
1261                 dr.setHotspotBounds(left, top, right, bottom);
1262             }
1263         }
1264
1265         if (mHotspotBounds == null) {
1266             mHotspotBounds = new Rect(left, top, right, bottom);
1267         } else {
1268             mHotspotBounds.set(left, top, right, bottom);
1269         }
1270     }
1271
1272     @Override
1273     public void getHotspotBounds(Rect outRect) {
1274         if (mHotspotBounds != null) {
1275             outRect.set(mHotspotBounds);
1276         } else {
1277             super.getHotspotBounds(outRect);
1278         }
1279     }
1280
1281     @Override
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;
1288             if (dr != null) {
1289                 dr.setVisible(visible, restart);
1290             }
1291         }
1292
1293         return changed;
1294     }
1295
1296     @Override
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;
1302             if (dr != null) {
1303                 dr.setDither(dither);
1304             }
1305         }
1306     }
1307
1308     @Override
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;
1314             if (dr != null) {
1315                 dr.setAlpha(alpha);
1316             }
1317         }
1318     }
1319
1320     @Override
1321     public int getAlpha() {
1322         final Drawable dr = getFirstNonNullDrawable();
1323         if (dr != null) {
1324             return dr.getAlpha();
1325         } else {
1326             return super.getAlpha();
1327         }
1328     }
1329
1330     @Override
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;
1336             if (dr != null) {
1337                 dr.setColorFilter(colorFilter);
1338             }
1339         }
1340     }
1341
1342     @Override
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;
1348             if (dr != null) {
1349                 dr.setTintList(tint);
1350             }
1351         }
1352     }
1353
1354     @Override
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;
1360             if (dr != null) {
1361                 dr.setTintMode(tintMode);
1362             }
1363         }
1364     }
1365
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;
1371             if (dr != null) {
1372                 return dr;
1373             }
1374         }
1375         return null;
1376     }
1377
1378     /**
1379      * Sets the opacity of this drawable directly instead of collecting the
1380      * states from the layers.
1381      *
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
1388      */
1389     public void setOpacity(int opacity) {
1390         mLayerState.mOpacityOverride = opacity;
1391     }
1392
1393     @Override
1394     public int getOpacity() {
1395         if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
1396             return mLayerState.mOpacityOverride;
1397         }
1398         return mLayerState.getOpacity();
1399     }
1400
1401     @Override
1402     public void setAutoMirrored(boolean mirrored) {
1403         mLayerState.mAutoMirrored = mirrored;
1404
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;
1409             if (dr != null) {
1410                 dr.setAutoMirrored(mirrored);
1411             }
1412         }
1413     }
1414
1415     @Override
1416     public boolean isAutoMirrored() {
1417         return mLayerState.mAutoMirrored;
1418     }
1419
1420     @Override
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;
1426             if (dr != null) {
1427                 dr.jumpToCurrentState();
1428             }
1429         }
1430     }
1431
1432     @Override
1433     public boolean isStateful() {
1434         return mLayerState.isStateful();
1435     }
1436
1437     @Override
1438     protected boolean onStateChange(int[] state) {
1439         boolean changed = false;
1440
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]);
1447                 changed = true;
1448             }
1449         }
1450
1451         if (changed) {
1452             updateLayerBounds(getBounds());
1453         }
1454
1455         return changed;
1456     }
1457
1458     @Override
1459     protected boolean onLevelChange(int level) {
1460         boolean changed = false;
1461
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]);
1468                 changed = true;
1469             }
1470         }
1471
1472         if (changed) {
1473             updateLayerBounds(getBounds());
1474         }
1475
1476         return changed;
1477     }
1478
1479     @Override
1480     protected void onBoundsChange(Rect bounds) {
1481         updateLayerBounds(bounds);
1482     }
1483
1484     private void updateLayerBounds(Rect bounds) {
1485         int paddingL = 0;
1486         int paddingT = 0;
1487         int paddingR = 0;
1488         int paddingB = 0;
1489
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;
1495
1496         for (int i = 0, count = mLayerState.mNum; i < count; i++) {
1497             final ChildDrawable r = array[i];
1498             final Drawable d = r.mDrawable;
1499             if (d == null) {
1500                 continue;
1501             }
1502
1503             final int insetT = r.mInsetT;
1504             final int insetB = r.mInsetB;
1505
1506             // Resolve insets for RTL. Relative insets override absolute
1507             // insets.
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;
1512
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);
1518
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);
1526
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);
1532
1533             if (isPaddingNested) {
1534                 paddingL += mPaddingL[i];
1535                 paddingR += mPaddingR[i];
1536                 paddingT += mPaddingT[i];
1537                 paddingB += mPaddingB[i];
1538             }
1539         }
1540     }
1541
1542     /**
1543      * Resolves layer gravity given explicit gravity and dimensions.
1544      * <p>
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.
1548      *
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
1553      */
1554     private static int resolveGravity(int gravity, int width, int height,
1555             int intrinsicWidth, int intrinsicHeight) {
1556         if (!Gravity.isHorizontal(gravity)) {
1557             if (width < 0) {
1558                 gravity |= Gravity.FILL_HORIZONTAL;
1559             } else {
1560                 gravity |= Gravity.START;
1561             }
1562         }
1563
1564         if (!Gravity.isVertical(gravity)) {
1565             if (height < 0) {
1566                 gravity |= Gravity.FILL_VERTICAL;
1567             } else {
1568                 gravity |= Gravity.TOP;
1569             }
1570         }
1571
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;
1577         }
1578
1579         if (height < 0 && intrinsicHeight < 0) {
1580             gravity |= Gravity.FILL_VERTICAL;
1581         }
1582
1583         return gravity;
1584     }
1585
1586     @Override
1587     public int getIntrinsicWidth() {
1588         int width = -1;
1589         int padL = 0;
1590         int padR = 0;
1591
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) {
1599                 continue;
1600             }
1601
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;
1611
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;
1616             if (w > width) {
1617                 width = w;
1618             }
1619
1620             if (nest) {
1621                 padL += mPaddingL[i];
1622                 padR += mPaddingR[i];
1623             }
1624         }
1625
1626         return width;
1627     }
1628
1629     @Override
1630     public int getIntrinsicHeight() {
1631         int height = -1;
1632         int padT = 0;
1633         int padB = 0;
1634
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) {
1641                 continue;
1642             }
1643
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;
1648             if (h > height) {
1649                 height = h;
1650             }
1651
1652             if (nest) {
1653                 padT += mPaddingT[i];
1654                 padB += mPaddingB[i];
1655             }
1656         }
1657
1658         return height;
1659     }
1660
1661     /**
1662      * Refreshes the cached padding values for the specified child.
1663      *
1664      * @return true if the child's padding has changed
1665      */
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;
1676                 return true;
1677             }
1678         }
1679         return false;
1680     }
1681
1682     /**
1683      * Ensures the child padding caches are large enough.
1684      */
1685     void ensurePadding() {
1686         final int N = mLayerState.mNum;
1687         if (mPaddingL != null && mPaddingL.length >= N) {
1688             return;
1689         }
1690
1691         mPaddingL = new int[N];
1692         mPaddingT = new int[N];
1693         mPaddingR = new int[N];
1694         mPaddingB = new int[N];
1695     }
1696
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]);
1702         }
1703     }
1704
1705     @Override
1706     public ConstantState getConstantState() {
1707         if (mLayerState.canConstantState()) {
1708             mLayerState.mChangingConfigurations = getChangingConfigurations();
1709             return mLayerState;
1710         }
1711         return null;
1712     }
1713
1714     @Override
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;
1722                 if (dr != null) {
1723                     dr.mutate();
1724                 }
1725             }
1726             mMutated = true;
1727         }
1728         return this;
1729     }
1730
1731     /**
1732      * @hide
1733      */
1734     public void clearMutated() {
1735         super.clearMutated();
1736
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;
1741             if (dr != null) {
1742                 dr.clearMutated();
1743             }
1744         }
1745         mMutated = false;
1746     }
1747
1748     @Override
1749     public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) {
1750         boolean changed = false;
1751
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;
1756             if (dr != null) {
1757                 changed |= dr.setLayoutDirection(layoutDirection);
1758             }
1759         }
1760
1761         updateLayerBounds(getBounds());
1762         return changed;
1763     }
1764
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;
1776
1777         ChildDrawable(int density) {
1778             mDensity = density;
1779         }
1780
1781         ChildDrawable(@NonNull ChildDrawable orig, @NonNull LayerDrawable owner,
1782                 @Nullable Resources res) {
1783             final Drawable dr = orig.mDrawable;
1784             final Drawable clone;
1785             if (dr != null) {
1786                 final ConstantState cs = dr.getConstantState();
1787                 if (cs == null) {
1788                     clone = dr;
1789                 } else if (res != null) {
1790                     clone = cs.newDrawable(res);
1791                 } else {
1792                     clone = cs.newDrawable();
1793                 }
1794                 clone.setCallback(owner);
1795                 clone.setLayoutDirection(dr.getLayoutDirection());
1796                 clone.setBounds(dr.getBounds());
1797                 clone.setLevel(dr.getLevel());
1798             } else {
1799                 clone = null;
1800             }
1801
1802             mDrawable = clone;
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;
1813             mId = orig.mId;
1814
1815             mDensity = Drawable.resolveDensity(res, orig.mDensity);
1816             if (orig.mDensity != mDensity) {
1817                 applyDensityScaling(orig.mDensity, mDensity);
1818             }
1819         }
1820
1821         public boolean canApplyTheme() {
1822             return mThemeAttrs != null
1823                     || (mDrawable != null && mDrawable.canApplyTheme());
1824         }
1825
1826         public final void setDensity(int targetDensity) {
1827             if (mDensity != targetDensity) {
1828                 final int sourceDensity = mDensity;
1829                 mDensity = targetDensity;
1830
1831                 applyDensityScaling(sourceDensity, targetDensity);
1832             }
1833         }
1834
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);
1842             }
1843             if (mInsetE != INSET_UNDEFINED) {
1844                 mInsetE = Drawable.scaleFromDensity(mInsetE, sourceDensity, targetDensity, false);
1845             }
1846             if (mWidth > 0) {
1847                 mWidth = Drawable.scaleFromDensity(mWidth, sourceDensity, targetDensity, true);
1848             }
1849             if (mHeight > 0) {
1850                 mHeight = Drawable.scaleFromDensity(mHeight, sourceDensity, targetDensity, true);
1851             }
1852         }
1853     }
1854
1855     static class LayerState extends ConstantState {
1856         private int[] mThemeAttrs;
1857
1858         int mNum;
1859         ChildDrawable[] mChildren;
1860
1861         int mDensity;
1862
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;
1871
1872         @Config int mChangingConfigurations;
1873         @Config int mChildrenChangingConfigurations;
1874
1875         private boolean mHaveOpacity;
1876         private int mOpacity;
1877
1878         private boolean mHaveIsStateful;
1879         private boolean mIsStateful;
1880
1881         private boolean mAutoMirrored = false;
1882
1883         private int mPaddingMode = PADDING_MODE_NEST;
1884
1885         LayerState(@Nullable LayerState orig, @NonNull LayerDrawable owner,
1886                 @Nullable Resources res) {
1887             mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0);
1888
1889             if (orig != null) {
1890                 final ChildDrawable[] origChildDrawable = orig.mChildren;
1891                 final int N = orig.mNum;
1892
1893                 mNum = N;
1894                 mChildren = new ChildDrawable[N];
1895
1896                 mChangingConfigurations = orig.mChangingConfigurations;
1897                 mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
1898
1899                 for (int i = 0; i < N; i++) {
1900                     final ChildDrawable or = origChildDrawable[i];
1901                     mChildren[i] = new ChildDrawable(or, owner, res);
1902                 }
1903
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;
1918
1919                 if (orig.mDensity != mDensity) {
1920                     applyDensityScaling(orig.mDensity, mDensity);
1921                 }
1922             } else {
1923                 mNum = 0;
1924                 mChildren = null;
1925             }
1926         }
1927
1928         public final void setDensity(int targetDensity) {
1929             if (mDensity != targetDensity) {
1930                 final int sourceDensity = mDensity;
1931                 mDensity = targetDensity;
1932
1933                 onDensityChanged(sourceDensity, targetDensity);
1934             }
1935         }
1936
1937         protected void onDensityChanged(int sourceDensity, int targetDensity) {
1938             applyDensityScaling(sourceDensity, targetDensity);
1939         }
1940
1941         private void applyDensityScaling(int sourceDensity, int targetDensity) {
1942             if (mPaddingLeft > 0) {
1943                 mPaddingLeft = Drawable.scaleFromDensity(
1944                         mPaddingLeft, sourceDensity, targetDensity, false);
1945             }
1946             if (mPaddingTop > 0) {
1947                 mPaddingTop = Drawable.scaleFromDensity(
1948                         mPaddingTop, sourceDensity, targetDensity, false);
1949             }
1950             if (mPaddingRight > 0) {
1951                 mPaddingRight = Drawable.scaleFromDensity(
1952                         mPaddingRight, sourceDensity, targetDensity, false);
1953             }
1954             if (mPaddingBottom > 0) {
1955                 mPaddingBottom = Drawable.scaleFromDensity(
1956                         mPaddingBottom, sourceDensity, targetDensity, false);
1957             }
1958             if (mPaddingStart > 0) {
1959                 mPaddingStart = Drawable.scaleFromDensity(
1960                         mPaddingStart, sourceDensity, targetDensity, false);
1961             }
1962             if (mPaddingEnd > 0) {
1963                 mPaddingEnd = Drawable.scaleFromDensity(
1964                         mPaddingEnd, sourceDensity, targetDensity, false);
1965             }
1966         }
1967
1968         @Override
1969         public boolean canApplyTheme() {
1970             if (mThemeAttrs != null || super.canApplyTheme()) {
1971                 return true;
1972             }
1973
1974             final ChildDrawable[] array = mChildren;
1975             final int N = mNum;
1976             for (int i = 0; i < N; i++) {
1977                 final ChildDrawable layer = array[i];
1978                 if (layer.canApplyTheme()) {
1979                     return true;
1980                 }
1981             }
1982
1983             return false;
1984         }
1985
1986         @Override
1987         public Drawable newDrawable() {
1988             return new LayerDrawable(this, null);
1989         }
1990
1991         @Override
1992         public Drawable newDrawable(@Nullable Resources res) {
1993             return new LayerDrawable(this, res);
1994         }
1995
1996         @Override
1997         public @Config int getChangingConfigurations() {
1998             return mChangingConfigurations
1999                     | mChildrenChangingConfigurations;
2000         }
2001
2002         public final int getOpacity() {
2003             if (mHaveOpacity) {
2004                 return mOpacity;
2005             }
2006
2007             final ChildDrawable[] array = mChildren;
2008             final int N = mNum;
2009
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) {
2014                     firstIndex = i;
2015                     break;
2016                 }
2017             }
2018
2019             int op;
2020             if (firstIndex >= 0) {
2021                 op = array[firstIndex].mDrawable.getOpacity();
2022             } else {
2023                 op = PixelFormat.TRANSPARENT;
2024             }
2025
2026             // Merge all remaining non-null drawables.
2027             for (int i = firstIndex + 1; i < N; i++) {
2028                 final Drawable dr = array[i].mDrawable;
2029                 if (dr != null) {
2030                     op = Drawable.resolveOpacity(op, dr.getOpacity());
2031                 }
2032             }
2033
2034             mOpacity = op;
2035             mHaveOpacity = true;
2036             return op;
2037         }
2038
2039         public final boolean isStateful() {
2040             if (mHaveIsStateful) {
2041                 return mIsStateful;
2042             }
2043
2044             final ChildDrawable[] array = mChildren;
2045             final int N = mNum;
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()) {
2050                     isStateful = true;
2051                     break;
2052                 }
2053             }
2054
2055             mIsStateful = isStateful;
2056             mHaveIsStateful = true;
2057             return isStateful;
2058         }
2059
2060         public final boolean canConstantState() {
2061             final ChildDrawable[] array = mChildren;
2062             final int N = mNum;
2063             for (int i = 0; i < N; i++) {
2064                 final Drawable dr = array[i].mDrawable;
2065                 if (dr != null && dr.getConstantState() == null) {
2066                     return false;
2067                 }
2068             }
2069
2070             // Don't cache the result, this method is not called very often.
2071             return true;
2072         }
2073
2074         public void invalidateCache() {
2075             mHaveOpacity = false;
2076             mHaveIsStateful = false;
2077         }
2078
2079         @Override
2080         public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
2081             final ChildDrawable[] array = mChildren;
2082             final int N = mNum;
2083             int pixelCount = 0;
2084             for (int i = 0; i < N; i++) {
2085                 final Drawable dr = array[i].mDrawable;
2086                 if (dr != null) {
2087                     final ConstantState state = dr.getConstantState();
2088                     if (state != null) {
2089                         pixelCount += state.addAtlasableBitmaps(atlasList);
2090                     }
2091                 }
2092             }
2093             return pixelCount;
2094         }
2095     }
2096 }
2097