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