FileDocCategorySizeDatePackage
AnimatedStateListDrawable.javaAPI DocAndroid 5.1 API25925Thu Mar 12 22:22:30 GMT 2015android.graphics.drawable

AnimatedStateListDrawable

public class AnimatedStateListDrawable extends StateListDrawable
Drawable containing a set of Drawable keyframes where the currently displayed keyframe is chosen based on the current state set. Animations between keyframes may optionally be defined using transition elements.

This drawable can be defined in an XML file with the <animated-selector> element. Each keyframe Drawable is defined in a nested <item> element. Transitions are defined in a nested <transition> element.

attr
ref android.R.styleable#DrawableStates_state_focused
attr
ref android.R.styleable#DrawableStates_state_window_focused
attr
ref android.R.styleable#DrawableStates_state_enabled
attr
ref android.R.styleable#DrawableStates_state_checkable
attr
ref android.R.styleable#DrawableStates_state_checked
attr
ref android.R.styleable#DrawableStates_state_selected
attr
ref android.R.styleable#DrawableStates_state_activated
attr
ref android.R.styleable#DrawableStates_state_active
attr
ref android.R.styleable#DrawableStates_state_single
attr
ref android.R.styleable#DrawableStates_state_first
attr
ref android.R.styleable#DrawableStates_state_middle
attr
ref android.R.styleable#DrawableStates_state_last
attr
ref android.R.styleable#DrawableStates_state_pressed

Fields Summary
private static final String
LOGTAG
private static final String
ELEMENT_TRANSITION
private static final String
ELEMENT_ITEM
private AnimatedStateListState
mState
private Transition
mTransition
The currently running transition, if any.
private int
mTransitionToIndex
Index to be set after the transition ends.
private int
mTransitionFromIndex
Index away from which we are transitioning.
private boolean
mMutated
Constructors Summary
public AnimatedStateListDrawable()


      
        this(null, null);
    
private AnimatedStateListDrawable(AnimatedStateListState state, android.content.res.Resources res)

        super(null);

        // Every animated state list drawable has its own constant state.
        final AnimatedStateListState newState = new AnimatedStateListState(state, this, res);
        setConstantState(newState);
        onStateChange(getState());
        jumpToCurrentState();
    
Methods Summary
public voidaddState(int[] stateSet, Drawable drawable, int id)
Add a new drawable to the set of keyframes.

param
stateSet An array of resource IDs to associate with the keyframe
param
drawable The drawable to show when in the specified state, may not be null
param
id The unique identifier for the keyframe

        if (drawable == null) {
            throw new IllegalArgumentException("Drawable must not be null");
        }

        mState.addStateSet(stateSet, drawable, id);
        onStateChange(getState());
    
public voidaddTransition(int fromId, int toId, T transition, boolean reversible)
Adds a new transition between keyframes.

param
fromId Unique identifier of the starting keyframe
param
toId Unique identifier of the ending keyframe
param
transition An {@link Animatable} drawable to use as a transition, may not be null
param
reversible Whether the transition can be reversed

        if (transition == null) {
            throw new IllegalArgumentException("Transition drawable must not be null");
        }

        mState.addTransition(fromId, toId, transition, reversible);
    
public voidapplyTheme(android.content.res.Resources.Theme theme)

        super.applyTheme(theme);

        final AnimatedStateListState state = mState;
        if (state == null || state.mAnimThemeAttrs == null) {
            return;
        }

        final TypedArray a = theme.resolveAttributes(
                state.mAnimThemeAttrs, R.styleable.AnimatedRotateDrawable);
        updateStateFromTypedArray(a);
        a.recycle();

        init();
    
public voidclearMutated()

hide

        super.clearMutated();
        mMutated = false;
    
android.graphics.drawable.AnimatedStateListDrawable$AnimatedStateListStatecloneConstantState()

        return new AnimatedStateListState(mState, this, null);
    
public voidinflate(android.content.res.Resources r, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs, android.content.res.Resources.Theme theme)

        final TypedArray a = obtainAttributes(
                r, theme, attrs, R.styleable.AnimatedStateListDrawable);
        super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible);
        updateStateFromTypedArray(a);
        a.recycle();

        inflateChildElements(r, parser, attrs, theme);

        init();
    
