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