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