private voidinflateChildElements(android.content.res.Resources r, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs, android.content.res.Resources.Theme theme)

        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) {
                continue;
            }

            if (parser.getName().equals(ELEMENT_ITEM)) {
                parseItem(r, parser, attrs, theme);
            } else if (parser.getName().equals(ELEMENT_TRANSITION)) {
                parseTransition(r, parser, attrs, theme);
            }
        }
    
private voidinit()

        onStateChange(getState());
    
public booleanisStateful()

        return true;
    
public voidjumpToCurrentState()

        super.jumpToCurrentState();

        if (mTransition != null) {
            mTransition.stop();
            mTransition = null;

            selectDrawable(mTransitionToIndex);
            mTransitionToIndex = -1;
            mTransitionFromIndex = -1;
        }
    
public Drawablemutate()

        if (!mMutated && super.mutate() == this) {
            mState.mutate();
            mMutated = true;
        }

        return this;
    
protected booleanonStateChange(int[] stateSet)

        // If we're not already at the target index, either attempt to find a
        // valid transition to it or jump directly there.
        final int targetIndex = mState.indexOfKeyframe(stateSet);
        boolean changed = targetIndex != getCurrentIndex()
                && (selectTransition(targetIndex) || selectDrawable(targetIndex));

        // We need to propagate the state change to the current drawable, but
        // we can't call StateListDrawable.onStateChange() without changing the
        // current drawable.
        final Drawable current = getCurrent();
        if (current != null) {
            changed |= current.setState(stateSet);
        }

        return changed;
    
private intparseItem(android.content.res.Resources r, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs, android.content.res.Resources.Theme theme)

        // This allows state list drawable item elements to be themed at
        // inflation time but does NOT make them work for Zygote preload.
        final TypedArray a = obtainAttributes(r, theme, attrs,
                R.styleable.AnimatedStateListDrawableItem);
        final int keyframeId = a.getResourceId(R.styleable.AnimatedStateListDrawableItem_id, 0);
        Drawable dr = a.getDrawable(R.styleable.AnimatedStateListDrawableItem_drawable);
        a.recycle();

        final int[] states = extractStateSet(attrs);

        // Loading child elements modifies the state of the AttributeSet's
        // underlying parser, so it needs to happen after obtaining
        // attributes and extracting states.
        if (dr == null) {
            int type;
            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 = Drawable.createFromXmlInner(r, parser, attrs, theme);
        }

        return mState.addStateSet(states, dr, keyframeId);
    
private intparseTransition(android.content.res.Resources r, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs, android.content.res.Resources.Theme theme)

        // This allows state list drawable item elements to be themed at
        // inflation time but does NOT make them work for Zygote preload.
        final TypedArray a = obtainAttributes(r, theme, attrs,
                R.styleable.AnimatedStateListDrawableTransition);
        final int fromId = a.getResourceId(
                R.styleable.AnimatedStateListDrawableTransition_fromId, 0);
        final int toId = a.getResourceId(
                R.styleable.AnimatedStateListDrawableTransition_toId, 0);
        final boolean reversible = a.getBoolean(
                R.styleable.AnimatedStateListDrawableTransition_reversible, false);
        Drawable dr = a.getDrawable(
                R.styleable.AnimatedStateListDrawableTransition_drawable);
        a.recycle();

        // Loading child elements modifies the state of the AttributeSet's
        // underlying parser, so it needs to happen after obtaining
        // attributes and extracting states.
        if (dr == null) {
            int type;
            while ((type = parser.next()) == XmlPullParser.TEXT) {
            }
            if (type != XmlPullParser.START_TAG) {
                throw new XmlPullParserException(
                        parser.getPositionDescription()
                                + ": <transition> tag requires a 'drawable' attribute or "
                                + "child tag defining a drawable");
            }
            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
        }

        return mState.addTransition(fromId, toId, dr, reversible);
    
