diff options
author | Doris Liu <tianliu@google.com> | 2013-10-04 13:22:09 -0700 |
---|---|---|
committer | Doris Liu <tianliu@google.com> | 2013-10-10 18:02:48 -0700 |
commit | d70f0fb68064e170fb2cf041a707ed04d68040f9 (patch) | |
tree | c4ed99de7f92602ac839037efb2bbbcbc664fdf7 /src/com/android/camera/ui/FilmStripView.java | |
parent | 8c80782326d1cdc764b9a9a791b2c5525e8ef234 (diff) | |
download | android_packages_apps_Snap-d70f0fb68064e170fb2cf041a707ed04d68040f9.tar.gz android_packages_apps_Snap-d70f0fb68064e170fb2cf041a707ed04d68040f9.tar.bz2 android_packages_apps_Snap-d70f0fb68064e170fb2cf041a707ed04d68040f9.zip |
Add fling support in zoom view
Bug: 10679704
Change-Id: I7ab72fdf4e836495b2471c65fd57f952e863f1dd
Diffstat (limited to 'src/com/android/camera/ui/FilmStripView.java')
-rw-r--r-- | src/com/android/camera/ui/FilmStripView.java | 124 |
1 files changed, 120 insertions, 4 deletions
diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java index b6f83adf1..3e29dc0d3 100644 --- a/src/com/android/camera/ui/FilmStripView.java +++ b/src/com/android/camera/ui/FilmStripView.java @@ -17,6 +17,7 @@ package com.android.camera.ui; import android.animation.Animator; +import android.animation.AnimatorSet; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.app.Activity; @@ -25,7 +26,6 @@ import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.RectF; -import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Handler; import android.util.AttributeSet; @@ -34,7 +34,6 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.Scroller; @@ -53,6 +52,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private static final int BUFFER_SIZE = 5; private static final int GEOMETRY_ADJUST_TIME_MS = 400; private static final int SNAP_IN_CENTER_TIME_MS = 600; + private static final float FLING_COASTING_DURATION_S = 0.05f; private static final int ZOOM_ANIMATION_DURATION_MS = 200; private static final int CAMERA_PREVIEW_SWIPE_THRESHOLD = 300; private static final float FILM_STRIP_SCALE = 0.5f; @@ -61,6 +61,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private static final float TOLERANCE = 0.1f; // Only check for intercepting touch events within first 500ms private static final int SWIPE_TIME_OUT = 500; + private static final int DECELERATION_FACTOR = 4; private CameraActivity mActivity; private FilmStripGestureRecognizer mGestureRecognizer; @@ -377,6 +378,8 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { public void fling(float velocity); + public void flingInsideZoomView (float velocityX, float velocityY); + public void scrollToPosition(int position, int duration, boolean interruptible); public boolean goToNextItem(); @@ -1372,6 +1375,8 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return; } mScale = FULL_SCREEN_SCALE; + mController.cancelZoomAnimation(); + mController.cancelFlingAnimation(); current.resetTransform(); mController.cancelLoadingZoomedImage(); mZoomView.setVisibility(GONE); @@ -1882,6 +1887,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private final ValueAnimator mScaleAnimator; private ValueAnimator mZoomAnimator; + private AnimatorSet mFlingAnimator; private final MyScroller mScroller; private boolean mCanStopScroll; @@ -2065,6 +2071,94 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } @Override + public void flingInsideZoomView(float velocityX, float velocityY) { + if (!isZoomStarted()) { + return; + } + + final ViewItem current = mViewItem[mCurrentItem]; + if (current == null) { + return; + } + + final int factor = DECELERATION_FACTOR; + // Deceleration curve for distance: + // S(t) = s + (e - s) * (1 - (1 - t/T) ^ factor) + // Need to find the ending distance (e), so that the starting velocity + // is the velocity of fling. + // Velocity is the derivative of distance + // V(t) = (e - s) * factor * (-1) * (1 - t/T) ^ (factor - 1) * (-1/T) + // = (e - s) * factor * (1 - t/T) ^ (factor - 1) / T + // Since V(0) = V0, we have e = T / factor * V0 + s + + // Duration T should be long enough so that at the end of the fling, + // image moves at 1 pixel/s for about P = 50ms = 0.05s + // i.e. V(T - P) = 1 + // V(T - P) = V0 * (1 - (T -P) /T) ^ (factor - 1) = 1 + // T = P * V0 ^ (1 / (factor -1)) + + final float velocity = Math.max(Math.abs(velocityX), Math.abs(velocityY)); + // Dynamically calculate duration + final float duration = (float) (FLING_COASTING_DURATION_S + * Math.pow(velocity, (1f/ (factor - 1f)))); + + final float translationX = current.getTranslationX(); + final float translationY = current.getTranslationY(); + + final ValueAnimator decelerationX = ValueAnimator.ofFloat(translationX, + translationX + duration / factor * velocityX); + final ValueAnimator decelerationY = ValueAnimator.ofFloat(translationY, + translationY + duration / factor * velocityY); + + decelerationY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float transX = (Float) decelerationX.getAnimatedValue(); + float transY = (Float) decelerationY.getAnimatedValue(); + + current.updateTransform(transX, transY, mScale, + mScale, mDrawArea.width(), mDrawArea.height()); + } + }); + + mFlingAnimator = new AnimatorSet(); + mFlingAnimator.play(decelerationX).with(decelerationY); + mFlingAnimator.setDuration((int) (duration * 1000)); + mFlingAnimator.setInterpolator(new TimeInterpolator() { + @Override + public float getInterpolation(float input) { + return (float)(1.0f - Math.pow((1.0f - input), factor)); + } + }); + mFlingAnimator.addListener(new Animator.AnimatorListener() { + private boolean mCancelled = false; + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mCancelled) { + loadZoomedImage(); + } + mFlingAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + mFlingAnimator.start(); + } + + @Override public boolean stopScrolling(boolean forced) { if (!isScrolling()) { return true; @@ -2141,6 +2235,19 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { scaleTo(1f, GEOMETRY_ADJUST_TIME_MS); } + private void cancelFlingAnimation() { + // Cancels flinging for zoomed images + if (isFlingAnimationRunning()) { + mFlingAnimator.cancel(); + } + } + + private void cancelZoomAnimation() { + if (isZoomAnimationRunning()) { + mZoomAnimator.cancel(); + } + } + private void enterFullScreen() { if (mListener != null) { mListener.onDataFullScreenChange(mViewItem[mCurrentItem].getId(), true); @@ -2236,6 +2343,10 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return mScale > FULL_SCREEN_SCALE; } + public boolean isFlingAnimationRunning() { + return mFlingAnimator != null && mFlingAnimator.isRunning(); + } + public boolean isZoomAnimationRunning() { return mZoomAnimator != null && mZoomAnimator.isRunning(); } @@ -2398,6 +2509,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { @Override public boolean onDown(float x, float y) { + mController.cancelFlingAnimation(); if (!mController.stopScrolling(false)) { return false; } @@ -2417,7 +2529,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (currItem == null) { return false; } - if (mController.isZoomAnimationRunning()) { + if (mController.isZoomAnimationRunning() || mController.isFlingAnimationRunning()) { return false; } if (mController.isZoomStarted()) { @@ -2553,6 +2665,11 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (currItem == null) { return false; } + if (mController.isZoomStarted()) { + // Fling within the zoomed image + mController.flingInsideZoomView(velocityX, velocityY); + return true; + } if (Math.abs(velocityX) < Math.abs(velocityY)) { // ignore vertical fling. return true; @@ -2662,7 +2779,6 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return true; } - @Override public void onScaleEnd() { if (mScale > FULL_SCREEN_SCALE + TOLERANCE) { |