LayerDrawable.java revision bd3bfc5285dcacff0a69fecf3baeeeb90d887a58
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.content.res.ColorStateList;
21import android.content.res.Resources;
22import android.content.res.Resources.Theme;
23import android.content.res.TypedArray;
24import android.graphics.Bitmap;
25import android.graphics.Canvas;
26import android.graphics.ColorFilter;
27import android.graphics.Outline;
28import android.graphics.PixelFormat;
29import android.graphics.PorterDuff.Mode;
30import android.graphics.Rect;
31import android.util.AttributeSet;
32import android.util.LayoutDirection;
33import android.view.Gravity;
34import android.view.View;
35
36import com.android.internal.R;
37
38import org.xmlpull.v1.XmlPullParser;
39import org.xmlpull.v1.XmlPullParserException;
40
41import java.io.IOException;
42import java.util.Collection;
43
44/**
45 * A Drawable that manages an array of other Drawables. These are drawn in array
46 * order, so the element with the largest index will be drawn on top.
47 * <p>
48 * It can be defined in an XML file with the <code>&lt;layer-list></code> element.
49 * Each Drawable in the layer is defined in a nested <code>&lt;item></code>.
50 * <p>
51 * For more information, see the guide to
52 * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
53 *
54 * @attr ref android.R.styleable#LayerDrawable_paddingMode
55 * @attr ref android.R.styleable#LayerDrawableItem_left
56 * @attr ref android.R.styleable#LayerDrawableItem_top
57 * @attr ref android.R.styleable#LayerDrawableItem_right
58 * @attr ref android.R.styleable#LayerDrawableItem_bottom
59 * @attr ref android.R.styleable#LayerDrawableItem_start
60 * @attr ref android.R.styleable#LayerDrawableItem_end
61 * @attr ref android.R.styleable#LayerDrawableItem_width
62 * @attr ref android.R.styleable#LayerDrawableItem_height
63 * @attr ref android.R.styleable#LayerDrawableItem_gravity
64 * @attr ref android.R.styleable#LayerDrawableItem_drawable
65 * @attr ref android.R.styleable#LayerDrawableItem_id
66*/
67public class LayerDrawable extends Drawable implements Drawable.Callback {
68    /**
69     * Padding mode used to nest each layer inside the padding of the previous
70     * layer.
71     *
72     * @see #setPaddingMode(int)
73     */
74    public static final int PADDING_MODE_NEST = 0;
75
76    /**
77     * Padding mode used to stack each layer directly atop the previous layer.
78     *
79     * @see #setPaddingMode(int)
80     */
81    public static final int PADDING_MODE_STACK = 1;
82
83    /** Value used for undefined start and end insets. */
84    private static final int UNDEFINED_INSET = Integer.MIN_VALUE;
85
86    LayerState mLayerState;
87
88    private int mOpacityOverride = PixelFormat.UNKNOWN;
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     * Create 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     */
105    public LayerDrawable(Drawable[] layers) {
106        this(layers, null);
107    }
108
109    /**
110     * Create a new layer drawable with the specified list of layers and the
111     * specified constant state.
112     *
113     * @param layers The list of layers to add to this drawable.
114     * @param state The constant drawable state.
115     */
116    LayerDrawable(Drawable[] layers, LayerState state) {
117        this(state, null);
118
119        final int length = layers.length;
120        final ChildDrawable[] r = new ChildDrawable[length];
121        for (int i = 0; i < length; i++) {
122            r[i] = new ChildDrawable();
123            r[i].mDrawable = layers[i];
124            layers[i].setCallback(this);
125            mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations();
126        }
127        mLayerState.mNum = length;
128        mLayerState.mChildren = r;
129
130        ensurePadding();
131    }
132
133    LayerDrawable() {
134        this((LayerState) null, null);
135    }
136
137    LayerDrawable(LayerState state, Resources res) {
138        mLayerState = createConstantState(state, res);
139        if (mLayerState.mNum > 0) {
140            ensurePadding();
141        }
142    }
143
144    LayerState createConstantState(LayerState state, Resources res) {
145        return new LayerState(state, this, res);
146    }
147
148    @Override
149    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
150            throws XmlPullParserException, IOException {
151        super.inflate(r, parser, attrs, theme);
152
153        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable);
154        updateStateFromTypedArray(a);
155        a.recycle();
156
157        inflateLayers(r, parser, attrs, theme);
158
159        ensurePadding();
160        onStateChange(getState());
161    }
162
163    /**
164     * Initializes the constant state from the values in the typed array.
165     */
166    private void updateStateFromTypedArray(TypedArray a) {
167        final LayerState state = mLayerState;
168
169        // Account for any configuration changes.
170        state.mChangingConfigurations |= a.getChangingConfigurations();
171
172        // Extract the theme attributes, if any.
173        state.mThemeAttrs = a.extractThemeAttrs();
174
175        mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, mOpacityOverride);
176
177        state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored,
178                state.mAutoMirrored);
179        state.mPaddingMode = a.getInteger(R.styleable.LayerDrawable_paddingMode,
180                state.mPaddingMode);
181    }
182
183    /**
184     * Inflates child layers using the specified parser.
185     */
186    private void inflateLayers(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
187            throws XmlPullParserException, IOException {
188        final LayerState state = mLayerState;
189
190        final int innerDepth = parser.getDepth() + 1;
191        int type;
192        int depth;
193        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
194                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
195            if (type != XmlPullParser.START_TAG) {
196                continue;
197            }
198
199            if (depth > innerDepth || !parser.getName().equals("item")) {
200                continue;
201            }
202
203            final ChildDrawable layer = new ChildDrawable();
204            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
205            updateLayerFromTypedArray(layer, a);
206            a.recycle();
207
208            if (layer.mDrawable == null) {
209                while ((type = parser.next()) == XmlPullParser.TEXT) {
210                }
211                if (type != XmlPullParser.START_TAG) {
212                    throw new XmlPullParserException(parser.getPositionDescription()
213                            + ": <item> tag requires a 'drawable' attribute or "
214                            + "child tag defining a drawable");
215                }
216                layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
217            }
218
219            if (layer.mDrawable != null) {
220                state.mChildrenChangingConfigurations |=
221                        layer.mDrawable.getChangingConfigurations();
222                layer.mDrawable.setCallback(this);
223            }
224
225            addLayer(layer);
226        }
227    }
228
229    private void updateLayerFromTypedArray(ChildDrawable layer, TypedArray a) {
230        final LayerState state = mLayerState;
231
232        // Account for any configuration changes.
233        state.mChildrenChangingConfigurations |= a.getChangingConfigurations();
234
235        // Extract the theme attributes, if any.
236        layer.mThemeAttrs = a.extractThemeAttrs();
237
238        layer.mInsetL = a.getDimensionPixelOffset(
239                R.styleable.LayerDrawableItem_left, layer.mInsetL);
240        layer.mInsetT = a.getDimensionPixelOffset(
241                R.styleable.LayerDrawableItem_top, layer.mInsetT);
242        layer.mInsetR = a.getDimensionPixelOffset(
243                R.styleable.LayerDrawableItem_right, layer.mInsetR);
244        layer.mInsetB = a.getDimensionPixelOffset(
245                R.styleable.LayerDrawableItem_bottom, layer.mInsetB);
246        layer.mInsetS = a.getDimensionPixelOffset(
247                R.styleable.LayerDrawableItem_start, layer.mInsetS);
248        layer.mInsetE = a.getDimensionPixelOffset(
249                R.styleable.LayerDrawableItem_end, layer.mInsetE);
250        layer.mWidth = a.getDimensionPixelSize(
251                R.styleable.LayerDrawableItem_width, layer.mWidth);
252        layer.mHeight = a.getDimensionPixelSize(
253                R.styleable.LayerDrawableItem_height, layer.mHeight);
254        layer.mGravity = a.getInteger(
255                R.styleable.LayerDrawableItem_gravity, layer.mGravity);
256        layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId);
257
258        final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
259        if (dr != null) {
260            layer.mDrawable = dr;
261        }
262    }
263
264    @Override
265    public void applyTheme(Theme t) {
266        super.applyTheme(t);
267
268        final LayerState state = mLayerState;
269        if (state == null) {
270            return;
271        }
272
273        if (state.mThemeAttrs != null) {
274            final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.LayerDrawable);
275            updateStateFromTypedArray(a);
276            a.recycle();
277        }
278
279        final ChildDrawable[] array = state.mChildren;
280        final int N = state.mNum;
281        for (int i = 0; i < N; i++) {
282            final ChildDrawable layer = array[i];
283            if (layer.mThemeAttrs != null) {
284                final TypedArray a = t.resolveAttributes(layer.mThemeAttrs,
285                        R.styleable.LayerDrawableItem);
286                updateLayerFromTypedArray(layer, a);
287                a.recycle();
288            }
289
290            final Drawable d = layer.mDrawable;
291            if (d.canApplyTheme()) {
292                d.applyTheme(t);
293            }
294        }
295
296        ensurePadding();
297        onStateChange(getState());
298    }
299
300    @Override
301    public boolean canApplyTheme() {
302        return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme();
303    }
304
305    /**
306     * @hide
307     */
308    @Override
309    public boolean isProjected() {
310        if (super.isProjected()) {
311            return true;
312        }
313
314        final ChildDrawable[] layers = mLayerState.mChildren;
315        final int N = mLayerState.mNum;
316        for (int i = 0; i < N; i++) {
317            if (layers[i].mDrawable.isProjected()) {
318                return true;
319            }
320        }
321
322        return false;
323    }
324
325    /**
326     * Adds a new layer at the end of list of layers and returns its index.
327     *
328     * @param layer The layer to add.
329     * @return The index of the layer.
330     */
331    int addLayer(ChildDrawable layer) {
332        final LayerState st = mLayerState;
333        final int N = st.mChildren != null ? st.mChildren.length : 0;
334        final int i = st.mNum;
335        if (i >= N) {
336            final ChildDrawable[] nu = new ChildDrawable[N + 10];
337            if (i > 0) {
338                System.arraycopy(st.mChildren, 0, nu, 0, i);
339            }
340
341            st.mChildren = nu;
342        }
343
344        st.mChildren[i] = layer;
345        st.mNum++;
346        st.invalidateCache();
347        return i;
348    }
349
350    /**
351     * Add a new layer to this drawable. The new layer is identified by an id.
352     *
353     * @param dr The drawable to add as a layer.
354     * @param themeAttrs Theme attributes extracted from the layer.
355     * @param id The id of the new layer.
356     * @param left The left padding of the new layer.
357     * @param top The top padding of the new layer.
358     * @param right The right padding of the new layer.
359     * @param bottom The bottom padding of the new layer.
360     */
361    ChildDrawable addLayer(Drawable dr, int[] themeAttrs, int id,
362            int left, int top, int right, int bottom) {
363        final ChildDrawable childDrawable = createLayer(dr);
364        childDrawable.mId = id;
365        childDrawable.mThemeAttrs = themeAttrs;
366        childDrawable.mDrawable.setAutoMirrored(isAutoMirrored());
367        childDrawable.mInsetL = left;
368        childDrawable.mInsetT = top;
369        childDrawable.mInsetR = right;
370        childDrawable.mInsetB = bottom;
371
372        addLayer(childDrawable);
373
374        mLayerState.mChildrenChangingConfigurations |= dr.getChangingConfigurations();
375        dr.setCallback(this);
376
377        return childDrawable;
378    }
379
380    private ChildDrawable createLayer(Drawable dr) {
381        final ChildDrawable layer = new ChildDrawable();
382        layer.mDrawable = dr;
383        return layer;
384    }
385
386    /**
387     * Adds a new layer containing the specified {@code drawable} to the end of
388     * the layer list and returns its index.
389     *
390     * @param dr The drawable to add as a new layer.
391     * @return The index of the new layer.
392     */
393    public int addLayer(Drawable dr) {
394        final ChildDrawable layer = createLayer(dr);
395        final int index = addLayer(layer);
396        return index;
397    }
398
399    /**
400     * Looks for a layer with the given ID and returns its {@link Drawable}.
401     * <p>
402     * If multiple layers are found for the given ID, returns the
403     * {@link Drawable} for the matching layer at the highest index.
404     *
405     * @param id The layer ID to search for.
406     * @return The {@link Drawable} for the highest-indexed layer that has the
407     *         given ID, or null if not found.
408     */
409    public Drawable findDrawableByLayerId(int id) {
410        final ChildDrawable[] layers = mLayerState.mChildren;
411        for (int i = mLayerState.mNum - 1; i >= 0; i--) {
412            if (layers[i].mId == id) {
413                return layers[i].mDrawable;
414            }
415        }
416
417        return null;
418    }
419
420    /**
421     * Sets the ID of a layer.
422     *
423     * @param index The index of the layer to modify, must be in the range
424     *              {@code 0...getNumberOfLayers()-1}.
425     * @param id The id to assign to the layer.
426     *
427     * @see #getId(int)
428     * @attr ref android.R.styleable#LayerDrawableItem_id
429     */
430    public void setId(int index, int id) {
431        mLayerState.mChildren[index].mId = id;
432    }
433
434    /**
435     * Returns the ID of the specified layer.
436     *
437     * @param index The index of the layer, must be in the range
438     *              {@code 0...getNumberOfLayers()-1}.
439     * @return The id of the layer or {@link android.view.View#NO_ID} if the
440     *         layer has no id.
441     *
442     * @see #setId(int, int)
443     * @attr ref android.R.styleable#LayerDrawableItem_id
444     */
445    public int getId(int index) {
446        if (index >= mLayerState.mNum) {
447            throw new IndexOutOfBoundsException();
448        }
449        return mLayerState.mChildren[index].mId;
450    }
451
452    /**
453     * Returns the number of layers contained within this layer drawable.
454     *
455     * @return The number of layers.
456     */
457    public int getNumberOfLayers() {
458        return mLayerState.mNum;
459    }
460
461    /**
462     * Replaces the {@link Drawable} for the layer with the given id.
463     *
464     * @param id The layer ID to search for.
465     * @param drawable The replacement {@link Drawable}.
466     * @return Whether the {@link Drawable} was replaced (could return false if
467     *         the id was not found).
468     */
469    public boolean setDrawableByLayerId(int id, Drawable drawable) {
470        final int index = findIndexByLayerId(id);
471        if (index < 0) {
472            return false;
473        }
474
475        setDrawable(index, drawable);
476        return true;
477    }
478
479    /**
480     * Returns the layer with the specified {@code id}.
481     * <p>
482     * If multiple layers have the same ID, returns the layer with the lowest
483     * index.
484     *
485     * @param id The ID of the layer to return.
486     * @return The index of the layer with the specified ID.
487     */
488    public int findIndexByLayerId(int id) {
489        final ChildDrawable[] layers = mLayerState.mChildren;
490        final int N = mLayerState.mNum;
491        for (int i = 0; i < N; i++) {
492            final ChildDrawable childDrawable = layers[i];
493            if (childDrawable.mId == id) {
494                return i;
495            }
496        }
497
498        return -1;
499    }
500
501    /**
502     * Sets the drawable for the layer at the specified index.
503     *
504     * @param index The index of the layer to modify, must be in the range
505     *              {@code 0...getNumberOfLayers()-1}.
506     * @param drawable The drawable to set for the layer.
507     *
508     * @see #getDrawable(int)
509     * @attr ref android.R.styleable#LayerDrawableItem_drawable
510     */
511    public void setDrawable(int index, Drawable drawable) {
512        if (index >= mLayerState.mNum) {
513            throw new IndexOutOfBoundsException();
514        }
515
516        final ChildDrawable[] layers = mLayerState.mChildren;
517        final ChildDrawable childDrawable = layers[index];
518        if (childDrawable.mDrawable != null) {
519            if (drawable != null) {
520                final Rect bounds = childDrawable.mDrawable.getBounds();
521                drawable.setBounds(bounds);
522            }
523
524            childDrawable.mDrawable.setCallback(null);
525        }
526
527        if (drawable != null) {
528            drawable.setCallback(this);
529            drawable.setLayoutDirection(getLayoutDirection());
530            drawable.setLevel(getLevel());
531        }
532
533        childDrawable.mDrawable = drawable;
534        mLayerState.invalidateCache();
535    }
536
537    /**
538     * Returns the drawable for the layer at the specified index.
539     *
540     * @param index The index of the layer, must be in the range
541     *              {@code 0...getNumberOfLayers()-1}.
542     * @return The {@link Drawable} at the specified layer index.
543     *
544     * @see #setDrawable(int, Drawable)
545     * @attr ref android.R.styleable#LayerDrawableItem_drawable
546     */
547    public Drawable getDrawable(int index) {
548        if (index >= mLayerState.mNum) {
549            throw new IndexOutOfBoundsException();
550        }
551        return mLayerState.mChildren[index].mDrawable;
552    }
553
554    /**
555     * Sets an explicit size for the specified layer.
556     * <p>
557     * <strong>Note:</strong> Setting an explicit layer size changes the
558     * default layer gravity behavior. See {@link #setLayerGravity(int, int)}
559     * for more information.
560     *
561     * @param index the index of the layer to adjust
562     * @param w width in pixels, or -1 to use the intrinsic width
563     * @param h height in pixels, or -1 to use the intrinsic height
564     * @see #getLayerWidth(int)
565     * @see #getLayerHeight(int)
566     * @attr ref android.R.styleable#LayerDrawableItem_width
567     * @attr ref android.R.styleable#LayerDrawableItem_height
568     */
569    public void setLayerSize(int index, int w, int h) {
570        final ChildDrawable childDrawable = mLayerState.mChildren[index];
571        childDrawable.mWidth = w;
572        childDrawable.mHeight = h;
573    }
574
575    /**
576     * @param index the index of the layer to adjust
577     * @param w width in pixels, or -1 to use the intrinsic width
578     * @attr ref android.R.styleable#LayerDrawableItem_width
579     */
580    public void setLayerWidth(int index, int w) {
581        final ChildDrawable childDrawable = mLayerState.mChildren[index];
582        childDrawable.mWidth = w;
583    }
584
585    /**
586     * @param index the index of the drawable to adjust
587     * @return the explicit width of the layer, or -1 if not specified
588     * @see #setLayerSize(int, int, int)
589     * @attr ref android.R.styleable#LayerDrawableItem_width
590     */
591    public int getLayerWidth(int index) {
592        final ChildDrawable childDrawable = mLayerState.mChildren[index];
593        return childDrawable.mWidth;
594    }
595
596    /**
597     * @param index the index of the layer to adjust
598     * @param h height in pixels, or -1 to use the intrinsic height
599     * @attr ref android.R.styleable#LayerDrawableItem_height
600     */
601    public void setLayerHeight(int index, int h) {
602        final ChildDrawable childDrawable = mLayerState.mChildren[index];
603        childDrawable.mHeight = h;
604    }
605
606    /**
607     * @param index the index of the drawable to adjust
608     * @return the explicit height of the layer, or -1 if not specified
609     * @see #setLayerSize(int, int, int)
610     * @attr ref android.R.styleable#LayerDrawableItem_height
611     */
612    public int getLayerHeight(int index) {
613        final ChildDrawable childDrawable = mLayerState.mChildren[index];
614        return childDrawable.mHeight;
615    }
616
617    /**
618     * Sets the gravity used to position or stretch the specified layer within
619     * its container. Gravity is applied after any layer insets (see
620     * {@link #setLayerInset(int, int, int, int, int)}) or padding (see
621     * {@link #setPaddingMode(int)}).
622     * <p>
623     * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default
624     * behavior depends on whether an explicit width or height has been set
625     * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set,
626     * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or
627     * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction
628     * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}.
629     *
630     * @param index the index of the drawable to adjust
631     * @param gravity the gravity to set for the layer
632     *
633     * @see #getLayerGravity(int)
634     * @attr ref android.R.styleable#LayerDrawableItem_gravity
635     */
636    public void setLayerGravity(int index, int gravity) {
637        final ChildDrawable childDrawable = mLayerState.mChildren[index];
638        childDrawable.mGravity = gravity;
639    }
640
641    /**
642     * @param index the index of the layer
643     * @return the gravity used to position or stretch the specified layer
644     *         within its container
645     *
646     * @see #setLayerGravity(int, int)
647     * @attr ref android.R.styleable#LayerDrawableItem_gravity
648     */
649    public int getLayerGravity(int index) {
650        final ChildDrawable childDrawable = mLayerState.mChildren[index];
651        return childDrawable.mGravity;
652    }
653
654    /**
655     * Specifies the insets in pixels for the drawable at the specified index.
656     *
657     * @param index the index of the drawable to adjust
658     * @param l number of pixels to add to the left bound
659     * @param t number of pixels to add to the top bound
660     * @param r number of pixels to subtract from the right bound
661     * @param b number of pixels to subtract from the bottom bound
662     *
663     * @attr ref android.R.styleable#LayerDrawableItem_left
664     * @attr ref android.R.styleable#LayerDrawableItem_top
665     * @attr ref android.R.styleable#LayerDrawableItem_right
666     * @attr ref android.R.styleable#LayerDrawableItem_bottom
667     */
668    public void setLayerInset(int index, int l, int t, int r, int b) {
669        setLayerInsetInternal(index, l, t, r, b, UNDEFINED_INSET, UNDEFINED_INSET);
670    }
671
672    /**
673     * Specifies the relative insets in pixels for the drawable at the
674     * specified index.
675     *
676     * @param index the index of the layer to adjust
677     * @param s number of pixels to inset from the start bound
678     * @param t number of pixels to inset from the top bound
679     * @param e number of pixels to inset from the end bound
680     * @param b number of pixels to inset from the bottom bound
681     *
682     * @attr ref android.R.styleable#LayerDrawableItem_start
683     * @attr ref android.R.styleable#LayerDrawableItem_top
684     * @attr ref android.R.styleable#LayerDrawableItem_end
685     * @attr ref android.R.styleable#LayerDrawableItem_bottom
686     */
687    public void setLayerInsetRelative(int index, int s, int t, int e, int b) {
688        setLayerInsetInternal(index, 0, t, 0, b, s, e);
689    }
690
691    /**
692     * @param index the index of the layer to adjust
693     * @param l number of pixels to inset from the left bound
694     * @attr ref android.R.styleable#LayerDrawableItem_left
695     */
696    public void setLayerInsetLeft(int index, int l) {
697        final ChildDrawable childDrawable = mLayerState.mChildren[index];
698        childDrawable.mInsetL = l;
699    }
700
701    /**
702     * @param index the index of the layer
703     * @return number of pixels to inset from the left bound
704     * @attr ref android.R.styleable#LayerDrawableItem_left
705     */
706    public int getLayerInsetLeft(int index) {
707        final ChildDrawable childDrawable = mLayerState.mChildren[index];
708        return childDrawable.mInsetL;
709    }
710
711    /**
712     * @param index the index of the layer to adjust
713     * @param r number of pixels to inset from the right bound
714     * @attr ref android.R.styleable#LayerDrawableItem_right
715     */
716    public void setLayerInsetRight(int index, int r) {
717        final ChildDrawable childDrawable = mLayerState.mChildren[index];
718        childDrawable.mInsetR = r;
719    }
720
721    /**
722     * @param index the index of the layer
723     * @return number of pixels to inset from the right bound
724     * @attr ref android.R.styleable#LayerDrawableItem_right
725     */
726    public int getLayerInsetRight(int index) {
727        final ChildDrawable childDrawable = mLayerState.mChildren[index];
728        return childDrawable.mInsetR;
729    }
730
731    /**
732     * @param index the index of the layer to adjust
733     * @param t number of pixels to inset from the top bound
734     * @attr ref android.R.styleable#LayerDrawableItem_top
735     */
736    public void setLayerInsetTop(int index, int t) {
737        final ChildDrawable childDrawable = mLayerState.mChildren[index];
738        childDrawable.mInsetT = t;
739    }
740
741    /**
742     * @param index the index of the layer
743     * @return number of pixels to inset from the top bound
744     * @attr ref android.R.styleable#LayerDrawableItem_top
745     */
746    public int getLayerInsetTop(int index) {
747        final ChildDrawable childDrawable = mLayerState.mChildren[index];
748        return childDrawable.mInsetT;
749    }
750
751    /**
752     * @param index the index of the layer to adjust
753     * @param b number of pixels to inset from the bottom bound
754     * @attr ref android.R.styleable#LayerDrawableItem_bottom
755     */
756    public void setLayerInsetBottom(int index, int b) {
757        final ChildDrawable childDrawable = mLayerState.mChildren[index];
758        childDrawable.mInsetB = b;
759    }
760
761    /**
762     * @param index the index of the layer
763     * @return number of pixels to inset from the bottom bound
764     * @attr ref android.R.styleable#LayerDrawableItem_bottom
765     */
766    public int getLayerInsetBottom(int index) {
767        final ChildDrawable childDrawable = mLayerState.mChildren[index];
768        return childDrawable.mInsetB;
769    }
770
771    /**
772     * @param index the index of the layer to adjust
773     * @param s number of pixels to inset from the start bound
774     * @attr ref android.R.styleable#LayerDrawableItem_start
775     */
776    public void setLayerInsetStart(int index, int s) {
777        final ChildDrawable childDrawable = mLayerState.mChildren[index];
778        childDrawable.mInsetS = s;
779    }
780
781    /**
782     * @param index the index of the layer
783     * @return number of pixels to inset from the start bound
784     * @attr ref android.R.styleable#LayerDrawableItem_start
785     */
786    public int getLayerInsetStart(int index) {
787        final ChildDrawable childDrawable = mLayerState.mChildren[index];
788        return childDrawable.mInsetS;
789    }
790
791    /**
792     * @param index the index of the layer to adjust
793     * @param e number of pixels to inset from the end bound
794     * @attr ref android.R.styleable#LayerDrawableItem_end
795     */
796    public void setLayerInsetEnd(int index, int e) {
797        final ChildDrawable childDrawable = mLayerState.mChildren[index];
798        childDrawable.mInsetE = e;
799    }
800
801    /**
802     * @param index the index of the layer
803     * @return number of pixels to inset from the end bound
804     * @attr ref android.R.styleable#LayerDrawableItem_end
805     */
806    public int getLayerInsetEnd(int index) {
807        final ChildDrawable childDrawable = mLayerState.mChildren[index];
808        return childDrawable.mInsetE;
809    }
810
811    private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) {
812        final ChildDrawable childDrawable = mLayerState.mChildren[index];
813        childDrawable.mInsetL = l;
814        childDrawable.mInsetT = t;
815        childDrawable.mInsetR = r;
816        childDrawable.mInsetB = b;
817        childDrawable.mInsetS = s;
818        childDrawable.mInsetE = e;
819    }
820
821    /**
822     * Specifies how layer padding should affect the bounds of subsequent
823     * layers. The default value is {@link #PADDING_MODE_NEST}.
824     *
825     * @param mode padding mode, one of:
826     *            <ul>
827     *            <li>{@link #PADDING_MODE_NEST} to nest each layer inside the
828     *            padding of the previous layer
829     *            <li>{@link #PADDING_MODE_STACK} to stack each layer directly
830     *            atop the previous layer
831     *            </ul>
832     *
833     * @see #getPaddingMode()
834     * @attr ref android.R.styleable#LayerDrawable_paddingMode
835     */
836    public void setPaddingMode(int mode) {
837        if (mLayerState.mPaddingMode != mode) {
838            mLayerState.mPaddingMode = mode;
839        }
840    }
841
842    /**
843     * @return the current padding mode
844     *
845     * @see #setPaddingMode(int)
846     * @attr ref android.R.styleable#LayerDrawable_paddingMode
847     */
848    public int getPaddingMode() {
849      return mLayerState.mPaddingMode;
850    }
851
852    @Override
853    public void invalidateDrawable(Drawable who) {
854        invalidateSelf();
855    }
856
857    @Override
858    public void scheduleDrawable(Drawable who, Runnable what, long when) {
859        scheduleSelf(what, when);
860    }
861
862    @Override
863    public void unscheduleDrawable(Drawable who, Runnable what) {
864        unscheduleSelf(what);
865    }
866
867    @Override
868    public void draw(Canvas canvas) {
869        final ChildDrawable[] array = mLayerState.mChildren;
870        final int N = mLayerState.mNum;
871        for (int i = 0; i < N; i++) {
872            array[i].mDrawable.draw(canvas);
873        }
874    }
875
876    @Override
877    public int getChangingConfigurations() {
878        return super.getChangingConfigurations()
879                | mLayerState.mChangingConfigurations
880                | mLayerState.mChildrenChangingConfigurations;
881    }
882
883    @Override
884    public boolean getPadding(Rect padding) {
885        if (mLayerState.mPaddingMode == PADDING_MODE_NEST) {
886            computeNestedPadding(padding);
887        } else {
888            computeStackedPadding(padding);
889        }
890
891        return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
892    }
893
894    private void computeNestedPadding(Rect padding) {
895        padding.left = 0;
896        padding.top = 0;
897        padding.right = 0;
898        padding.bottom = 0;
899
900        // Add all the padding.
901        final ChildDrawable[] array = mLayerState.mChildren;
902        final int N = mLayerState.mNum;
903        for (int i = 0; i < N; i++) {
904            refreshChildPadding(i, array[i]);
905
906            padding.left += mPaddingL[i];
907            padding.top += mPaddingT[i];
908            padding.right += mPaddingR[i];
909            padding.bottom += mPaddingB[i];
910        }
911    }
912
913    private void computeStackedPadding(Rect padding) {
914        padding.left = 0;
915        padding.top = 0;
916        padding.right = 0;
917        padding.bottom = 0;
918
919        // Take the max padding.
920        final ChildDrawable[] array = mLayerState.mChildren;
921        final int N = mLayerState.mNum;
922        for (int i = 0; i < N; i++) {
923            refreshChildPadding(i, array[i]);
924
925            padding.left = Math.max(padding.left, mPaddingL[i]);
926            padding.top = Math.max(padding.top, mPaddingT[i]);
927            padding.right = Math.max(padding.right, mPaddingR[i]);
928            padding.bottom = Math.max(padding.bottom, mPaddingB[i]);
929        }
930    }
931
932    /**
933     * Populates <code>outline</code> with the first available (non-empty) layer outline.
934     *
935     * @param outline Outline in which to place the first available layer outline
936     */
937    @Override
938    public void getOutline(@NonNull Outline outline) {
939        final LayerState state = mLayerState;
940        final ChildDrawable[] children = state.mChildren;
941        final int N = state.mNum;
942        for (int i = 0; i < N; i++) {
943            children[i].mDrawable.getOutline(outline);
944            if (!outline.isEmpty()) {
945                return;
946            }
947        }
948    }
949
950    @Override
951    public void setHotspot(float x, float y) {
952        final ChildDrawable[] array = mLayerState.mChildren;
953        final int N = mLayerState.mNum;
954        for (int i = 0; i < N; i++) {
955            array[i].mDrawable.setHotspot(x, y);
956        }
957    }
958
959    @Override
960    public void setHotspotBounds(int left, int top, int right, int bottom) {
961        final ChildDrawable[] array = mLayerState.mChildren;
962        final int N = mLayerState.mNum;
963        for (int i = 0; i < N; i++) {
964            array[i].mDrawable.setHotspotBounds(left, top, right, bottom);
965        }
966
967        if (mHotspotBounds == null) {
968            mHotspotBounds = new Rect(left, top, right, bottom);
969        } else {
970            mHotspotBounds.set(left, top, right, bottom);
971        }
972    }
973
974    @Override
975    public void getHotspotBounds(Rect outRect) {
976        if (mHotspotBounds != null) {
977            outRect.set(mHotspotBounds);
978        } else {
979            super.getHotspotBounds(outRect);
980        }
981    }
982
983    @Override
984    public boolean setVisible(boolean visible, boolean restart) {
985        final boolean changed = super.setVisible(visible, restart);
986        final ChildDrawable[] array = mLayerState.mChildren;
987        final int N = mLayerState.mNum;
988        for (int i = 0; i < N; i++) {
989            array[i].mDrawable.setVisible(visible, restart);
990        }
991
992        return changed;
993    }
994
995    @Override
996    public void setDither(boolean dither) {
997        final ChildDrawable[] array = mLayerState.mChildren;
998        final int N = mLayerState.mNum;
999        for (int i = 0; i < N; i++) {
1000            array[i].mDrawable.setDither(dither);
1001        }
1002    }
1003
1004    @Override
1005    public boolean getDither() {
1006        final ChildDrawable[] array = mLayerState.mChildren;
1007        if (mLayerState.mNum > 0) {
1008            // All layers should have the same dither set on them - just return
1009            // the first one
1010            return array[0].mDrawable.getDither();
1011        } else {
1012            return super.getDither();
1013        }
1014    }
1015
1016    @Override
1017    public void setAlpha(int alpha) {
1018        final ChildDrawable[] array = mLayerState.mChildren;
1019        final int N = mLayerState.mNum;
1020        for (int i = 0; i < N; i++) {
1021            array[i].mDrawable.setAlpha(alpha);
1022        }
1023    }
1024
1025    @Override
1026    public int getAlpha() {
1027        final ChildDrawable[] array = mLayerState.mChildren;
1028        if (mLayerState.mNum > 0) {
1029            // All layers should have the same alpha set on them - just return
1030            // the first one
1031            return array[0].mDrawable.getAlpha();
1032        } else {
1033            return super.getAlpha();
1034        }
1035    }
1036
1037    @Override
1038    public void setColorFilter(ColorFilter colorFilter) {
1039        final ChildDrawable[] array = mLayerState.mChildren;
1040        final int N = mLayerState.mNum;
1041        for (int i = 0; i < N; i++) {
1042            array[i].mDrawable.setColorFilter(colorFilter);
1043        }
1044    }
1045
1046    @Override
1047    public void setTintList(ColorStateList tint) {
1048        final ChildDrawable[] array = mLayerState.mChildren;
1049        final int N = mLayerState.mNum;
1050        for (int i = 0; i < N; i++) {
1051            array[i].mDrawable.setTintList(tint);
1052        }
1053    }
1054
1055    @Override
1056    public void setTintMode(Mode tintMode) {
1057        final ChildDrawable[] array = mLayerState.mChildren;
1058        final int N = mLayerState.mNum;
1059        for (int i = 0; i < N; i++) {
1060            array[i].mDrawable.setTintMode(tintMode);
1061        }
1062    }
1063
1064    /**
1065     * Sets the opacity of this drawable directly, instead of collecting the
1066     * states from the layers
1067     *
1068     * @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN
1069     *            PixelFormat.UNKNOWN} for the default behavior
1070     * @see PixelFormat#UNKNOWN
1071     * @see PixelFormat#TRANSLUCENT
1072     * @see PixelFormat#TRANSPARENT
1073     * @see PixelFormat#OPAQUE
1074     */
1075    public void setOpacity(int opacity) {
1076        mOpacityOverride = opacity;
1077    }
1078
1079    @Override
1080    public int getOpacity() {
1081        if (mOpacityOverride != PixelFormat.UNKNOWN) {
1082            return mOpacityOverride;
1083        }
1084        return mLayerState.getOpacity();
1085    }
1086
1087    @Override
1088    public void setAutoMirrored(boolean mirrored) {
1089        mLayerState.mAutoMirrored = mirrored;
1090
1091        final ChildDrawable[] array = mLayerState.mChildren;
1092        final int N = mLayerState.mNum;
1093        for (int i = 0; i < N; i++) {
1094            array[i].mDrawable.setAutoMirrored(mirrored);
1095        }
1096    }
1097
1098    @Override
1099    public boolean isAutoMirrored() {
1100        return mLayerState.mAutoMirrored;
1101    }
1102
1103    @Override
1104    public boolean isStateful() {
1105        return mLayerState.isStateful();
1106    }
1107
1108    @Override
1109    protected boolean onStateChange(int[] state) {
1110        boolean paddingChanged = false;
1111        boolean changed = false;
1112
1113        final ChildDrawable[] array = mLayerState.mChildren;
1114        final int N = mLayerState.mNum;
1115        for (int i = 0; i < N; i++) {
1116            final ChildDrawable r = array[i];
1117            if (r.mDrawable.isStateful() && r.mDrawable.setState(state)) {
1118                changed = true;
1119            }
1120
1121            if (refreshChildPadding(i, r)) {
1122                paddingChanged = true;
1123            }
1124        }
1125
1126        if (paddingChanged) {
1127            updateLayerBounds(getBounds());
1128        }
1129
1130        return changed;
1131    }
1132
1133    @Override
1134    protected boolean onLevelChange(int level) {
1135        boolean paddingChanged = false;
1136        boolean changed = false;
1137
1138        final ChildDrawable[] array = mLayerState.mChildren;
1139        final int N = mLayerState.mNum;
1140        for (int i = 0; i < N; i++) {
1141            final ChildDrawable r = array[i];
1142            if (r.mDrawable.setLevel(level)) {
1143                changed = true;
1144            }
1145
1146            if (refreshChildPadding(i, r)) {
1147                paddingChanged = true;
1148            }
1149        }
1150
1151        if (paddingChanged) {
1152            updateLayerBounds(getBounds());
1153        }
1154
1155        return changed;
1156    }
1157
1158    @Override
1159    protected void onBoundsChange(Rect bounds) {
1160        updateLayerBounds(bounds);
1161    }
1162
1163    private void updateLayerBounds(Rect bounds) {
1164        int padL = 0;
1165        int padT = 0;
1166        int padR = 0;
1167        int padB = 0;
1168
1169        final Rect outRect = mTmpOutRect;
1170        final int layoutDirection = getLayoutDirection();
1171        final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1172        final ChildDrawable[] array = mLayerState.mChildren;
1173        final int N = mLayerState.mNum;
1174        for (int i = 0; i < N; i++) {
1175            final ChildDrawable r = array[i];
1176            final Drawable d = r.mDrawable;
1177            final Rect container = mTmpContainer;
1178            container.set(d.getBounds());
1179
1180            // Take the resolved layout direction into account. If start / end
1181            // padding are defined, they will be resolved (hence overriding) to
1182            // left / right or right / left depending on the resolved layout
1183            // direction. If start / end padding are not defined, use the
1184            // left / right ones.
1185            final int insetL, insetR;
1186            if (layoutDirection == LayoutDirection.RTL) {
1187                insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
1188                insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
1189            } else {
1190                insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
1191                insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
1192            }
1193
1194            // Establish containing region based on aggregate padding and
1195            // requested insets for the current layer.
1196            container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT,
1197                    bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB);
1198
1199            // Apply resolved gravity to drawable based on resolved size.
1200            final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight);
1201            final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth;
1202            final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight;
1203            Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
1204            d.setBounds(outRect);
1205
1206            if (nest) {
1207                padL += mPaddingL[i];
1208                padR += mPaddingR[i];
1209                padT += mPaddingT[i];
1210                padB += mPaddingB[i];
1211            }
1212        }
1213    }
1214
1215    /**
1216     * Resolves layer gravity given explicit gravity and dimensions.
1217     * <p>
1218     * If the client hasn't specified a gravity but has specified an explicit
1219     * dimension, defaults to START or TOP. Otherwise, defaults to FILL to
1220     * preserve legacy behavior.
1221     *
1222     * @param gravity
1223     * @param width
1224     * @param height
1225     * @return
1226     */
1227    private int resolveGravity(int gravity, int width, int height) {
1228        if (!Gravity.isHorizontal(gravity)) {
1229            if (width < 0) {
1230                gravity |= Gravity.FILL_HORIZONTAL;
1231            } else {
1232                gravity |= Gravity.START;
1233            }
1234        }
1235
1236        if (!Gravity.isVertical(gravity)) {
1237            if (height < 0) {
1238                gravity |= Gravity.FILL_VERTICAL;
1239            } else {
1240                gravity |= Gravity.TOP;
1241            }
1242        }
1243
1244        return gravity;
1245    }
1246
1247    @Override
1248    public int getIntrinsicWidth() {
1249        int width = -1;
1250        int padL = 0;
1251        int padR = 0;
1252
1253        final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1254        final ChildDrawable[] array = mLayerState.mChildren;
1255        final int N = mLayerState.mNum;
1256        for (int i = 0; i < N; i++) {
1257            final ChildDrawable r = array[i];
1258            final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth;
1259            final int w = minWidth + r.mInsetL + r.mInsetR + padL + padR;
1260            if (w > width) {
1261                width = w;
1262            }
1263
1264            if (nest) {
1265                padL += mPaddingL[i];
1266                padR += mPaddingR[i];
1267            }
1268        }
1269
1270        return width;
1271    }
1272
1273    @Override
1274    public int getIntrinsicHeight() {
1275        int height = -1;
1276        int padT = 0;
1277        int padB = 0;
1278
1279        final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1280        final ChildDrawable[] array = mLayerState.mChildren;
1281        final int N = mLayerState.mNum;
1282        for (int i = 0; i < N; i++) {
1283            final ChildDrawable r = array[i];
1284            final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight;
1285            final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB;
1286            if (h > height) {
1287                height = h;
1288            }
1289
1290            if (nest) {
1291                padT += mPaddingT[i];
1292                padB += mPaddingB[i];
1293            }
1294        }
1295
1296        return height;
1297    }
1298
1299    /**
1300     * Refreshes the cached padding values for the specified child.
1301     *
1302     * @return true if the child's padding has changed
1303     */
1304    private boolean refreshChildPadding(int i, ChildDrawable r) {
1305        final Rect rect = mTmpRect;
1306        r.mDrawable.getPadding(rect);
1307        if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] ||
1308                rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
1309            mPaddingL[i] = rect.left;
1310            mPaddingT[i] = rect.top;
1311            mPaddingR[i] = rect.right;
1312            mPaddingB[i] = rect.bottom;
1313            return true;
1314        }
1315        return false;
1316    }
1317
1318    /**
1319     * Ensures the child padding caches are large enough.
1320     */
1321    void ensurePadding() {
1322        final int N = mLayerState.mNum;
1323        if (mPaddingL != null && mPaddingL.length >= N) {
1324            return;
1325        }
1326
1327        mPaddingL = new int[N];
1328        mPaddingT = new int[N];
1329        mPaddingR = new int[N];
1330        mPaddingB = new int[N];
1331    }
1332
1333    @Override
1334    public ConstantState getConstantState() {
1335        if (mLayerState.canConstantState()) {
1336            mLayerState.mChangingConfigurations = getChangingConfigurations();
1337            return mLayerState;
1338        }
1339        return null;
1340    }
1341
1342    @Override
1343    public Drawable mutate() {
1344        if (!mMutated && super.mutate() == this) {
1345            mLayerState = createConstantState(mLayerState, null);
1346            final ChildDrawable[] array = mLayerState.mChildren;
1347            final int N = mLayerState.mNum;
1348            for (int i = 0; i < N; i++) {
1349                array[i].mDrawable.mutate();
1350            }
1351            mMutated = true;
1352        }
1353        return this;
1354    }
1355
1356    /**
1357     * @hide
1358     */
1359    public void clearMutated() {
1360        super.clearMutated();
1361        final ChildDrawable[] array = mLayerState.mChildren;
1362        final int N = mLayerState.mNum;
1363        for (int i = 0; i < N; i++) {
1364            array[i].mDrawable.clearMutated();
1365        }
1366        mMutated = false;
1367    }
1368
1369    @Override
1370    public boolean onLayoutDirectionChange(int layoutDirection) {
1371        boolean changed = false;
1372        final ChildDrawable[] array = mLayerState.mChildren;
1373        final int N = mLayerState.mNum;
1374        for (int i = 0; i < N; i++) {
1375            changed |= array[i].mDrawable.setLayoutDirection(layoutDirection);
1376        }
1377        updateLayerBounds(getBounds());
1378        return changed;
1379    }
1380
1381    static class ChildDrawable {
1382        public Drawable mDrawable;
1383        public int[] mThemeAttrs;
1384        public int mInsetL, mInsetT, mInsetR, mInsetB;
1385        public int mInsetS = UNDEFINED_INSET;
1386        public int mInsetE = UNDEFINED_INSET;
1387        public int mWidth = -1;
1388        public int mHeight = -1;
1389        public int mGravity = Gravity.NO_GRAVITY;
1390        public int mId = View.NO_ID;
1391
1392        ChildDrawable() {
1393            // Default empty constructor.
1394        }
1395
1396        ChildDrawable(ChildDrawable orig, LayerDrawable owner, Resources res) {
1397            if (res != null) {
1398                mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
1399            } else {
1400                mDrawable = orig.mDrawable.getConstantState().newDrawable();
1401            }
1402            mDrawable.setCallback(owner);
1403            mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
1404            mDrawable.setBounds(orig.mDrawable.getBounds());
1405            mDrawable.setLevel(orig.mDrawable.getLevel());
1406            mThemeAttrs = orig.mThemeAttrs;
1407            mInsetL = orig.mInsetL;
1408            mInsetT = orig.mInsetT;
1409            mInsetR = orig.mInsetR;
1410            mInsetB = orig.mInsetB;
1411            mInsetS = orig.mInsetS;
1412            mInsetE = orig.mInsetE;
1413            mWidth = orig.mWidth;
1414            mHeight = orig.mHeight;
1415            mGravity = orig.mGravity;
1416            mId = orig.mId;
1417        }
1418    }
1419
1420    static class LayerState extends ConstantState {
1421        int mNum;
1422        ChildDrawable[] mChildren;
1423        int[] mThemeAttrs;
1424
1425        int mChangingConfigurations;
1426        int mChildrenChangingConfigurations;
1427
1428        private boolean mHaveOpacity;
1429        private int mOpacity;
1430
1431        private boolean mHaveIsStateful;
1432        private boolean mIsStateful;
1433
1434        private boolean mAutoMirrored = false;
1435
1436        private int mPaddingMode = PADDING_MODE_NEST;
1437
1438        LayerState(LayerState orig, LayerDrawable owner, Resources res) {
1439            if (orig != null) {
1440                final ChildDrawable[] origChildDrawable = orig.mChildren;
1441                final int N = orig.mNum;
1442
1443                mNum = N;
1444                mChildren = new ChildDrawable[N];
1445
1446                mChangingConfigurations = orig.mChangingConfigurations;
1447                mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
1448
1449                for (int i = 0; i < N; i++) {
1450                    final ChildDrawable or = origChildDrawable[i];
1451                    mChildren[i] = new ChildDrawable(or, owner, res);
1452                }
1453
1454                mHaveOpacity = orig.mHaveOpacity;
1455                mOpacity = orig.mOpacity;
1456                mHaveIsStateful = orig.mHaveIsStateful;
1457                mIsStateful = orig.mIsStateful;
1458                mAutoMirrored = orig.mAutoMirrored;
1459                mPaddingMode = orig.mPaddingMode;
1460                mThemeAttrs = orig.mThemeAttrs;
1461            } else {
1462                mNum = 0;
1463                mChildren = null;
1464            }
1465        }
1466
1467        @Override
1468        public boolean canApplyTheme() {
1469            if (mThemeAttrs != null || super.canApplyTheme()) {
1470                return true;
1471            }
1472
1473            final ChildDrawable[] array = mChildren;
1474            final int N = mNum;
1475            for (int i = 0; i < N; i++) {
1476                final ChildDrawable layer = array[i];
1477                if (layer.mThemeAttrs != null || layer.mDrawable.canApplyTheme()) {
1478                    return true;
1479                }
1480            }
1481
1482            return false;
1483        }
1484
1485        @Override
1486        public Drawable newDrawable() {
1487            return new LayerDrawable(this, null);
1488        }
1489
1490        @Override
1491        public Drawable newDrawable(Resources res) {
1492            return new LayerDrawable(this, res);
1493        }
1494
1495        @Override
1496        public int getChangingConfigurations() {
1497            return mChangingConfigurations;
1498        }
1499
1500        public final int getOpacity() {
1501            if (mHaveOpacity) {
1502                return mOpacity;
1503            }
1504
1505            final ChildDrawable[] array = mChildren;
1506            final int N = mNum;
1507            int op = N > 0 ? array[0].mDrawable.getOpacity() : PixelFormat.TRANSPARENT;
1508            for (int i = 1; i < N; i++) {
1509                op = Drawable.resolveOpacity(op, array[i].mDrawable.getOpacity());
1510            }
1511
1512            mOpacity = op;
1513            mHaveOpacity = true;
1514            return op;
1515        }
1516
1517        public final boolean isStateful() {
1518            if (mHaveIsStateful) {
1519                return mIsStateful;
1520            }
1521
1522            final ChildDrawable[] array = mChildren;
1523            final int N = mNum;
1524            boolean isStateful = false;
1525            for (int i = 0; i < N; i++) {
1526                if (array[i].mDrawable.isStateful()) {
1527                    isStateful = true;
1528                    break;
1529                }
1530            }
1531
1532            mIsStateful = isStateful;
1533            mHaveIsStateful = true;
1534            return isStateful;
1535        }
1536
1537        public final boolean canConstantState() {
1538            final ChildDrawable[] array = mChildren;
1539            final int N = mNum;
1540            for (int i = 0; i < N; i++) {
1541                if (array[i].mDrawable.getConstantState() == null) {
1542                    return false;
1543                }
1544            }
1545
1546            // Don't cache the result, this method is not called very often.
1547            return true;
1548        }
1549
1550        public void invalidateCache() {
1551            mHaveOpacity = false;
1552            mHaveIsStateful = false;
1553        }
1554
1555        @Override
1556        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
1557            final ChildDrawable[] array = mChildren;
1558            final int N = mNum;
1559            int pixelCount = 0;
1560            for (int i = 0; i < N; i++) {
1561                final ConstantState state = array[i].mDrawable.getConstantState();
1562                if (state != null) {
1563                    pixelCount += state.addAtlasableBitmaps(atlasList);
1564                }
1565            }
1566            return pixelCount;
1567        }
1568    }
1569}
1570
1571