Android GestureDetector支持View拖动、长按拖动

文章介绍了如何在Android应用中使用GestureDetector组件来检测并响应滑动和长按事件,包括基本的滑动操作和长按后拖动功能的实现,以及相关方法和事件处理逻辑。
摘要由CSDN通过智能技术生成

GestureDetector:是Android官方提供的手势识别组件,可以识别点击、双击、长按事件、拖动等等手势操作,这里我们借助GestureDetector支持View的滑动,并通过拓展代码支持长按拖动。

以下代码已经过测试可用,需要的可自行拷贝哈。

拖动

public class MoveView extends AppCompatButton {
    private GestureDetector mGesture;

    public MoveView(Context context) {
        super(context);
    }

    public MoveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public MoveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGesture = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                setTranslationX(getTranslationX() + e2.getX() - e1.getX());
                setTranslationY(getTranslationY() + e2.getY() - e1.getY());
                return true;
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mGesture.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_UP) { // 自动归位
            placeIconToSide();
        }
        return super.onTouchEvent(event);
    }

    /**
     * 移动位置对齐右边
     * 如需要判断对齐左右两边的,可以在onTouchEvent中记录ACTION_UP时的x,然后placeIconToSide中判断 2 * x > getScreenWidth() - width,
     *  -- true:setX(getScreenWidth() - width)
     *  -- false:setX(0)
     */
    private void placeIconToSide() {
        int width = getMeasuredWidth();
        int maxX = getScreenWidth() - width; // maxX为控件左边位置,即屏幕宽度 - 控件宽度
        setX(maxX); // 移动控件对齐
    }

    /**
     * @return 屏幕宽度
     */
    private int getScreenWidth() {
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        if (wm == null) return -1;
        Point point = new Point();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            wm.getDefaultDisplay().getRealSize(point);
        } else {
            wm.getDefaultDisplay().getSize(point);
        }
        return point.x;
    }
}

可直接拷贝上面代码,放在XML后测试触摸拖动

长按拖动

public class LongTouchMoveView extends AppCompatButton {

    public LongTouchMoveView(Context context) {
        super(context);
    }

    public LongTouchMoveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private float mLastFocusX;
    private float mLastFocusY;
    private boolean mInLongPress;
    private MotionEvent mCurrentDownEvent;
    private GestureDetector mGestureDetector;
    private GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener;


    public LongTouchMoveView(Context context, AttributeSet attrs) {
        super(context, attrs);

        setOnClickListener(v -> {
            //ToastUtils.showShort("onClick");
        });

        mSimpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
            @Override
            public void onLongPress(MotionEvent e) {
                vibrate(50);
                mInLongPress = true;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                if (!mInLongPress) return false;
                setTranslationX(getTranslationX() + e2.getX() - e1.getX());
                setTranslationY(getTranslationY() + e2.getY() - e1.getY());
                return true;
            }
        };

        mGestureDetector = new GestureDetector(context, mSimpleOnGestureListener);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean consumeEvent = false;
        boolean result = mGestureDetector.onTouchEvent(ev);
        if (!result) { // GestureDetector不支持长按滑动,这里通过自定义代码进行补充支持(GestureDetector#onTouchEvent()#MotionEvent.ACTION_MOVE事件处理时,只要是长按的直接不消费事件,即不予处理)
            /* 下面摘抄自 GestureDetector#onTouchEvent() 源码 */
            final int action = ev.getAction();
            final boolean pointerUp =
                (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
            final int skipIndex = pointerUp ? ev.getActionIndex() : -1;

            // Determine focal point
            float sumX = 0, sumY = 0;
            final int count = ev.getPointerCount();
            for (int i = 0; i < count; i++) {
                if (skipIndex == i) continue;
                sumX += ev.getX(i);
                sumY += ev.getY(i);
            }
            final int div = pointerUp ? count - 1 : count;
            final float focusX = sumX / div;
            final float focusY = sumY / div;

            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    if (mCurrentDownEvent != null) {
                        mCurrentDownEvent.recycle();
                    }
                    mCurrentDownEvent = MotionEvent.obtain(ev);
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mInLongPress) { // 正在长按中,支持滑动
                        final float scrollX = mLastFocusX - focusX;
                        final float scrollY = mLastFocusY - focusY;
                        if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
                            mSimpleOnGestureListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                            mLastFocusX = focusX;
                            mLastFocusY = focusY;
                            consumeEvent = true; // 消费 长按滑动
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (mInLongPress) {
                        placeIconToSide();
                        mInLongPress = false;
                        consumeEvent = true; // 消费 提起动作
                    }
                    break;
            }
        }
        if (consumeEvent) {
            return true; // 自己处理长按滑动、提起;(避免与单击事件冲突)
        } else {
            return super.onTouchEvent(ev); // 继续走默认路径
        }
    }

    /**
     * 移动位置对齐右边
     * 如需要判断对齐左右两边的,可以在onTouchEvent中记录ACTION_UP时的x,然后placeIconToSide中判断 2 * x > getScreenWidth() - width,
     * -- true:setX(getScreenWidth() - width)
     * -- false:setX(0)
     */
    private void placeIconToSide() {
        int width = getMeasuredWidth();
        int maxX = getScreenWidth() - width;
        setX(maxX);
    }

    /**
     * @return 屏幕宽度
     */
    private int getScreenWidth() {
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        if (wm == null) return -1;
        Point point = new Point();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            wm.getDefaultDisplay().getRealSize(point);
        } else {
            wm.getDefaultDisplay().getSize(point);
        }
        return point.x;
    }

    private void vibrate(final long milliseconds) {
        Vibrator vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
        if (vibrator == null) return;
        vibrator.vibrate(milliseconds);
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在 Android 应用程序中实现旋转和拖动手势,可以使用 GestureDetector 和 ScaleGestureDetector 类。这两个类都是 Android SDK 中的标准类,用于检测用户的手势。 下面是一个简单的示例代码,演示如何使用 GestureDetector 和 ScaleGestureDetector 实现旋转和拖动手势: ```java public class MainActivity extends AppCompatActivity { private ImageView imageView; private float scaleFactor = 1.0f; private float rotationDegrees = 0.f; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = findViewById(R.id.imageView); // 设置双指缩放手势监听器 ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() { @Override public boolean onScale(ScaleGestureDetector detector) { scaleFactor *= detector.getScaleFactor(); imageView.setScaleX(scaleFactor); imageView.setScaleY(scaleFactor); return true; } }); // 设置单指旋转手势监听器 final GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { rotationDegrees -= distanceX; imageView.setRotation(rotationDegrees); return true; } }); // 设置 ImageView 触摸事件监听器,用于将手势事件传递给上面的手势监听器 imageView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { scaleGestureDetector.onTouchEvent(event); gestureDetector.onTouchEvent(event); return true; } }); } } ``` 在这个示例中,我们创建了一个 ImageView,用于演示手势操作的效果。然后我们创建了一个 ScaleGestureDetectorGestureDetector 实例,并将它们分别用于监听双指缩放和单指旋转手势。 最后,我们将 ImageView 的触摸事件传递给上面的手势监听器,以便检测用户的手势操作。当检测到手势操作时,我们通过修改 ImageView 的缩放和旋转属性来实现旋转和拖动操作。 请注意,这只是一个简单的示例代码,实际应用中还需要对手势操作进行更多的处理和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值