StateListDrawable.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 org.xmlpull.v1.XmlPullParser;
20import org.xmlpull.v1.XmlPullParserException;
21
22import java.io.IOException;
23
24import android.content.res.Resources;
25import android.content.res.TypedArray;
26import android.util.AttributeSet;
27import android.util.StateSet;
28
29/**
30 *
31 * Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string
32 * ID value.
33 *
34 */
35public class StateListDrawable extends DrawableContainer {
36    public StateListDrawable()
37    {
38        this(null);
39    }
40
41    /**
42     * Add a new image/string ID to the set of images.
43     * @param stateSet - An array of resource Ids to associate with the image.
44     * Switch to this image by calling setState().
45     * @param drawable -The image to show.
46     */
47    public void addState(int[] stateSet, Drawable drawable) {
48        if (drawable != null) {
49            mStateListState.addStateSet(stateSet, drawable);
50            // in case the new state matches our current state...
51            onStateChange(getState());
52        }
53    }
54
55    @Override
56    public boolean isStateful() {
57        return true;
58    }
59
60    @Override
61    protected boolean onStateChange(int[] stateSet) {
62        int idx = mStateListState.indexOfStateSet(stateSet);
63        if (idx < 0) {
64            idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
65        }
66        if (selectDrawable(idx)) {
67            return true;
68        }
69        return super.onStateChange(stateSet);
70    }
71
72    @Override public void inflate(Resources r, XmlPullParser parser,
73            AttributeSet attrs)
74    throws XmlPullParserException, IOException {
75
76        TypedArray a = r.obtainAttributes(attrs,
77                com.android.internal.R.styleable.StateListDrawable);
78
79        super.inflateWithAttributes(r, parser, a,
80                com.android.internal.R.styleable.StateListDrawable_visible);
81
82        mStateListState.setVariablePadding(a.getBoolean(
83                com.android.internal.R.styleable.StateListDrawable_variablePadding, false));
84        mStateListState.setConstantSize(a.getBoolean(
85                com.android.internal.R.styleable.StateListDrawable_constantSize, false));
86
87        a.recycle();
88
89        int type;
90
91        final int innerDepth = parser.getDepth()+1;
92        int depth;
93        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
94               && ((depth=parser.getDepth()) >= innerDepth
95                       || type != XmlPullParser.END_TAG)) {
96            if (type != XmlPullParser.START_TAG) {
97                continue;
98            }
99
100            if (depth > innerDepth || !parser.getName().equals("item")) {
101                continue;
102            }
103
104            int drawableRes = 0;
105
106            int i;
107            int j = 0;
108            final int numAttrs = attrs.getAttributeCount();
109            int[] states = new int[numAttrs];
110            for (i = 0; i < numAttrs; i++) {
111                final int stateResId = attrs.getAttributeNameResource(i);
112                if (stateResId == 0) break;
113                if (stateResId == com.android.internal.R.attr.drawable) {
114                    drawableRes = attrs.getAttributeResourceValue(i, 0);
115                } else {
116                    states[j++] = attrs.getAttributeBooleanValue(i, false)
117                                  ? stateResId
118                                  : -stateResId;
119                }
120            }
121            states = StateSet.trimStateSet(states, j);
122
123            Drawable dr;
124            if (drawableRes != 0) {
125                dr = r.getDrawable(drawableRes);
126            } else {
127                while ((type=parser.next()) == XmlPullParser.TEXT) {
128                }
129                if (type != XmlPullParser.START_TAG) {
130                    throw new XmlPullParserException(
131                            parser.getPositionDescription()
132                            + ": <item> tag requires a 'drawable' attribute or "
133                            + "child tag defining a drawable");
134                }
135                dr = Drawable.createFromXmlInner(r, parser, attrs);
136            }
137
138            mStateListState.addStateSet(states, dr);
139        }
140
141        onStateChange(getState());
142    }
143
144    StateListState getStateListState() {
145        return mStateListState;
146    }
147
148    static final class StateListState extends DrawableContainerState
149    {
150        StateListState(StateListState orig, StateListDrawable owner)
151        {
152            super(orig, owner);
153
154            if (orig != null) {
155                mStateSets = orig.mStateSets;
156            } else {
157                mStateSets = new int[getChildren().length][];
158            }
159        }
160
161        int addStateSet(int[] stateSet, Drawable drawable) {
162            final int pos = addChild(drawable);
163            mStateSets[pos] = stateSet;
164            return pos;
165        }
166
167        private int indexOfStateSet(int[] stateSet)
168        {
169            final int[][] stateSets = mStateSets;
170            final int N = getChildCount();
171            for (int i=0; i<N; i++) {
172                if (StateSet.stateSetMatches(stateSets[i], stateSet)) {
173                    return i;
174                }
175            }
176            return -1;
177        }
178
179        @Override
180        public Drawable newDrawable()
181        {
182            return new StateListDrawable(this);
183        }
184
185        @Override
186        public void growArray(int oldSize, int newSize)
187        {
188            super.growArray(oldSize, newSize);
189            final int[][] newStateSets = new int[newSize][];
190            System.arraycopy(mStateSets, 0, newStateSets, 0, oldSize);
191            mStateSets = newStateSets;
192        }
193
194        private int[][]         mStateSets;
195    }
196
197    private StateListDrawable(StateListState state)
198    {
199        StateListState as = new StateListState(state, this);
200        mStateListState = as;
201        setConstantState(as);
202        onStateChange(getState());
203    }
204
205    private final StateListState mStateListState;
206}
207
208