package org.holoeverywhere.drawable; import java.io.IOException; import com.actionbarsherlock.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.StateSet; public class StateListDrawable extends DrawableContainer { static final class StateListState extends DrawableContainerState { int[][] mStateSets; StateListState(StateListState orig, StateListDrawable owner, Resources res) { super(orig, owner, res); if (orig != null) { mStateSets = orig.mStateSets; } else { mStateSets = new int[getChildren().length][]; } } int addStateSet(int[] stateSet, Drawable drawable) { final int pos = addChild(drawable); mStateSets[pos] = stateSet; return pos; } @Override public void growArray(int oldSize, int newSize) { super.growArray(oldSize, newSize); final int[][] newStateSets = new int[newSize][]; System.arraycopy(mStateSets, 0, newStateSets, 0, oldSize); mStateSets = newStateSets; } private int indexOfStateSet(int[] stateSet) { final int[][] stateSets = mStateSets; final int N = getChildCount(); for (int i = 0; i < N; i++) { if (StateSet.stateSetMatches(stateSets[i], stateSet)) { return i; } } return -1; } @Override public Drawable newDrawable() { return new StateListDrawable(this, null); } @Override public Drawable newDrawable(Resources res) { return new StateListDrawable(this, res); } } private static final boolean DEFAULT_DITHER = true; private boolean mMutated; private final StateListState mStateListState; public StateListDrawable() { this(null, null); } private StateListDrawable(StateListState state, Resources res) { StateListState as = new StateListState(state, this, res); mStateListState = as; setConstantState(as); onStateChange(getState()); } public void addState(int[] stateSet, Drawable drawable) { if (drawable != null) { mStateListState.addStateSet(stateSet, drawable); // in case the new state matches our current state... onStateChange(getState()); } } /** * Gets the number of states contained in this drawable. * * @return The number of states contained in this drawable. * @hide pending API council * @see #getStateSet(int) * @see #getStateDrawable(int) */ public int getStateCount() { return mStateListState.getChildCount(); } /** * Gets the drawable at an index. * * @param index The index of the drawable. * @return The drawable at the index. * @hide pending API council * @see #getStateCount() * @see #getStateSet(int) */ public Drawable getStateDrawable(int index) { return mStateListState.getChildren()[index]; } /** * Gets the index of the drawable with the provided state set. * * @param stateSet the state set to look up * @return the index of the provided state set, or -1 if not found * @hide pending API council * @see #getStateDrawable(int) * @see #getStateSet(int) */ public int getStateDrawableIndex(int[] stateSet) { return mStateListState.indexOfStateSet(stateSet); } StateListState getStateListState() { return mStateListState; } /** * Gets the state set at an index. * * @param index The index of the state set. * @return The state set at the index. * @hide pending API council * @see #getStateCount() * @see #getStateDrawable(int) */ public int[] getStateSet(int index) { return mStateListState.mStateSets[index]; } @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { TypedArray a = r.obtainAttributes(attrs, R.styleable.StateListDrawable); super.setVisible(a.getBoolean(R.styleable.StateListDrawable_android_visible, true), false); mStateListState.setVariablePadding(a.getBoolean( R.styleable.StateListDrawable_android_variablePadding, false)); mStateListState.setConstantSize(a.getBoolean( R.styleable.StateListDrawable_android_constantSize, false)); mStateListState.setEnterFadeDuration(a.getInt( R.styleable.StateListDrawable_android_enterFadeDuration, 0)); mStateListState.setExitFadeDuration(a.getInt( R.styleable.StateListDrawable_android_exitFadeDuration, 0)); setDither(a.getBoolean(R.styleable.StateListDrawable_android_dither, DEFAULT_DITHER)); a.recycle(); int type; final int innerDepth = parser.getDepth() + 1; int depth; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { if (type != XmlPullParser.START_TAG) { continue; } if (depth > innerDepth || !parser.getName().equals("item")) { continue; } int drawableRes = 0; int i; int j = 0; final int numAttrs = attrs.getAttributeCount(); int[] states = new int[numAttrs]; for (i = 0; i < numAttrs; i++) { final int stateResId = attrs.getAttributeNameResource(i); if (stateResId == 0) { break; } if (stateResId == android.R.attr.drawable) { drawableRes = attrs.getAttributeResourceValue(i, 0); } else { states[j++] = attrs.getAttributeBooleanValue(i, false) ? stateResId : -stateResId; } } states = StateSet.trimStateSet(states, j); Drawable dr; if (drawableRes != 0) { dr = DrawableCompat.getDrawable(r, drawableRes); } else { while ((type = parser.next()) == XmlPullParser.TEXT) { } if (type != XmlPullParser.START_TAG) { throw new XmlPullParserException( parser.getPositionDescription() + ": <item> tag requires a 'drawable' attribute or " + "child tag defining a drawable"); } dr = DrawableCompat.createFromXmlInner(r, parser, attrs); } mStateListState.addStateSet(states, dr); } onStateChange(getState()); } @Override public boolean isStateful() { return true; } @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { mStateListState.mStateSets = mStateListState.mStateSets.clone(); mMutated = true; } return this; } @Override protected boolean onStateChange(int[] stateSet) { int idx = mStateListState.indexOfStateSet(stateSet); if (idx < 0) { idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD); } if (selectDrawable(idx)) { return true; } return super.onStateChange(stateSet); } }