summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChih-Chung Chang <chihchung@google.com>2011-09-26 10:40:38 +0800
committerChih-Chung Chang <chihchung@google.com>2011-09-26 15:35:25 +0800
commitca07852ce136f83cc1badac7d8c0ce234570bca2 (patch)
treebf630d40cdfc149d96c8e62a8ded235e54fa0cfb
parentf02dacf2c5d46eb65a4d6b7ba7a85964b04001e2 (diff)
downloadandroid_packages_apps_Snap-ca07852ce136f83cc1badac7d8c0ce234570bca2.tar.gz
android_packages_apps_Snap-ca07852ce136f83cc1badac7d8c0ce234570bca2.tar.bz2
android_packages_apps_Snap-ca07852ce136f83cc1badac7d8c0ce234570bca2.zip
Fix 5223982: Add animation when scrolling hits the edge.
Change-Id: I3c5191af3fe44ba835ae9b22755613a933065bcd
-rw-r--r--src/com/android/gallery3d/ui/Paper.java146
-rw-r--r--src/com/android/gallery3d/ui/ScrollerHelper.java11
-rw-r--r--src/com/android/gallery3d/ui/SlotView.java39
3 files changed, 151 insertions, 45 deletions
diff --git a/src/com/android/gallery3d/ui/Paper.java b/src/com/android/gallery3d/ui/Paper.java
index 641fc2c8e..ecc415064 100644
--- a/src/com/android/gallery3d/ui/Paper.java
+++ b/src/com/android/gallery3d/ui/Paper.java
@@ -16,10 +16,14 @@
package com.android.gallery3d.ui;
+import com.android.gallery3d.common.Utils;
import com.android.gallery3d.ui.PositionRepository.Position;
import com.android.gallery3d.util.GalleryUtils;
import android.opengl.Matrix;
+import android.os.SystemClock;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11ExtensionPack;
@@ -28,22 +32,37 @@ import javax.microedition.khronos.opengles.GL11ExtensionPack;
class Paper {
private static final String TAG = "Paper";
private static final int ROTATE_FACTOR = 4;
- private OverscrollAnimation mAnimationLeft = new OverscrollAnimation();
- private OverscrollAnimation mAnimationRight = new OverscrollAnimation();
+ private EdgeAnimation mAnimationLeft = new EdgeAnimation();
+ private EdgeAnimation mAnimationRight = new EdgeAnimation();
private int mWidth, mHeight;
private float[] mMatrix = new float[16];
public void overScroll(float distance) {
+ distance /= mWidth; // make it relative to width
if (distance < 0) {
- mAnimationLeft.scroll(-distance);
+ mAnimationLeft.onPull(-distance);
} else {
- mAnimationRight.scroll(distance);
+ mAnimationRight.onPull(distance);
}
}
- public boolean advanceAnimation(long currentTimeMillis) {
- return mAnimationLeft.advanceAnimation(currentTimeMillis)
- | mAnimationRight.advanceAnimation(currentTimeMillis);
+ public void edgeReached(float velocity) {
+ velocity /= mWidth; // make it relative to width
+ if (velocity < 0) {
+ mAnimationRight.onAbsorb(-velocity);
+ } else {
+ mAnimationLeft.onAbsorb(velocity);
+ }
+ }
+
+ public void onRelease() {
+ mAnimationLeft.onRelease();
+ mAnimationRight.onRelease();
+ }
+
+ public boolean advanceAnimation() {
+ // Note that we use "|" because we want both animations get updated.
+ return mAnimationLeft.update() | mAnimationRight.update();
}
public void setSize(int width, int height) {
@@ -56,7 +75,12 @@ class Paper {
float left = mAnimationLeft.getValue();
float right = mAnimationRight.getValue();
float screenX = target.x - scrollX;
- float t = ((mWidth - screenX) * left - screenX * right) / (mWidth * mWidth);
+ // We linearly interpolate the value [left, right] for the screenX
+ // range int [-1/4, 5/4]*mWidth. So if part of the thumbnail is outside
+ // the screen, we still get some transform.
+ float x = screenX + mWidth / 4;
+ int range = 3 * mWidth / 2;
+ float t = ((range - x) * left - x * right) / range;
// compress t to the range (-1, 1) by the function
// f(t) = (1 / (1 + e^-t) - 0.5) * 2
// then multiply by 90 to make the range (-45, 45)
@@ -71,42 +95,96 @@ class Paper {
}
}
-class OverscrollAnimation {
- private static final String TAG = "OverscrollAnimation";
- private static final long START_ANIMATION = -1;
- private static final long NO_ANIMATION = -2;
- private static final long ANIMATION_DURATION = 500;
+// This class follows the structure of frameworks's EdgeEffect class.
+class EdgeAnimation {
+ private static final String TAG = "EdgeAnimation";
+
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_PULL = 1;
+ private static final int STATE_ABSORB = 2;
+ private static final int STATE_RELEASE = 3;
+
+ // Time it will take the effect to fully done in ms
+ private static final int ABSORB_TIME = 200;
+ private static final int RELEASE_TIME = 500;
+
+ private static final float VELOCITY_FACTOR = 0.1f;
+
+ private final Interpolator mInterpolator;
+
+ private int mState;
+ private long mAnimationStartTime;
+ private float mValue;
- private long mAnimationStartTime = NO_ANIMATION;
- private float mVelocity;
- private float mCurrentValue;
+ private float mValueStart;
+ private float mValueFinish;
+ private long mStartTime;
+ private long mDuration;
- public void scroll(float distance) {
- mAnimationStartTime = START_ANIMATION;
- mCurrentValue += distance;
+ public EdgeAnimation() {
+ mInterpolator = new DecelerateInterpolator();
+ mState = STATE_IDLE;
}
- public boolean advanceAnimation(long currentTimeMillis) {
- if (mAnimationStartTime == NO_ANIMATION) return false;
- if (mAnimationStartTime == START_ANIMATION) {
- mAnimationStartTime = currentTimeMillis;
- return true;
- }
+ private void startAnimation(float start, float finish, long duration,
+ int newState) {
+ mValueStart = start;
+ mValueFinish = finish;
+ mDuration = duration;
+ mStartTime = now();
+ mState = newState;
+ }
+
+ // The deltaDistance's magnitude is in the range of -1 (no change) to 1.
+ // The value 1 is the full length of the view. Negative values means the
+ // movement is in the opposite direction.
+ public void onPull(float deltaDistance) {
+ if (mState == STATE_ABSORB) return;
+ mValue = Utils.clamp(mValue + deltaDistance, -1.0f, 1.0f);
+ mState = STATE_PULL;
+ }
+
+ public void onRelease() {
+ if (mState == STATE_IDLE || mState == STATE_ABSORB) return;
+ startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE);
+ }
- long deltaTime = currentTimeMillis - mAnimationStartTime;
- float t = deltaTime / 100f;
- mCurrentValue *= Math.pow(0.5f, t);
- mAnimationStartTime = currentTimeMillis;
+ public void onAbsorb(float velocity) {
+ float finish = Utils.clamp(mValue + velocity * VELOCITY_FACTOR,
+ -1.0f, 1.0f);
+ startAnimation(mValue, finish, ABSORB_TIME, STATE_ABSORB);
+ }
- if (mCurrentValue < 1) {
- mAnimationStartTime = NO_ANIMATION;
- mCurrentValue = 0;
- return false;
+ public boolean update() {
+ if (mState == STATE_IDLE) return false;
+ if (mState == STATE_PULL) return true;
+
+ float t = Utils.clamp((float)(now() - mStartTime) / mDuration, 0.0f, 1.0f);
+ /* Use linear interpolation for absorb, quadratic for others */
+ float interp = (mState == STATE_ABSORB)
+ ? t : mInterpolator.getInterpolation(t);
+
+ mValue = mValueStart + (mValueFinish - mValueStart) * interp;
+
+ if (t >= 1.0f) {
+ switch (mState) {
+ case STATE_ABSORB:
+ startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE);
+ break;
+ case STATE_RELEASE:
+ mState = STATE_IDLE;
+ break;
+ }
}
+
return true;
}
public float getValue() {
- return mCurrentValue;
+ return mValue;
+ }
+
+ private long now() {
+ return SystemClock.uptimeMillis();
}
}
diff --git a/src/com/android/gallery3d/ui/ScrollerHelper.java b/src/com/android/gallery3d/ui/ScrollerHelper.java
index 9f19cec96..84235185b 100644
--- a/src/com/android/gallery3d/ui/ScrollerHelper.java
+++ b/src/com/android/gallery3d/ui/ScrollerHelper.java
@@ -58,6 +58,10 @@ public class ScrollerHelper {
return mScroller.getCurrX();
}
+ public float getCurrVelocity() {
+ return mScroller.getCurrVelocity();
+ }
+
public void setPosition(int position) {
mScroller.startScroll(
position, 0, // startX, startY
@@ -77,7 +81,8 @@ public class ScrollerHelper {
mOverflingEnabled ? mOverflingDistance : 0, 0);
}
- public boolean startScroll(int distance, int min, int max) {
+ // Returns the distance that over the scroll limit.
+ public int startScroll(int distance, int min, int max) {
int currPosition = mScroller.getCurrX();
int finalPosition = mScroller.getFinalX();
int newPosition = Utils.clamp(finalPosition + distance, min, max);
@@ -85,9 +90,7 @@ public class ScrollerHelper {
mScroller.startScroll(
currPosition, 0, // startX, startY
newPosition - currPosition, 0, 0); // dx, dy, duration
- return true;
- } else {
- return false;
}
+ return finalPosition + distance - newPosition;
}
}
diff --git a/src/com/android/gallery3d/ui/SlotView.java b/src/com/android/gallery3d/ui/SlotView.java
index 3a4de3965..4b0dc2950 100644
--- a/src/com/android/gallery3d/ui/SlotView.java
+++ b/src/com/android/gallery3d/ui/SlotView.java
@@ -147,7 +147,15 @@ public class SlotView extends GLView {
@Override
protected void onLayout(boolean changeSize, int l, int t, int r, int b) {
if (!changeSize) return;
+
+ // Make sure we are still at a resonable scroll position after the size
+ // is changed (like orientation change). We choose to keep the center
+ // visible slot still visible. This is arbitrary but reasonable.
+ int visibleIndex =
+ (mLayout.getVisibleStart() + mLayout.getVisibleEnd()) / 2;
mLayout.setSize(r - l, b - t);
+ makeSlotVisible(visibleIndex);
+
onLayoutChanged(r - l, b - t);
if (mOverscrollEffect == OVERSCROLL_3D) {
mPaper.setSize(r - l, b - t);
@@ -219,6 +227,10 @@ public class SlotView extends GLView {
mDownInScrolling = !mScroller.isFinished();
mScroller.forceFinished();
break;
+ case MotionEvent.ACTION_UP:
+ mPaper.onRelease();
+ invalidate();
+ break;
}
return true;
}
@@ -242,17 +254,30 @@ public class SlotView extends GLView {
long currentTimeMillis = canvas.currentAnimationTimeMillis();
boolean more = mScroller.advanceAnimation(currentTimeMillis);
- boolean paperActive = (mOverscrollEffect == OVERSCROLL_3D)
- && mPaper.advanceAnimation(currentTimeMillis);
+ int oldX = mScrollX;
updateScrollPosition(mScroller.getPosition(), false);
+
+ boolean paperActive = false;
+ if (mOverscrollEffect == OVERSCROLL_3D) {
+ // Check if an edge is reached and notify mPaper if so.
+ int newX = mScrollX;
+ int limit = mLayout.getScrollLimit();
+ if (oldX > 0 && newX == 0 || oldX < limit && newX == limit) {
+ float v = mScroller.getCurrVelocity();
+ if (newX == limit) v = -v;
+ mPaper.edgeReached(v);
+ }
+ paperActive = mPaper.advanceAnimation();
+ }
+
+ more |= paperActive;
+
float interpolate = 1f;
if (mAnimation != null) {
more |= mAnimation.calculate(currentTimeMillis);
interpolate = mAnimation.value;
}
- more |= paperActive;
-
if (WIDE) {
canvas.translate(-mScrollX, 0, 0);
} else {
@@ -643,10 +668,10 @@ public class SlotView extends GLView {
MotionEvent e2, float distanceX, float distanceY) {
cancelDown();
float distance = WIDE ? distanceX : distanceY;
- boolean canMove = mScroller.startScroll(
+ int overDistance = mScroller.startScroll(
Math.round(distance), 0, mLayout.getScrollLimit());
- if (mOverscrollEffect == OVERSCROLL_3D && !canMove) {
- mPaper.overScroll(distance);
+ if (mOverscrollEffect == OVERSCROLL_3D && overDistance != 0) {
+ mPaper.overScroll(overDistance);
}
invalidate();
return true;