package cc.shinichi.library.view.helper; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.animation.LinearInterpolator; import android.widget.RelativeLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import cc.shinichi.library.ImagePreview; import cc.shinichi.library.view.nine.ViewHelper; import cc.shinichi.library.view.photoview.PhotoView; import cc.shinichi.library.view.subsampling.SubsamplingScaleImageView; /** * @author 工藤 * @email qinglingou@gmail.com * create at 2018/10/19 11:22 * description:辅助下拉关闭图片 */ public class DragCloseView extends RelativeLayout { private static final String TAG = DragCloseView.class.getSimpleName(); private final static int MAX_EXIT_Y = 500; private final static long DURATION = 200; private SubsamplingScaleImageView imageStatic; private PhotoView imageAnime; private View videoView; private float mDownX; private float mDownY; private float mTranslationY; private onAlphaChangedListener mOnAlphaChangedListener; public DragCloseView(Context context) { this(context, null); } public DragCloseView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public DragCloseView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onFinishInflate() { super.onFinishInflate(); imageStatic = (SubsamplingScaleImageView) getChildAt(0); imageAnime = (PhotoView) getChildAt(1); videoView = getChildAt(2); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (ImagePreview.Companion.getInstance().isEnableDragClose()) { int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { mDownX = ev.getRawX(); mDownY = ev.getRawY(); } else if (action == MotionEvent.ACTION_MOVE) { return canInterceptDrag(ev); } } return false; } private boolean canInterceptDrag(MotionEvent ev) { float diffX = Math.abs(ev.getX() - mDownX); float diffY = Math.abs(ev.getY() - mDownY); if (diffY <= diffX) { // 下拉手势,Y 轴位移小于 X 轴位移,是横向滑动,不拦截 return false; } getParent().requestDisallowInterceptTouchEvent(true); if (imageStatic != null && imageStatic.getVisibility() == View.VISIBLE) { // 静图 boolean isAtEdge = ImagePreview.Companion.getInstance().isEnableDragCloseIgnoreScale() ? imageStatic.getScale() <= (imageStatic.getMinScale() + 0.001F) || imageStatic.isAtYEdge() : imageStatic.getScale() <= (imageStatic.getMinScale() + 0.001F) && imageStatic.isAtYEdge(); return isAtEdge && (imageStatic.getMaxTouchCount() == 0 || imageStatic.getMaxTouchCount() == 1); } else if (imageAnime != null && imageAnime.getVisibility() == View.VISIBLE) { // 动图 return imageAnime.getScale() <= (imageAnime.getMinimumScale() + 0.001F) && (imageAnime.getMaxTouchCount() == 0 || imageAnime.getMaxTouchCount() == 1); } else if (videoView != null && videoView.getVisibility() == View.VISIBLE) { // 视频 return true; } return false; } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction() & event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: mDownX = event.getRawX(); mDownY = event.getRawY(); case MotionEvent.ACTION_MOVE: if (ImagePreview.Companion.getInstance().isEnableDragClose()) { if ((imageAnime != null && imageAnime.getVisibility() == View.VISIBLE) || (imageStatic != null && imageStatic.getVisibility() == View.VISIBLE) || (videoView != null && videoView.getVisibility() == View.VISIBLE) ) { onOneFingerPanActionMove(event); } } break; case MotionEvent.ACTION_UP: onActionUp(); break; default: break; } return true; } /** * 处理拖拽事件 * * @param event */ private void onOneFingerPanActionMove(MotionEvent event) { float moveY = event.getRawY(); mTranslationY = moveY - mDownY; if (mOnAlphaChangedListener != null) { mOnAlphaChangedListener.onTranslationYChanged(event, mTranslationY); } setTranslationY(mTranslationY); } private void onActionUp() { // 是否启用上拉关闭 boolean enableUpDragClose = ImagePreview.Companion.getInstance().isEnableUpDragClose(); if (enableUpDragClose) { if (Math.abs(mTranslationY) > MAX_EXIT_Y) { if (null != mOnAlphaChangedListener) { mOnAlphaChangedListener.onExit(); } exit(); } else { resetCallBackAnimation(); } } else { if (mTranslationY > MAX_EXIT_Y) { if (null != mOnAlphaChangedListener) { mOnAlphaChangedListener.onExit(); } exit(); } else { resetCallBackAnimation(); } } } private void resetCallBackAnimation() { ValueAnimator animatorY = ValueAnimator.ofFloat(mTranslationY, 0); animatorY.setDuration(DURATION); animatorY.addUpdateListener(animation -> { mTranslationY = (float) animation.getAnimatedValue(); setTranslationY(mTranslationY); }); animatorY.addListener(new SimpleAnimatorListener() { @Override public void onAnimationEnd(@NonNull Animator animation) { mTranslationY = 0; reset(); } }); animatorY.start(); } private void exit() { ValueAnimator animatorExit; if (mTranslationY > 0) { animatorExit = ValueAnimator.ofFloat(mTranslationY, getHeight()); } else { animatorExit = ValueAnimator.ofFloat(mTranslationY, -getHeight()); } animatorExit.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { float fraction = (float) animation.getAnimatedValue(); ViewHelper.setScrollY(DragCloseView.this, -(int) fraction); } }); animatorExit.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(@NonNull Animator animation) { } @Override public void onAnimationEnd(@NonNull Animator animation) { ((Activity) getContext()).finish(); } @Override public void onAnimationCancel(@NonNull Animator animation) { } @Override public void onAnimationRepeat(@NonNull Animator animation) { } }); animatorExit.setDuration(DURATION); animatorExit.setInterpolator(new LinearInterpolator()); animatorExit.start(); } /** * 暴露的回调方法(可根据位移距离或者alpha来改变主UI控件的透明度等 */ public void setOnAlphaChangeListener(onAlphaChangedListener alphaChangeListener) { mOnAlphaChangedListener = alphaChangeListener; } private void reset() { if (null != mOnAlphaChangedListener) { mOnAlphaChangedListener.onTranslationYChanged(null, mTranslationY); } } public abstract static class SimpleAnimatorListener implements Animator.AnimatorListener { @Override public void onAnimationStart(@NonNull Animator animation) { } @Override public void onAnimationCancel(@NonNull Animator animation) { } @Override public void onAnimationRepeat(@NonNull Animator animation) { } } public interface onAlphaChangedListener { void onTranslationYChanged(MotionEvent event, float translationY); void onExit(); } }