Android进阶Android开发经验谈Android进阶之路

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

2017-11-25  本文已影响1377人  08_carmelo

前言

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
上一篇 下一篇

猜你喜欢

热点阅读