summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/widget/AbsListView.java24
-rw-r--r--core/java/android/widget/ListPopupWindow.java2
-rw-r--r--core/java/android/widget/ListView.java2
-rw-r--r--graphics/java/android/graphics/drawable/Ripple.java142
-rw-r--r--graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java13
5 files changed, 115 insertions, 68 deletions
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index becda67..0966be3 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -110,6 +110,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* @see #setTranscriptMode(int)
*/
public static final int TRANSCRIPT_MODE_DISABLED = 0;
+
/**
* The list will automatically scroll to the bottom when a data set change
* notification is received and only if the last item is already visible
@@ -118,6 +119,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* @see #setTranscriptMode(int)
*/
public static final int TRANSCRIPT_MODE_NORMAL = 1;
+
/**
* The list will automatically scroll to the bottom, no matter what items
* are currently visible.
@@ -2489,8 +2491,30 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
+ /**
+ * Positions the selector in a way that mimics keyboard focus. If the
+ * selector drawable supports hotspots, this manages the focus hotspot.
+ */
+ void positionSelectorLikeFocus(int position, View sel) {
+ positionSelector(position, sel);
+
+ final Drawable selector = mSelector;
+ if (selector != null && selector.supportsHotspots() && position != INVALID_POSITION) {
+ final Rect bounds = mSelectorRect;
+ final float x = bounds.exactCenterX();
+ final float y = bounds.exactCenterY();
+ selector.setHotspot(R.attr.state_focused, x, y);
+ }
+ }
+
void positionSelector(int position, View sel) {
if (position != INVALID_POSITION) {
+ if (mSelectorPosition != position) {
+ final Drawable selector = mSelector;
+ if (selector != null && selector.supportsHotspots()) {
+ selector.clearHotspots();
+ }
+ }
mSelectorPosition = position;
}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index b47177a..10ec105 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1565,7 +1565,7 @@ public class ListPopupWindow {
// Ensure that keyboard focus starts from the last touched position.
setSelectedPositionInt(position);
- positionSelector(position, child);
+ positionSelectorLikeFocus(position, child);
// Refresh the drawable state to reflect the new pressed state,
// which will also update the selector state.
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 5de67c8..eeb8015 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2564,7 +2564,7 @@ public class ListView extends AbsListView {
if (needToRedraw) {
if (selectedView != null) {
- positionSelector(selectedPos, selectedView);
+ positionSelectorLikeFocus(selectedPos, selectedView);
mSelectedTop = selectedView.getTop();
}
if (!awakenScrollBars()) {
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 207834a..3446000 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -18,6 +18,7 @@ package android.graphics.drawable;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
@@ -85,8 +86,9 @@ class Ripple {
/** Configured maximum ripple radius. */
private final int mMaxInsideRadius;
- private ObjectAnimator mEnter;
- private ObjectAnimator mExit;
+ private ObjectAnimator mOuter;
+ private ObjectAnimator mInner;
+ private ObjectAnimator mAlpha;
/** Maximum ripple radius. */
private int mMaxRadius;
@@ -267,28 +269,46 @@ class Ripple {
public void exit() {
mExitFinished = false;
- final ObjectAnimator exit = ObjectAnimator.ofFloat(this, "innerRadius", 0, mMaxRadius);
- exit.setAutoCancel(true);
- exit.setDuration(EXIT_DURATION);
- exit.setInterpolator(INTERPOLATOR);
- exit.addListener(mAnimationListener);
+ final ObjectAnimator inner = ObjectAnimator.ofFloat(this, "innerRadius", 0, mMaxRadius);
+ inner.setAutoCancel(true);
+ inner.setDuration(EXIT_DURATION);
+ inner.setInterpolator(INTERPOLATOR);
+ inner.addListener(mAnimationListener);
- if (mEnter != null && mEnter.isStarted()) {
+ if (mOuter != null && mOuter.isStarted()) {
// If we haven't been running the enter animation for long enough,
// delay the exit animator.
- final int elapsed = (int) (mEnter.getAnimatedFraction() * mEnter.getDuration());
+ final int elapsed = (int) (mOuter.getAnimatedFraction() * mOuter.getDuration());
final int delay = Math.max(0, EXIT_MIN_DELAY - elapsed);
- exit.setStartDelay(delay);
+ inner.setStartDelay(delay);
}
- exit.start();
+ inner.start();
- final ObjectAnimator fade = ObjectAnimator.ofFloat(this, "alphaMultiplier", 0);
- fade.setAutoCancel(true);
- fade.setDuration(EXIT_DURATION);
- fade.start();
+ final ObjectAnimator alpha = ObjectAnimator.ofFloat(this, "alphaMultiplier", 0);
+ alpha.setAutoCancel(true);
+ alpha.setDuration(EXIT_DURATION);
+ alpha.start();
- mExit = exit;
+ mInner = inner;
+ mAlpha = alpha;
+ }
+
+ /**
+ * Cancel all animations.
+ */
+ public void cancel() {
+ if (mInner != null) {
+ mInner.end();
+ }
+
+ if (mOuter != null) {
+ mOuter.cancel();
+ }
+
+ if (mAlpha != null) {
+ mAlpha.end();
+ }
}
private void invalidateSelf() {
@@ -299,47 +319,55 @@ class Ripple {
* Starts the enter animation.
*/
private void enter() {
- final ObjectAnimator enter = ObjectAnimator.ofFloat(this, "outerRadius", mMaxRadius);
- enter.setAutoCancel(true);
- enter.setDuration(ENTER_DURATION);
- enter.setInterpolator(INTERPOLATOR);
- enter.start();
-
- final ObjectAnimator fade = ObjectAnimator.ofFloat(this, "alphaMultiplier", 1);
- fade.setAutoCancel(true);
- fade.setDuration(FADE_DURATION);
- fade.start();
-
- // TODO: Starting with a delay will still cancel the fade in.
- if (false && mPulseEnabled) {
- final ObjectAnimator pulse = ObjectAnimator.ofFloat(
- this, "alphaMultiplier", 1, PULSE_MIN_ALPHA);
- pulse.setAutoCancel(true);
- pulse.setDuration(PULSE_DURATION + PULSE_INTERVAL);
- pulse.setRepeatCount(ObjectAnimator.INFINITE);
- pulse.setRepeatMode(ObjectAnimator.REVERSE);
- pulse.setStartDelay(PULSE_DELAY);
- pulse.start();
+ final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerRadius", mMaxRadius);
+ outer.setAutoCancel(true);
+ outer.setDuration(ENTER_DURATION);
+ outer.setInterpolator(INTERPOLATOR);
+ outer.start();
+
+ final ObjectAnimator alpha = ObjectAnimator.ofFloat(this, "alphaMultiplier", 1);
+ if (mPulseEnabled) {
+ alpha.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final ObjectAnimator pulse = ObjectAnimator.ofFloat(
+ this, "alphaMultiplier", 1, PULSE_MIN_ALPHA);
+ pulse.setAutoCancel(true);
+ pulse.setDuration(PULSE_DURATION + PULSE_INTERVAL);
+ pulse.setRepeatCount(ObjectAnimator.INFINITE);
+ pulse.setRepeatMode(ObjectAnimator.REVERSE);
+ pulse.setStartDelay(PULSE_DELAY);
+ pulse.start();
+
+ mAlpha = pulse;
+ }
+ });
}
+ alpha.setAutoCancel(true);
+ alpha.setDuration(FADE_DURATION);
+ alpha.start();
- mEnter = enter;
+ mOuter = outer;
+ mAlpha = alpha;
}
/**
* Starts the outside transition animation.
*/
private void outside() {
- final float targetRadius = mMaxOutsideRadius;
- final ObjectAnimator outside = ObjectAnimator.ofFloat(this, "outerRadius", targetRadius);
- outside.setAutoCancel(true);
- outside.setDuration(OUTSIDE_DURATION);
- outside.setInterpolator(INTERPOLATOR);
- outside.start();
-
- final ObjectAnimator fade = ObjectAnimator.ofFloat(this, "alphaMultiplier", 1);
- fade.setAutoCancel(true);
- fade.setDuration(FADE_DURATION);
- fade.start();
+ final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerRadius", mMaxOutsideRadius);
+ outer.setAutoCancel(true);
+ outer.setDuration(OUTSIDE_DURATION);
+ outer.setInterpolator(INTERPOLATOR);
+ outer.start();
+
+ final ObjectAnimator alpha = ObjectAnimator.ofFloat(this, "alphaMultiplier", 1);
+ alpha.setAutoCancel(true);
+ alpha.setDuration(FADE_DURATION);
+ alpha.start();
+
+ mOuter = outer;
+ mAlpha = alpha;
}
/**
@@ -358,27 +386,15 @@ class Ripple {
}
}
- private final AnimatorListener mAnimationListener = new AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
-
+ private final AnimatorListener mAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- if (animation == mExit) {
+ if (animation == mInner) {
mExitFinished = true;
mOuterRadius = 0;
mInnerRadius = 0;
mAlphaMultiplier = 1;
}
}
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
};
}
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 9000e5a..0097183 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -380,11 +380,18 @@ public class TouchFeedbackDrawable extends LayerDrawable {
@Override
public void clearHotspots() {
- if (mRipples == null) {
- return;
+ if (mRipples != null) {
+ mRipples.clear();
+ }
+
+ final int count = mAnimatingRipplesCount;
+ final Ripple[] ripples = mAnimatingRipples;
+ for (int i = 0; i < count; i++) {
+ ripples[i].cancel();
+ ripples[i] = null;
}
- mRipples.clear();
+ mAnimatingRipplesCount = 0;
invalidateSelf();
}