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