Android手势处理看这一篇就够了

作者: 08_carmelo | 来源:发表于2017-11-25 21:36 被阅读1377次

    前言

    Android提供了几种处理手势的监听器,但是监听器之间的关系比较乱,新手不太容易知道该用哪一种?什么时候该重写onTouchEvent什么时候直接实现监听器。本文对Android提供的手势监听器一一说明,并在文末给出实际项目的方案选择建议。
    常见手势有:单击,长按,双击,滑动,快速滑动,缩放
    处理方法有两种:重写onTouchEvent来自己识别各种手势,也可以直接使用Android提供的各种手势监听器。

    Android手势监听器

    GestureDetector.OnGestureListener

    这个监听器能处理大部分手势,但是实际项目一般不会用它,后面说明原因
    使用方法:

    1. Activity或者View implements GestureDetector.OnGestureListener
    2. 把onTouchEvent托管给GestureDetector
    @Override
        public boolean onTouchEvent(MotionEvent event) {
            return gd.onTouchEvent(event);
        }
    

    必须重写里面的6个方法:按下,onSowPress(按下没松开),单击,拖动,长按,快速滑动。
    这个监听器的缺陷:

    1. 必须实现该接口的所有方法,但使用者一般不关心所有的事件
    2. onDown(),在原生的onTouchEvent已经可以处理了,多余
    3. onSingleTapUp不是真正的单击:快速双击会执行两次onSingleTapUp
    4. onScroll()只能处理单指,不能处理多指的缩放
    5. 快速滑动会执行多次onScroll 在执行onFling()
    6. 不能处理双击事件
    SimpleOnGestureListener

    针对上面的缺陷,这个类可以按需实现,并且支持真正的单击onSingleTapConfirmed(双击不会执行此方法)和双击

    看下源码:

    public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,
                OnContextClickListener
    

    可以看出这个类包涵了OnGestureListener所有手势,增加了双击判断。
    使用方法:

    public class MainActivity extends Activity{
        private GestureDetector gd;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            gd = new GestureDetector(new MyGesture());
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            return gd.onTouchEvent(event);
        }
        class MyGesture extends GestureDetector.SimpleOnGestureListener{
            //按需实现自己关心的手势
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                return super.onDoubleTap(e);
            }
        }
    }
    
    OnDoubleTapListener

    如果你只是关心点击和双击事件,那么直接实现OnDoubleTapListener 是更好的选择

    ScaleGestureDetector.OnScaleGestureListener
    public class MainActivity extends Activity implements ScaleGestureDetector.OnScaleGestureListener{
        private ScaleGestureDetector gd;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            gd = new ScaleGestureDetector(this,this);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            gd.onTouchEvent(event);
            return true;
        }
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            Log.d("dml"," onScale detector = " + detector.getScaleFactor());
            return false;
        }
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return true;
        }
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
        }
    }
    

    处理缩放事件,注意onScaleBegin碧玺返回true!才能执行onScale,这个接口的缺陷:不能自己控制灵敏系数

    onTouchEvent

    这是在手势监听器上面的回调,所以在这里当然可以处理所有的手势。处理缩放和拖动:

    public class MainActivity extends Activity{
        /**
         * 记录当前操作的状态,可选值为STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE
         */
        private int currentStatus;
        /**
         * 初始化状态常量
         */
        public static final int STATUS_INIT = 1;
        /**
         * 图片放大状态常量
         */
        public static final int STATUS_ZOOM_OUT = 2;
        /**
         * 图片缩小状态常量
         */
        public static final int STATUS_ZOOM_IN = 3;
        /**
         * 图片拖动状态常量
         */
        public static final int STATUS_MOVE = 4;
        /**
         * 记录上次手指移动时的横坐标
         */
        private float lastXMove = -1;
        /**
         * 记录上次手指移动时的纵坐标
         */
        private float lastYMove = -1;
        /**
         * 记录上次两指之间的距离
         */
        private double lastFingerDis;
        /**
         * 记录手指移动的距离所造成的缩放比例
         */
        private float scaledRatio;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_POINTER_DOWN:
                    if (event.getPointerCount() == 2) {
                        // 当有两个手指按在屏幕上时,计算两指之间的距离
                        lastFingerDis = distanceBetweenFingers(event);
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (event.getPointerCount() == 1) {
                        // 只有单指按在屏幕上移动时,为拖动状态
                        float xMove = event.getX();
                        float yMove = event.getY();
                        if (lastXMove == -1 && lastYMove == -1) {
                            lastXMove = xMove;
                            lastYMove = yMove;
                        }
                        currentStatus = STATUS_MOVE;
                        // ------------拖动数值----------------
                        //after(xMove,yMove),befor(lastXMove,lastYMove)
                        lastXMove = xMove;
                        lastYMove = yMove;
    
                    } else if (event.getPointerCount() == 2) {
                        // 有两个手指按在屏幕上移动时,为缩放状态
                        double fingerDis = distanceBetweenFingers(event);
                        if (fingerDis > lastFingerDis) {
                            currentStatus = STATUS_ZOOM_OUT;
                        } else {
                            currentStatus = STATUS_ZOOM_IN;
                        }
                        // ------------缩放倍数----------------
                        scaledRatio = (float) (fingerDis / lastFingerDis);
                    }
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    if (event.getPointerCount() == 2) {
                        // 手指离开屏幕时将临时值还原
                        lastXMove = -1;
                        lastYMove = -1;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    // 手指离开屏幕时将临时值还原
                    lastXMove = -1;
                    lastYMove = -1;
                    break;
                default:
                    break;
            }
            return true;
        }
        /**
         * 计算两个手指之间的距离。
         *
         * @param event
         * @return 两个手指之间的距离
         */
        private double distanceBetweenFingers(MotionEvent event) {
            float disX = Math.abs(event.getX(0) - event.getX(1));
            float disY = Math.abs(event.getY(0) - event.getY(1));
            return Math.sqrt(disX * disX + disY * disY);
        }
    }
    

    总结

    根据实际场景选择处理方式:

    1. 简单的单击和双击和长按:用SimpleOnGestureListener
    2. 简单的缩放,没有拖动:ScaleGestureDetector
    3. 缩放+拖动:onTouchEvent
    4. 多手势复杂场景:SimpleOnGestureListener + onTouchEvent

    相关文章

      网友评论

        本文标题:Android手势处理看这一篇就够了

        本文链接:https://www.haomeiwen.com/subject/xslmsxtx.html