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