diff --git a/photoview/src/main/java/com/github/chrisbanes/photoview/OnSwipeCloseListener.java b/photoview/src/main/java/com/github/chrisbanes/photoview/OnSwipeCloseListener.java new file mode 100644 index 00000000..a200e8e7 --- /dev/null +++ b/photoview/src/main/java/com/github/chrisbanes/photoview/OnSwipeCloseListener.java @@ -0,0 +1,22 @@ +package com.github.chrisbanes.photoview; + +/** + * A callback to be invoked when swiped up or down + */ +public interface OnSwipeCloseListener { + /** + * A callback while swiping + * @param delta Amount of movement + */ + void onProgress(float delta); + + /** + * A callback when the amount of movement exceeds the threshold + */ + void onFinish(); + + /** + * A callback if the threshold is not exceeded when release user finger + */ + void onCancel(); +} \ No newline at end of file diff --git a/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoView.java b/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoView.java index 8a8ba0a7..b4913a96 100644 --- a/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoView.java +++ b/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoView.java @@ -186,6 +186,10 @@ public float getScale() { return attacher.getScale(); } + public float getMinCloseThreshold() { + return attacher.getMinCloseThreshold(); + } + public void setAllowParentInterceptOnEdge(boolean allow) { attacher.setAllowParentInterceptOnEdge(allow); } @@ -226,6 +230,10 @@ public void setOnViewDragListener(OnViewDragListener listener) { attacher.setOnViewDragListener(listener); } + public void setOnSwipeCloseListener(OnSwipeCloseListener listener) { + attacher.setOnSwipeCloseListener(listener); + } + public void setScale(float scale) { attacher.setScale(scale); } @@ -238,6 +246,10 @@ public void setScale(float scale, float focalX, float focalY, boolean animate) { attacher.setScale(scale, focalX, focalY, animate); } + public void setMinCloseThreshold(float minCloseThreshold) { + attacher.setMinCloseThreshold(minCloseThreshold); + } + public void setZoomTransitionDuration(int milliseconds) { attacher.setZoomTransitionDuration(milliseconds); } diff --git a/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java b/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java index 55965b81..f5cdeb3a 100644 --- a/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java +++ b/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java @@ -24,6 +24,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.View.OnLongClickListener; +import android.view.ViewConfiguration; import android.view.ViewParent; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; @@ -42,6 +43,7 @@ public class PhotoViewAttacher implements View.OnTouchListener, private static float DEFAULT_MAX_SCALE = 3.0f; private static float DEFAULT_MID_SCALE = 1.75f; private static float DEFAULT_MIN_SCALE = 1.0f; + private static float DEFAULT_MIN_CLOSE_THRESHOLD = 1000.0f; private static int DEFAULT_ZOOM_DURATION = 200; private static final int HORIZONTAL_EDGE_NONE = -1; @@ -59,6 +61,8 @@ public class PhotoViewAttacher implements View.OnTouchListener, private float mMinScale = DEFAULT_MIN_SCALE; private float mMidScale = DEFAULT_MID_SCALE; private float mMaxScale = DEFAULT_MAX_SCALE; + private float mMinCloseThreshold = DEFAULT_MIN_CLOSE_THRESHOLD; + private boolean mAllowParentInterceptOnEdge = true; private boolean mBlockParentIntercept = false; @@ -86,6 +90,7 @@ public class PhotoViewAttacher implements View.OnTouchListener, private OnScaleChangedListener mScaleChangeListener; private OnSingleFlingListener mSingleFlingListener; private OnViewDragListener mOnViewDragListener; + private OnSwipeCloseListener mOnSwipeCloseListener; private FlingRunnable mCurrentFlingRunnable; private int mHorizontalScrollEdge = HORIZONTAL_EDGE_BOTH; @@ -385,6 +390,9 @@ public boolean onTouch(View v, MotionEvent ev) { if (mGestureDetector != null && mGestureDetector.onTouchEvent(ev)) { handled = true; } + if(mOnSwipeCloseListener != null) { + swipe(v, ev); + } } return handled; @@ -409,6 +417,14 @@ public void setMaximumScale(float maximumScale) { mMaxScale = maximumScale; } + public float getMinCloseThreshold() { + return mMinCloseThreshold; + } + + public void setMinCloseThreshold(float minCloseThreshold) { + mMinCloseThreshold = minCloseThreshold; + } + public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) { Util.checkZoomLevels(minimumScale, mediumScale, maximumScale); mMinScale = minimumScale; @@ -444,6 +460,10 @@ public void setOnViewDragListener(OnViewDragListener listener) { mOnViewDragListener = listener; } + public void setOnSwipeCloseListener(OnSwipeCloseListener listener) { + mOnSwipeCloseListener = listener; + } + public void setScale(float scale) { setScale(scale, false); } @@ -725,6 +745,32 @@ private void cancelFling() { } } + private void swipe(View v, MotionEvent ev) { + if(getScale() == mMinScale) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + // Save Y coordinate position in order to drag the image + v.setTag(ev.getY()); + break; + case MotionEvent.ACTION_MOVE: + float delta = ev.getRawY() - (Float) v.getTag(); + // If the delta is doubleTapSlop or less, it will not move + if (Math.abs(delta) > ViewConfiguration.get(v.getContext()).getScaledDoubleTapSlop()) { + v.setTranslationY(delta); + mOnSwipeCloseListener.onProgress(delta); + } + break; + case MotionEvent.ACTION_UP: + if (Math.abs(v.getTranslationY()) > mMinCloseThreshold) { + mOnSwipeCloseListener.onFinish(); + } else { + mOnSwipeCloseListener.onCancel(); + } + break; + } + } + } + private class AnimatedZoomRunnable implements Runnable { private final float mFocalX, mFocalY; diff --git a/sample/src/main/java/com/github/chrisbanes/photoview/sample/SimpleSampleActivity.java b/sample/src/main/java/com/github/chrisbanes/photoview/sample/SimpleSampleActivity.java index f212c86e..f2459793 100755 --- a/sample/src/main/java/com/github/chrisbanes/photoview/sample/SimpleSampleActivity.java +++ b/sample/src/main/java/com/github/chrisbanes/photoview/sample/SimpleSampleActivity.java @@ -15,6 +15,7 @@ */ package com.github.chrisbanes.photoview.sample; +import android.animation.ObjectAnimator; import android.graphics.Matrix; import android.graphics.RectF; import android.graphics.drawable.Drawable; @@ -30,6 +31,7 @@ import com.github.chrisbanes.photoview.OnMatrixChangedListener; import com.github.chrisbanes.photoview.OnPhotoTapListener; import com.github.chrisbanes.photoview.OnSingleFlingListener; +import com.github.chrisbanes.photoview.OnSwipeCloseListener; import com.github.chrisbanes.photoview.PhotoView; import java.util.Random; @@ -139,6 +141,26 @@ public boolean onMenuItemClick(MenuItem item) { mPhotoView.setOnMatrixChangeListener(new MatrixChangeListener()); mPhotoView.setOnPhotoTapListener(new PhotoTapListener()); mPhotoView.setOnSingleFlingListener(new SingleFlingListener()); + mPhotoView.setOnSwipeCloseListener(new OnSwipeCloseListener() { + @Override + public void onProgress(float delta) { + float threshold = mPhotoView.getMinCloseThreshold(); + mPhotoView.setAlpha((threshold - Math.abs(delta))/threshold); + } + + @Override + public void onFinish() { + finish(); + } + + @Override + public void onCancel() { + ObjectAnimator.ofFloat(mPhotoView, "translationY", mPhotoView.getTranslationY(), 0f) + .setDuration(100L) + .start(); + mPhotoView.setAlpha(1.0f); + } + }); } private class PhotoTapListener implements OnPhotoTapListener {