summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2010-10-18 17:02:43 -0700
committerDianne Hackborn <hackbod@google.com>2010-11-03 19:11:19 -0700
commit079e23575024e103358c982152afb7a720ae1a8a (patch)
treebb87037321dfc3be8fcf7c64737fa0fdca3ceb5c /graphics
parent079fd674fb9005771dd383a1a483d7dc5072b5b3 (diff)
downloadframeworks_base-079e23575024e103358c982152afb7a720ae1a8a.zip
frameworks_base-079e23575024e103358c982152afb7a720ae1a8a.tar.gz
frameworks_base-079e23575024e103358c982152afb7a720ae1a8a.tar.bz2
Add new fade in/out feature for drawable containers.
This is used to allow list view's pressed and activated indicators to fade in an out, though of course it can be used elsewhere as well. There is a lot of complexity in supporting this in list view. The two main things that are being dealt with: - When recycling views, we need to make sure that the view's drawable state doesn't get animated from an old row's state. The recycler now keeps track of which position a view was last in, and if it is reused at a new position there is a new View/Drawable API to tell it to jump to its current state instead of animating. - For the pressed indicator to fade out, we need to keep displaying it after it is hidden. There are new variables and code to keep track of this state, and tweaks in various places to be able to remember the last selected position and continue updating the drawable bounds as needed. Change-Id: Ic96aa1a3c05e519665abf3098892ff2cc4f0ef2f
Diffstat (limited to 'graphics')
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java196
-rw-r--r--graphics/java/android/graphics/drawable/StateListDrawable.java13
3 files changed, 201 insertions, 15 deletions
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 7b2d9d7..baa9d62 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -423,6 +423,13 @@ public abstract class Drawable {
}
/**
+ * If this Drawable does transition animations between states, ask that
+ * it immediately jump to the current state and skip any active animations.
+ */
+ public void jumpToCurrentState() {
+ }
+
+ /**
* @return The current drawable that will be used by this drawable. For simple drawables, this
* is just the drawable itself. For drawables that change state like
* {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 124d907..e55a746 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -21,6 +21,7 @@ import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.os.SystemClock;
/**
* A helper class that contains several {@link Drawable}s and selects which one to use.
@@ -28,6 +29,8 @@ import android.graphics.Rect;
* You can subclass it to create your own DrawableContainers or directly use one its child classes.
*/
public class DrawableContainer extends Drawable implements Drawable.Callback {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "DrawableContainer";
/**
* To be proper, we should have a getter for dither (and alpha, etc.)
@@ -48,6 +51,12 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
private int mCurIndex = -1;
private boolean mMutated;
+ // Animations.
+ private Runnable mAnimationRunnable;
+ private long mEnterAnimationEnd;
+ private long mExitAnimationEnd;
+ private Drawable mLastDrawable;
+
// overrides from Drawable
@Override
@@ -55,6 +64,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (mCurrDrawable != null) {
mCurrDrawable.draw(canvas);
}
+ if (mLastDrawable != null) {
+ mLastDrawable.draw(canvas);
+ }
}
@Override
@@ -83,7 +95,11 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (mAlpha != alpha) {
mAlpha = alpha;
if (mCurrDrawable != null) {
- mCurrDrawable.setAlpha(alpha);
+ if (mEnterAnimationEnd == 0) {
+ mCurrDrawable.setAlpha(alpha);
+ } else {
+ animate(false);
+ }
}
}
}
@@ -108,8 +124,29 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
}
+ /**
+ * Change the global fade duration when a new drawable is entering
+ * the scene.
+ * @param ms The amount of time to fade in milliseconds.
+ */
+ public void setEnterFadeDuration(int ms) {
+ mDrawableContainerState.mEnterFadeDuration = ms;
+ }
+
+ /**
+ * Change the global fade duration when a new drawable is leaving
+ * the scene.
+ * @param ms The amount of time to fade in milliseconds.
+ */
+ public void setExitFadeDuration(int ms) {
+ mDrawableContainerState.mExitFadeDuration = ms;
+ }
+
@Override
protected void onBoundsChange(Rect bounds) {
+ if (mLastDrawable != null) {
+ mLastDrawable.setBounds(bounds);
+ }
if (mCurrDrawable != null) {
mCurrDrawable.setBounds(bounds);
}
@@ -121,7 +158,34 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
@Override
+ public void jumpToCurrentState() {
+ boolean changed = false;
+ if (mLastDrawable != null) {
+ mLastDrawable.jumpToCurrentState();
+ mLastDrawable = null;
+ changed = true;
+ }
+ if (mCurrDrawable != null) {
+ mCurrDrawable.jumpToCurrentState();
+ }
+ if (mExitAnimationEnd != 0) {
+ mExitAnimationEnd = 0;
+ changed = true;
+ }
+ if (mEnterAnimationEnd != 0) {
+ mEnterAnimationEnd = 0;
+ changed = true;
+ }
+ if (changed) {
+ invalidateSelf();
+ }
+ }
+
+ @Override
protected boolean onStateChange(int[] state) {
+ if (mLastDrawable != null) {
+ return mLastDrawable.setState(state);
+ }
if (mCurrDrawable != null) {
return mCurrDrawable.setState(state);
}
@@ -130,6 +194,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
@Override
protected boolean onLevelChange(int level) {
+ if (mLastDrawable != null) {
+ return mLastDrawable.setLevel(level);
+ }
if (mCurrDrawable != null) {
return mCurrDrawable.setLevel(level);
}
@@ -168,22 +235,19 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0;
}
- public void invalidateDrawable(Drawable who)
- {
+ public void invalidateDrawable(Drawable who) {
if (who == mCurrDrawable && mCallback != null) {
mCallback.invalidateDrawable(this);
}
}
- public void scheduleDrawable(Drawable who, Runnable what, long when)
- {
+ public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (who == mCurrDrawable && mCallback != null) {
mCallback.scheduleDrawable(this, what, when);
}
}
- public void unscheduleDrawable(Drawable who, Runnable what)
- {
+ public void unscheduleDrawable(Drawable who, Runnable what) {
if (who == mCurrDrawable && mCallback != null) {
mCallback.unscheduleDrawable(this, what);
}
@@ -192,6 +256,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
@Override
public boolean setVisible(boolean visible, boolean restart) {
boolean changed = super.setVisible(visible, restart);
+ if (mLastDrawable != null) {
+ mLastDrawable.setVisible(visible, restart);
+ }
if (mCurrDrawable != null) {
mCurrDrawable.setVisible(visible, restart);
}
@@ -208,16 +275,39 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (idx == mCurIndex) {
return false;
}
- if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
- Drawable d = mDrawableContainerState.mDrawables[idx];
+
+ final long now = SystemClock.uptimeMillis();
+
+ if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + idx
+ + ": exit=" + mDrawableContainerState.mExitFadeDuration
+ + " enter=" + mDrawableContainerState.mEnterFadeDuration);
+
+ if (mDrawableContainerState.mExitFadeDuration > 0) {
+ if (mLastDrawable != null) {
+ mLastDrawable.setVisible(false, false);
+ }
if (mCurrDrawable != null) {
- mCurrDrawable.setVisible(false, false);
+ mLastDrawable = mCurrDrawable;
+ mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;
+ } else {
+ mLastDrawable = null;
+ mExitAnimationEnd = 0;
}
+ } else if (mCurrDrawable != null) {
+ mCurrDrawable.setVisible(false, false);
+ }
+
+ if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
+ Drawable d = mDrawableContainerState.mDrawables[idx];
mCurrDrawable = d;
mCurIndex = idx;
if (d != null) {
+ if (mDrawableContainerState.mEnterFadeDuration > 0) {
+ mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
+ } else {
+ d.setAlpha(mAlpha);
+ }
d.setVisible(isVisible(), true);
- d.setAlpha(mAlpha);
d.setDither(mDrawableContainerState.mDither);
d.setColorFilter(mColorFilter);
d.setState(getState());
@@ -225,16 +315,72 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
d.setBounds(getBounds());
}
} else {
- if (mCurrDrawable != null) {
- mCurrDrawable.setVisible(false, false);
- }
mCurrDrawable = null;
mCurIndex = -1;
}
+
+ if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) {
+ if (mAnimationRunnable == null) {
+ mAnimationRunnable = new Runnable() {
+ @Override public void run() {
+ animate(true);
+ invalidateSelf();
+ }
+ };
+ } else {
+ unscheduleSelf(mAnimationRunnable);
+ }
+ // Compute first frame and schedule next animation.
+ animate(true);
+ }
+
invalidateSelf();
+
return true;
}
+ void animate(boolean schedule) {
+ final long now = SystemClock.uptimeMillis();
+ boolean animating = false;
+ if (mCurrDrawable != null) {
+ if (mEnterAnimationEnd != 0) {
+ if (mEnterAnimationEnd <= now) {
+ mCurrDrawable.setAlpha(mAlpha);
+ mEnterAnimationEnd = 0;
+ } else {
+ int animAlpha = (int)((mEnterAnimationEnd-now)*255)
+ / mDrawableContainerState.mEnterFadeDuration;
+ if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha);
+ mCurrDrawable.setAlpha(((255-animAlpha)*mAlpha)/255);
+ animating = true;
+ }
+ }
+ } else {
+ mEnterAnimationEnd = 0;
+ }
+ if (mLastDrawable != null) {
+ if (mExitAnimationEnd != 0) {
+ if (mExitAnimationEnd <= now) {
+ mLastDrawable.setVisible(false, false);
+ mLastDrawable = null;
+ mExitAnimationEnd = 0;
+ } else {
+ int animAlpha = (int)((mExitAnimationEnd-now)*255)
+ / mDrawableContainerState.mExitFadeDuration;
+ if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha);
+ mLastDrawable.setAlpha((animAlpha*mAlpha)/255);
+ animating = true;
+ }
+ }
+ } else {
+ mExitAnimationEnd = 0;
+ }
+
+ if (schedule && animating) {
+ scheduleSelf(mAnimationRunnable, now + 1000/60);
+ }
+ }
+
@Override
public Drawable getCurrent() {
return mCurrDrawable;
@@ -300,6 +446,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
boolean mDither = DEFAULT_DITHER;
+ int mEnterFadeDuration;
+ int mExitFadeDuration;
+
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
@@ -340,6 +489,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mDither = orig.mDither;
+ mEnterFadeDuration = orig.mEnterFadeDuration;
+ mExitFadeDuration = orig.mExitFadeDuration;
+
} else {
mDrawables = new Drawable[10];
mNumChildren = 0;
@@ -476,6 +628,22 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
}
+ public final void setEnterFadeDuration(int duration) {
+ mEnterFadeDuration = duration;
+ }
+
+ public final int getEnterFadeDuration() {
+ return mEnterFadeDuration;
+ }
+
+ public final void setExitFadeDuration(int duration) {
+ mExitFadeDuration = duration;
+ }
+
+ public final int getExitFadeDuration() {
+ return mExitFadeDuration;
+ }
+
public final int getOpacity() {
if (mHaveOpacity) {
return mOpacity;
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 239be40..384ca81 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -20,6 +20,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.Arrays;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -44,6 +45,7 @@ import android.util.StateSet;
* @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
@@ -52,6 +54,9 @@ import android.util.StateSet;
* @attr ref android.R.styleable#DrawableStates_state_pressed
*/
public class StateListDrawable extends DrawableContainer {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "StateListDrawable";
+
/**
* To be proper, we should have a getter for dither (and alpha, etc.)
* so that proxy classes like this can save/restore their delegates'
@@ -93,6 +98,8 @@ public class StateListDrawable extends DrawableContainer {
@Override
protected boolean onStateChange(int[] stateSet) {
int idx = mStateListState.indexOfStateSet(stateSet);
+ if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states "
+ + Arrays.toString(stateSet) + " found " + idx);
if (idx < 0) {
idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
}
@@ -117,6 +124,10 @@ public class StateListDrawable extends DrawableContainer {
com.android.internal.R.styleable.StateListDrawable_variablePadding, false));
mStateListState.setConstantSize(a.getBoolean(
com.android.internal.R.styleable.StateListDrawable_constantSize, false));
+ mStateListState.setEnterFadeDuration(a.getInt(
+ com.android.internal.R.styleable.StateListDrawable_enterFadeDuration, 0));
+ mStateListState.setExitFadeDuration(a.getInt(
+ com.android.internal.R.styleable.StateListDrawable_exitFadeDuration, 0));
setDither(a.getBoolean(com.android.internal.R.styleable.StateListDrawable_dither,
DEFAULT_DITHER));
@@ -251,7 +262,7 @@ public class StateListDrawable extends DrawableContainer {
}
static final class StateListState extends DrawableContainerState {
- private int[][] mStateSets;
+ int[][] mStateSets;
StateListState(StateListState orig, StateListDrawable owner, Resources res) {
super(orig, owner, res);