private booleanselectTransition(int toIndex)

        final int fromIndex;
        final Transition currentTransition = mTransition;
        if (currentTransition != null) {
            if (toIndex == mTransitionToIndex) {
                // Already animating to that keyframe.
                return true;
            } else if (toIndex == mTransitionFromIndex && currentTransition.canReverse()) {
                // Reverse the current animation.
                currentTransition.reverse();
                mTransitionToIndex = mTransitionFromIndex;
                mTransitionFromIndex = toIndex;
                return true;
            }

            // Start the next transition from the end of the current one.
            fromIndex = mTransitionToIndex;

            // Changing animation, end the current animation.
            currentTransition.stop();
        } else {
            fromIndex = getCurrentIndex();
        }

        // Reset state.
        mTransition = null;
        mTransitionFromIndex = -1;
        mTransitionToIndex = -1;

        final AnimatedStateListState state = mState;
        final int fromId = state.getKeyframeIdAt(fromIndex);
        final int toId = state.getKeyframeIdAt(toIndex);
        if (toId == 0 || fromId == 0) {
            // Missing a keyframe ID.
            return false;
        }

        final int transitionIndex = state.indexOfTransition(fromId, toId);
        if (transitionIndex < 0) {
            // Couldn't select a transition.
            return false;
        }

        boolean hasReversibleFlag = state.transitionHasReversibleFlag(fromId, toId);

        // This may fail if we're already on the transition, but that's okay!
        selectDrawable(transitionIndex);

        final Transition transition;
        final Drawable d = getCurrent();
        if (d instanceof AnimationDrawable) {
            final boolean reversed = state.isTransitionReversed(fromId, toId);

            transition = new AnimationDrawableTransition((AnimationDrawable) d,
                    reversed, hasReversibleFlag);
        } else if (d instanceof AnimatedVectorDrawable) {
            final boolean reversed = state.isTransitionReversed(fromId, toId);

            transition = new AnimatedVectorDrawableTransition((AnimatedVectorDrawable) d,
                    reversed, hasReversibleFlag);
        } else if (d instanceof Animatable) {
            transition = new AnimatableTransition((Animatable) d);
        } else {
            // We don't know how to animate this transition.
            return false;
        }

        transition.start();

        mTransition = transition;
        mTransitionFromIndex = fromIndex;
        mTransitionToIndex = toIndex;
        return true;
    
protected voidsetConstantState(DrawableContainerState state)

        super.setConstantState(state);

        if (state instanceof AnimatedStateListState) {
            mState = (AnimatedStateListState) state;
        }
    
public booleansetVisible(boolean visible, boolean restart)

        final boolean changed = super.setVisible(visible, restart);

        if (mTransition != null && (changed || restart)) {
            if (visible) {
                mTransition.start();
            } else {
                // Ensure we're showing the correct state when visible.
                jumpToCurrentState();
            }
        }

        return changed;
    
private voidupdateStateFromTypedArray(android.content.res.TypedArray a)

        final AnimatedStateListState state = mState;

        // Account for any configuration changes.
        state.mChangingConfigurations |= a.getChangingConfigurations();

        // Extract the theme attributes, if any.
        state.mAnimThemeAttrs = a.extractThemeAttrs();

        state.setVariablePadding(a.getBoolean(
                R.styleable.AnimatedStateListDrawable_variablePadding, state.mVariablePadding));
        state.setConstantSize(a.getBoolean(
                R.styleable.AnimatedStateListDrawable_constantSize, state.mConstantSize));
        state.setEnterFadeDuration(a.getInt(
                R.styleable.AnimatedStateListDrawable_enterFadeDuration, state.mEnterFadeDuration));
        state.setExitFadeDuration(a.getInt(
                R.styleable.AnimatedStateListDrawable_exitFadeDuration, state.mExitFadeDuration));

        setDither(a.getBoolean(
                R.styleable.AnimatedStateListDrawable_dither, state.mDither));
        setAutoMirrored(a.getBoolean(
                R.styleable.AnimatedStateListDrawable_autoMirrored, state.mAutoMirrored));