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