StateListDrawable.java revision 076357b8567458d4b6dfdcf839ef751634cd2bfb
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 * Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string 31 * ID value. 32 * <p/> 33 * <p>It can be defined in an XML file with the <code><selector></code> element. 34 * Each state Drawable is defined in a nested <code><item></code> element.</p> 35 * 36 * @attr ref android.R.styleable#StateListDrawable_visible 37 * @attr ref android.R.styleable#StateListDrawable_variablePadding 38 * @attr ref android.R.styleable#StateListDrawable_constantSize 39 * @attr ref android.R.styleable#DrawableStates_state_focused 40 * @attr ref android.R.styleable#DrawableStates_state_window_focused 41 * @attr ref android.R.styleable#DrawableStates_state_enabled 42 * @attr ref android.R.styleable#DrawableStates_state_checkable 43 * @attr ref android.R.styleable#DrawableStates_state_checked 44 * @attr ref android.R.styleable#DrawableStates_state_selected 45 * @attr ref android.R.styleable#DrawableStates_state_active 46 * @attr ref android.R.styleable#DrawableStates_state_single 47 * @attr ref android.R.styleable#DrawableStates_state_first 48 * @attr ref android.R.styleable#DrawableStates_state_middle 49 * @attr ref android.R.styleable#DrawableStates_state_last 50 * @attr ref android.R.styleable#DrawableStates_state_pressed 51 */ 52public class StateListDrawable extends DrawableContainer { 53 private final StateListState mStateListState; 54 private boolean mMutated; 55 56 public StateListDrawable() { 57 this(null); 58 } 59 60 /** 61 * Add a new image/string ID to the set of images. 62 * 63 * @param stateSet - An array of resource Ids to associate with the image. 64 * Switch to this image by calling setState(). 65 * @param drawable -The image to show. 66 */ 67 public void addState(int[] stateSet, Drawable drawable) { 68 if (drawable != null) { 69 mStateListState.addStateSet(stateSet, drawable); 70 // in case the new state matches our current state... 71 onStateChange(getState()); 72 } 73 } 74 75 @Override 76 public boolean isStateful() { 77 return true; 78 } 79 80 @Override 81 protected boolean onStateChange(int[] stateSet) { 82 int idx = mStateListState.indexOfStateSet(stateSet); 83 if (idx < 0) { 84 idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD); 85 } 86 if (selectDrawable(idx)) { 87 return true; 88 } 89 return super.onStateChange(stateSet); 90 } 91 92 @Override 93 public void inflate(Resources r, XmlPullParser parser, 94 AttributeSet attrs) 95 throws XmlPullParserException, IOException { 96 97 TypedArray a = r.obtainAttributes(attrs, 98 com.android.internal.R.styleable.StateListDrawable); 99 100 super.inflateWithAttributes(r, parser, a, 101 com.android.internal.R.styleable.StateListDrawable_visible); 102 103 mStateListState.setVariablePadding(a.getBoolean( 104 com.android.internal.R.styleable.StateListDrawable_variablePadding, false)); 105 mStateListState.setConstantSize(a.getBoolean( 106 com.android.internal.R.styleable.StateListDrawable_constantSize, false)); 107 108 a.recycle(); 109 110 int type; 111 112 final int innerDepth = parser.getDepth() + 1; 113 int depth; 114 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 115 && ((depth = parser.getDepth()) >= innerDepth 116 || type != XmlPullParser.END_TAG)) { 117 if (type != XmlPullParser.START_TAG) { 118 continue; 119 } 120 121 if (depth > innerDepth || !parser.getName().equals("item")) { 122 continue; 123 } 124 125 int drawableRes = 0; 126 127 int i; 128 int j = 0; 129 final int numAttrs = attrs.getAttributeCount(); 130 int[] states = new int[numAttrs]; 131 for (i = 0; i < numAttrs; i++) { 132 final int stateResId = attrs.getAttributeNameResource(i); 133 if (stateResId == 0) break; 134 if (stateResId == com.android.internal.R.attr.drawable) { 135 drawableRes = attrs.getAttributeResourceValue(i, 0); 136 } else { 137 states[j++] = attrs.getAttributeBooleanValue(i, false) 138 ? stateResId 139 : -stateResId; 140 } 141 } 142 states = StateSet.trimStateSet(states, j); 143 144 Drawable dr; 145 if (drawableRes != 0) { 146 dr = r.getDrawable(drawableRes); 147 } else { 148 while ((type = parser.next()) == XmlPullParser.TEXT) { 149 } 150 if (type != XmlPullParser.START_TAG) { 151 throw new XmlPullParserException( 152 parser.getPositionDescription() 153 + ": <item> tag requires a 'drawable' attribute or " 154 + "child tag defining a drawable"); 155 } 156 dr = Drawable.createFromXmlInner(r, parser, attrs); 157 } 158 159 mStateListState.addStateSet(states, dr); 160 } 161 162 onStateChange(getState()); 163 } 164 165 StateListState getStateListState() { 166 return mStateListState; 167 } 168 169 /** 170 * Gets the number of states contained in this drawable. 171 * 172 * @return The number of states contained in this drawable. 173 * @hide pending API council 174 * @see #getStateSet(int) 175 * @see #getStateDrawable(int) 176 */ 177 public int getStateCount() { 178 return mStateListState.getChildCount(); 179 } 180 181 /** 182 * Gets the state set at an index. 183 * 184 * @param index The index of the state set. 185 * @return The state set at the index. 186 * @hide pending API council 187 * @see #getStateCount() 188 * @see #getStateDrawable(int) 189 */ 190 public int[] getStateSet(int index) { 191 return mStateListState.mStateSets[index]; 192 } 193 194 /** 195 * Gets the drawable at an index. 196 * 197 * @param index The index of the drawable. 198 * @return The drawable at the index. 199 * @hide pending API council 200 * @see #getStateCount() 201 * @see #getStateSet(int) 202 */ 203 public Drawable getStateDrawable(int index) { 204 return mStateListState.getChildren()[index]; 205 } 206 207 @Override 208 public Drawable mutate() { 209 if (!mMutated && super.mutate() == this) { 210 final int[][] sets = mStateListState.mStateSets; 211 final int count = sets.length; 212 mStateListState.mStateSets = new int[count][]; 213 for (int i = 0; i < count; i++) { 214 mStateListState.mStateSets[i] = sets[i].clone(); 215 } 216 mMutated = true; 217 } 218 return this; 219 } 220 221 static final class StateListState extends DrawableContainerState { 222 private int[][] mStateSets; 223 224 StateListState(StateListState orig, StateListDrawable owner) { 225 super(orig, owner); 226 227 if (orig != null) { 228 mStateSets = orig.mStateSets; 229 } else { 230 mStateSets = new int[getChildren().length][]; 231 } 232 } 233 234 int addStateSet(int[] stateSet, Drawable drawable) { 235 final int pos = addChild(drawable); 236 mStateSets[pos] = stateSet; 237 return pos; 238 } 239 240 private int indexOfStateSet(int[] stateSet) { 241 final int[][] stateSets = mStateSets; 242 final int N = getChildCount(); 243 for (int i = 0; i < N; i++) { 244 if (StateSet.stateSetMatches(stateSets[i], stateSet)) { 245 return i; 246 } 247 } 248 return -1; 249 } 250 251 @Override 252 public Drawable newDrawable() { 253 return new StateListDrawable(this); 254 } 255 256 @Override 257 public void growArray(int oldSize, int newSize) { 258 super.growArray(oldSize, newSize); 259 final int[][] newStateSets = new int[newSize][]; 260 System.arraycopy(mStateSets, 0, newStateSets, 0, oldSize); 261 mStateSets = newStateSets; 262 } 263 } 264 265 private StateListDrawable(StateListState state) { 266 StateListState as = new StateListState(state, this); 267 mStateListState = as; 268 setConstantState(as); 269 onStateChange(getState()); 270 } 271} 272 273