diff options
6 files changed, 187 insertions, 47 deletions
diff --git a/api/current.txt b/api/current.txt index fb598fd..3451f70 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11284,7 +11284,7 @@ package android.graphics.drawable { public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable { ctor public AnimatedStateListDrawable(); method public void addState(int[], android.graphics.drawable.Drawable, int); - method public void addTransition(int, int, android.graphics.drawable.AnimationDrawable, boolean); + method public void addTransition(int, int, android.graphics.drawable.Drawable, boolean); } public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable { diff --git a/graphics/java/android/graphics/drawable/Animatable.java b/graphics/java/android/graphics/drawable/Animatable.java index 4edfad4..983d650 100644 --- a/graphics/java/android/graphics/drawable/Animatable.java +++ b/graphics/java/android/graphics/drawable/Animatable.java @@ -17,7 +17,7 @@ package android.graphics.drawable; /** - * Interface that drawables suporting animations should implement. + * Interface that drawables supporting animations should implement. */ public interface Animatable { /** diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java index e1dec9d..8483820 100644 --- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java @@ -20,6 +20,8 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; @@ -82,17 +84,23 @@ public class AnimatedStateListDrawable extends StateListDrawable { @Override public boolean setVisible(boolean visible, boolean restart) { + // If we're relying on an Animatable transition, the super method + // will handle visibility changes. final boolean changed = super.setVisible(visible, restart); + if (mAnim != null) { if (visible) { - if (changed || restart) { - // TODO: Should this support restart? - mAnim.end(); + if (restart) { + mAnim.cancel(); + mAnim.start(); + } else if (changed && mAnim.isPaused()) { + mAnim.resume(); } - } else { - mAnim.end(); + } else if (mAnim.isRunning()) { + mAnim.pause(); } } + return changed; } @@ -100,14 +108,16 @@ public class AnimatedStateListDrawable extends StateListDrawable { * 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 + * @param drawable The drawable to show when in the specified state, may not be null * @param id The unique identifier for the keyframe */ - public void addState(int[] stateSet, Drawable drawable, int id) { - if (drawable != null) { - mState.addStateSet(stateSet, drawable, id); - onStateChange(getState()); + public void addState(@NonNull int[] stateSet, @NonNull Drawable drawable, int id) { + if (drawable == null) { + throw new IllegalArgumentException("Drawable must not be null"); } + + mState.addStateSet(stateSet, drawable, id); + onStateChange(getState()); } /** @@ -115,11 +125,16 @@ public class AnimatedStateListDrawable extends StateListDrawable { * * @param fromId Unique identifier of the starting keyframe * @param toId Unique identifier of the ending keyframe - * @param anim An AnimationDrawable to use as a transition + * @param transition An animatable drawable to use as a transition, may not be null * @param reversible Whether the transition can be reversed */ - public void addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) { - mState.addTransition(fromId, toId, anim, reversible); + public void addTransition(int fromId, int toId, @NonNull Drawable transition, + boolean reversible) { + if (transition == null) { + throw new IllegalArgumentException("Transition drawable must not be null"); + } + + mState.addTransition(fromId, toId, transition, reversible); } @Override @@ -131,13 +146,16 @@ public class AnimatedStateListDrawable extends StateListDrawable { protected boolean onStateChange(int[] stateSet) { final int keyframeIndex = mState.indexOfKeyframe(stateSet); if (keyframeIndex == getCurrentIndex()) { + // No transition needed. return false; } + // Attempt to find a valid transition to the keyframe. if (selectTransition(keyframeIndex)) { return true; } + // No valid transition, attempt to jump directly to the keyframe. if (selectDrawable(keyframeIndex)) { return true; } @@ -146,9 +164,13 @@ public class AnimatedStateListDrawable extends StateListDrawable { } private boolean selectTransition(int toIndex) { + if (toIndex == mAnimToIndex) { + // Already animating to that keyframe. + return true; + } + if (mAnim != null) { if (toIndex == mAnimToIndex) { - // Already animating to that keyframe. return true; } else if (toIndex == mAnimFromIndex) { // Reverse the current animation. @@ -159,9 +181,14 @@ public class AnimatedStateListDrawable extends StateListDrawable { } // Changing animation, end the current animation. - mAnim.end(); + mAnim.cancel(); + mAnim = null; } + // Reset state. + mAnimFromIndex = -1; + mAnimToIndex = -1; + final AnimatedStateListState state = mState; final int fromIndex = getCurrentIndex(); final int fromId = state.getKeyframeIdAt(fromIndex); @@ -179,42 +206,54 @@ public class AnimatedStateListDrawable extends StateListDrawable { } final Drawable d = getCurrent(); - if (!(d instanceof AnimationDrawable)) { - // Transition isn't an animation. + if (d instanceof AnimationDrawable) { + // We can support reverse() here. + final boolean reversed = mState.isTransitionReversed(fromId, toId); + mAnim = getAnimationDrawableAnimator((AnimationDrawable) d, reversed); + mAnim.start(); + } else if (d instanceof Animatable) { + // Let the transition animate itself. + ((Animatable) d).start(); + } else { + // We don't know how to animate this transition. return false; } - final AnimationDrawable ad = (AnimationDrawable) d; - final boolean reversed = mState.isTransitionReversed(fromId, toId); + mAnimFromIndex = fromIndex; + mAnimToIndex = toIndex; + return true; + } + + private ObjectAnimator getAnimationDrawableAnimator(@NonNull AnimationDrawable ad, + boolean reversed) { final int frameCount = ad.getNumberOfFrames(); final int fromFrame = reversed ? frameCount - 1 : 0; final int toFrame = reversed ? 0 : frameCount - 1; - final FrameInterpolator interp = new FrameInterpolator(ad, reversed); final ObjectAnimator anim = ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame); anim.setAutoCancel(true); anim.setDuration(interp.getTotalDuration()); anim.addListener(mAnimListener); anim.setInterpolator(interp); - anim.start(); - mAnim = anim; - mAnimFromIndex = fromIndex; - mAnimToIndex = toIndex; - return true; + return anim; } @Override public void jumpToCurrentState() { + // If we're relying on an Animatable transition, the super method + // will handle jumping it to the current state. super.jumpToCurrentState(); if (mAnim != null) { mAnim.end(); + mAnim = null; } } @Override - public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) + public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, + @NonNull AttributeSet attrs, @Nullable Theme theme) throws XmlPullParserException, IOException { final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedStateListDrawable); @@ -260,7 +299,8 @@ public class AnimatedStateListDrawable extends StateListDrawable { onStateChange(getState()); } - private int parseTransition(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) + private int parseTransition(@NonNull Resources r, @NonNull XmlPullParser parser, + @NonNull AttributeSet attrs, @Nullable Theme theme) throws XmlPullParserException, IOException { int drawableRes = 0; int fromId = 0; @@ -304,19 +344,11 @@ public class AnimatedStateListDrawable extends StateListDrawable { dr = Drawable.createFromXmlInner(r, parser, attrs, theme); } - final AnimationDrawable anim; - if (dr instanceof AnimationDrawable) { - anim = (AnimationDrawable) dr; - } else { - throw new XmlPullParserException(parser.getPositionDescription() - + ": <transition> tag requires a 'drawable' attribute or " - + "child tag defining a drawable of type <animation>"); - } - - return mState.addTransition(fromId, toId, anim, reversible); + return mState.addTransition(fromId, toId, dr, reversible); } - private int parseItem(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) + private int parseItem(@NonNull Resources r, @NonNull XmlPullParser parser, + @NonNull AttributeSet attrs, @Nullable Theme theme) throws XmlPullParserException, IOException { int drawableRes = 0; int keyframeId = 0; @@ -390,8 +422,8 @@ public class AnimatedStateListDrawable extends StateListDrawable { final LongSparseLongArray mTransitions; final SparseIntArray mStateIds; - AnimatedStateListState(AnimatedStateListState orig, AnimatedStateListDrawable owner, - Resources res) { + AnimatedStateListState(@Nullable AnimatedStateListState orig, + @NonNull AnimatedStateListDrawable owner, @Nullable Resources res) { super(orig, owner, res); if (orig != null) { @@ -403,7 +435,7 @@ public class AnimatedStateListDrawable extends StateListDrawable { } } - int addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) { + int addTransition(int fromId, int toId, @NonNull Drawable anim, boolean reversible) { final int pos = super.addChild(anim); final long keyFromTo = generateTransitionKey(fromId, toId); mTransitions.append(keyFromTo, pos); @@ -416,13 +448,13 @@ public class AnimatedStateListDrawable extends StateListDrawable { return addChild(anim); } - int addStateSet(int[] stateSet, Drawable drawable, int id) { + int addStateSet(@NonNull int[] stateSet, @NonNull Drawable drawable, int id) { final int index = super.addStateSet(stateSet, drawable); mStateIds.put(index, id); return index; } - int indexOfKeyframe(int[] stateSet) { + int indexOfKeyframe(@NonNull int[] stateSet) { final int index = super.indexOfStateSet(stateSet); if (index >= 0) { return index; @@ -460,13 +492,13 @@ public class AnimatedStateListDrawable extends StateListDrawable { } } - void setConstantState(AnimatedStateListState state) { + void setConstantState(@NonNull AnimatedStateListState state) { super.setConstantState(state); mState = state; } - private AnimatedStateListDrawable(AnimatedStateListState state, Resources res) { + private AnimatedStateListDrawable(@Nullable AnimatedStateListState state, @Nullable Resources res) { super(null); final AnimatedStateListState newState = new AnimatedStateListState(state, this, res); diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml index 613660c13..56fa0a9 100644 --- a/tests/VectorDrawableTest/AndroidManifest.xml +++ b/tests/VectorDrawableTest/AndroidManifest.xml @@ -71,6 +71,15 @@ </intent-filter> </activity> <activity + android:name="AnimatedStateVectorDrawableTest" + android:label="AnimatedStateList and AnimatedVectorDrawable" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="com.android.test.dynamic.TEST" /> + </intent-filter> + </activity> + <activity android:name="VectorDrawable01" android:label="VectorTest1" > <intent-filter> diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable.xml b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable.xml new file mode 100644 index 0000000..30fb1b8 --- /dev/null +++ b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable.xml @@ -0,0 +1,47 @@ +<!-- + Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/on" android:state_checked="true" + android:drawable="@drawable/vector_drawable12" /> + <item android:id="@+id/off" + android:drawable="@drawable/vector_drawable12" /> + <transition android:fromId="@+id/off" android:toId="@+id/on"> + <animated-vector android:drawable="@drawable/vector_drawable12"> + <target + android:name="pie1" + android:animation="@anim/trim_path_animation01" /> + <target + android:name="v" + android:animation="@anim/trim_path_animation02" /> + <target + android:name="v" + android:animation="@anim/trim_path_animation05" /> + <target + android:name="rotationGroup" + android:animation="@anim/trim_path_animation03" /> + <target + android:name="rotationGroup3" + android:animation="@anim/trim_path_animation03" /> + <target + android:name="rotationGroupBlue" + android:animation="@anim/trim_path_animation03" /> + <target + android:name="rotationGroup" + android:animation="@anim/trim_path_animation04" /> + </animated-vector> + </transition> +</animated-selector> diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java new file mode 100644 index 0000000..0ae0136 --- /dev/null +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.test.dynamic; + +import android.app.Activity; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.GridLayout; +import android.widget.ScrollView; + +public class AnimatedStateVectorDrawableTest extends Activity { + private static final String LOGCAT = "AnimatedStateVectorDrawableTest"; + + protected int[] icon = { + R.drawable.state_animation_vector_drawable + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ScrollView scrollView = new ScrollView(this); + GridLayout container = new GridLayout(this); + scrollView.addView(container); + container.setColumnCount(1); + + for (int i = 0; i < icon.length; i++) { + CheckBox button = new CheckBox(this); + button.setWidth(400); + button.setHeight(400); + button.setBackgroundResource(icon[i]); + container.addView(button); + } + + setContentView(scrollView); + } +} |