能否重写SimpleOnGestureListener检测释放?抛物线滑动回弹实现疑问
实现Tinder式抛物线滑动回弹效果
你已经搭好了抛物线滑动的核心逻辑,要实现未滑满时的回弹效果,核心就是捕获用户抬手动作,再根据当前滑动距离判断是否触发平滑回弹动画。下面给你两种基于现有代码的扩展方案,都是非常实用的实现方式:
方案一:在OnTouchListener中直接处理ACTION_UP事件
你的onTouch方法目前只是把事件转发给GestureDetector,我们可以先在这里拦截ACTION_UP事件,执行完回弹逻辑后再传递事件,这样逻辑更直观:
public class MoveViewTouchListener implements View.OnTouchListener { public GestureDetector mGestureDetector; public View mView; public double a; public double b; public double c; // 自定义回弹阈值:比如滑动距离未超过屏幕1/3就触发回弹 private float mReboundThreshold; public MoveViewTouchListener(View view, double a, double b, double c) { mGestureDetector = new GestureDetector(view.getContext(), mGestureListener); mView = view; this.a = a; this.b = b; this.c = c; // 初始化阈值:取当前屏幕宽度的1/3 mReboundThreshold = view.getContext().getResources().getDisplayMetrics().widthPixels / 3f; } @Override public boolean onTouch(View v, MotionEvent event) { // 优先处理抬手事件 if (event.getAction() == MotionEvent.ACTION_UP) { checkAndDoRebound(); } return mGestureDetector.onTouchEvent(event); } // 判断是否需要回弹并执行平滑动画 private void checkAndDoRebound() { float currentX = mView.getTranslationX(); // 滑动距离未达阈值时触发回弹 if (Math.abs(currentX) < mReboundThreshold) { // 使用ValueAnimator实现自然的回弹过渡 ValueAnimator animatorX = ValueAnimator.ofFloat(currentX, 0f); animatorX.setDuration(300); // 动画时长300ms,可根据需求调整 animatorX.addUpdateListener(animation -> { float value = (float) animation.getAnimatedValue(); mView.setTranslationX(value); // 同步更新Y轴的抛物线位置 double y = a * value * value + b * value + c; mView.setTranslationY((float) y); }); animatorX.start(); } // 若超过阈值,可添加滑出屏幕的动画逻辑(可选) } private GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { private float mMotionDownX, mMotionDownY; @Override public boolean onDown(MotionEvent e) { mMotionDownX = e.getRawX() - mView.getTranslationX(); mMotionDownY = e.getRawY() - mView.getTranslationY(); return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { float newX = e2.getRawX() - mMotionDownX; mView.setTranslationX(newX); // 注意:这里直接用构造函数传入的a/b/c,避免覆盖参数 double y = a * newX * newX + b * newX + c; Log.d("Chex ","X = " + newX +", Y = " + y); mView.setTranslationY((float)y); return true; } }; }
方案二:重写SimpleOnGestureListener的onUp方法
GestureDetector.SimpleOnGestureListener本身已经实现了OnGestureListener的所有回调,你可以直接重写onUp方法来捕获抬手动作,让逻辑更内聚:
public class MoveViewTouchListener implements View.OnTouchListener { public GestureDetector mGestureDetector; public View mView; public double a; public double b; public double c; private float mReboundThreshold; public MoveViewTouchListener(View view, double a, double b, double c) { mGestureDetector = new GestureDetector(view.getContext(), mGestureListener); mView = view; this.a = a; this.b = b; this.c = c; mReboundThreshold = view.getContext().getResources().getDisplayMetrics().widthPixels / 3f; } @Override public boolean onTouch(View v, MotionEvent event) { return mGestureDetector.onTouchEvent(event); } private void startReboundAnimation() { float currentX = mView.getTranslationX(); ValueAnimator animatorX = ValueAnimator.ofFloat(currentX, 0f); animatorX.setDuration(300); animatorX.addUpdateListener(animation -> { float value = (float) animation.getAnimatedValue(); mView.setTranslationX(value); double y = a * value * value + b * value + c; mView.setTranslationY((float) y); }); animatorX.start(); } private GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { private float mMotionDownX, mMotionDownY; @Override public boolean onDown(MotionEvent e) { mMotionDownX = e.getRawX() - mView.getTranslationX(); mMotionDownY = e.getRawY() - mView.getTranslationY(); return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { float newX = e2.getRawX() - mMotionDownX; mView.setTranslationX(newX); double y = a * newX * newX + b * newX + c; Log.d("Chex ","X = " + newX +", Y = " + y); mView.setTranslationY((float)y); return true; } // 重写onUp回调,捕获用户抬手动作 @Override public boolean onUp(MotionEvent e) { float currentX = mView.getTranslationX(); if (Math.abs(currentX) < mReboundThreshold) { startReboundAnimation(); } return super.onUp(e); } }; }
关键注意事项
- 避免参数覆盖:你的原代码在
onScroll里重新定义了a/b/c局部变量,这会覆盖构造函数传入的参数,已经帮你修正了。 - 阈值自定义:可以根据产品需求调整
mReboundThreshold的取值,比如改成屏幕宽度的一半,或者固定像素值。 - 动画平滑性:用
ValueAnimator实现回弹是最自然的方式,避免直接设置translationX/Y导致的生硬跳转。 - 扩展滑出逻辑:如果需要完全复刻Tinder的体验,当滑动距离超过阈值时,可以添加快速滑出屏幕的动画,而不是保持当前位置。
内容的提问来源于stack exchange,提问作者Nicholas Muir
相关产品推荐
相关产品推荐

