android开发游记:多点触控解析与运用

原创 2016年05月30日 14:31:13

andorid 自2.0以后加入了对多点触控的支持,而多点触控的使用,在多数应用中是用不到的,或者用不用区别不大,但是在某些需要拖拽的控件中加入多点触控的支持会有更好的用户体验,最常见的比如下拉刷新,美团、京东、百度糯米、阿里旅行等应用都没有处理多点触控的情况,因此在拉动的时候用2只手连续拉动就会出现页面闪动的情况。毕竟只有少数闲得蛋疼的用户才会这样去拖拽,处不处理区别不大,但是在一些更加复杂的拖拽场合中多点触控的处理就很有必要了,比如qq的下拉抢红包。带着学习的态度,还是很有必要掌握的。

我们在触摸事件中可以得到MotionEvent对象,
先介绍MotionEvent的几个常用方法:

获取当前屏幕触摸点个数

getPointerCount()
这个方法可以获取触摸点个数,一般来说处理两个点就行了,3点的情况实在太少

获取的触摸状态

getActionMasked()

这个方法用于获取触摸事件的触摸状态,我们平时使用的getAction()方法是无法捕获多点触控的情况的,使用getActionMasked()就额外可以捕获到ACTION_POINTER_DOWNACTION_POINTER_UP事件

多点触控的特殊事件

MotionEvent.ACTION_POINTER_DOWN:
当屏幕上已经有一个点被按住,此时再按下其他点时触发。
MotionEvent.ACTION_POINTER_UP:
当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)。

每个触点分类处理

对触点进行分类处理是多点触控的核心思想,这里提供了3个方法帮助我们获取我们想要的触点:

//获取当前触点的index下标
getActionIndex() 
//获取触点唯一ID
getPointerId() 
//通过触点的ID获取index下标
findPointerIndex() 

我们得到的MotionEvent对象维护了一个有序的触点序列,这个序列记录了当前屏幕中的所有触点,我们可以通过index下标获取指定触点的信息,比如MotionEvent.getX()可以获取触摸点的X坐标,它还提供了一个> 重载方法MotionEvent.getX(index),获取指定触点的X坐标。

而这个有序序列的位置并不是不变的,根据用户触摸的顺序,序列的顺序是不断变化的,也就是说你使用一个下标在同一个触摸事件中两次获取到的不一定是同一个触点,为了解决这个问题系统为每个触点关联了唯一的ID,而这个ID是固定不变的,我们获取指定触点的思路就变成了 :1.使用ID获取index,2.再用index获取触点信息

如下通过一个ID获取指定触点的XY坐标:

//MotionEvent ev
int pointerIndex = ev.findPointerIndex(ev, mActivePointerId);
float x = ev.getX(pointerIndex);
float y = ev.getY(pointerIndex);

上面的代码还可以写成:

//MotionEvent ev
int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
float x = MotionEventCompat.getX(ev, pointerIndex);
float y = MotionEventCompat.getY(ev, pointerIndex);

MotionEventCompat是v4中引入的一个兼容低版本(API 8)的类,用于兼容低版本的MotionEvent,使用方法一> 致,后面的介绍都统一使用MotionEventCompat

示例

为了更好地理解多点触控原理,我们来做一个例子:自定义一个View可以双手连续拖拽,松开后弹回到原位

效果图:(注意这是两只手连续拖拽)

这里写图片描述

附上下载地址: demo下载地址

首先创建一个TestView继承自View:

    public class TestView extends View{
        public TestView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    }

重写dispatchTouchEvent,在里面调用一个用于处理多点触控的自定义方法dealMulTouchEvent

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        dealMulTouchEvent(event);
        return super.dispatchTouchEvent(event);
    }

多点触控分类处理

使用getActionMasked获取触摸事件进行分类处理:

    public void dealMulTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        switch (action) {
            case MotionEvent.ACTION_DOWN: 
                break;
            case MotionEvent.ACTION_MOVE: 
                break;
            case MotionEvent.ACTION_UP:
                break;
            case MotionEvent.ACTION_CANCEL:  
                break;
            case MotionEvent.ACTION_POINTER_DOWN: 
                break;
            case MotionEvent.ACTION_POINTER_UP: 
                break;
        }
    }

首先,在ACTION_DOWN的时候(第一只手按下),我们获取触点的x,y坐标,保存在mLastX和mLastY中,并获取当前触点ID,记作活动点,保存在一个类变量中:

    case MotionEvent.ACTION_DOWN: {
         final int pointerIndex = MotionEventCompat.getActionIndex(ev);
         final float x = MotionEventCompat.getX(ev, pointerIndex);
         final float y = MotionEventCompat.getY(ev, pointerIndex);
         // 记录开始拖拽时的坐标 (按下的坐标)
         mLastX = x;
         mLastY = y;
         // 记录当前触摸点的ID(活动点的ID)
         mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
         break;
    }

接着,在ACTION_MOVE的时候,我们通过活动点ID获取活动点的坐标,与上一次的坐标mLastX、mLastY进行差运算,计算横纵轴的拖拽距离dx、dy:

    case MotionEvent.ACTION_MOVE: {
         // 获取活动点坐标
         final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
         final float x = MotionEventCompat.getX(ev, pointerIndex);
         final float y = MotionEventCompat.getY(ev, pointerIndex);
         // 计算拖拽距离
         dy = y - mLastY;
         dx = x - mLastX;
         // 更新上次保存的坐标
         mLastX = x;
         mLastY = y;
         break;
    }

当用户另一只手再次按下时(触发第二个触点),触发ACTION_POINTER_DOWN事件,如果触发的点不是活动点的话,就更新mLastX 、mLastY,并设其为活动点:

    case MotionEvent.ACTION_POINTER_DOWN: {
        //获取再次按下的触点的ID
        final int pointerIndex = MotionEventCompat.getActionIndex(ev);
        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
        //如果触发的点不是活动点的话,就更新mLastX/mLastY,并设其为活动点
        if (pointerId != mActivePointerId) {
            mLastX = MotionEventCompat.getX(ev, pointerIndex);
            mLastY = MotionEventCompat.getY(ev, pointerIndex);
            mActivePointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
        }
        break;
    }

当用户两只手都在触摸事件中,这时松开其中一种手(不管是不是活动点的那一只手),触发ACTION_POINTER_UP事件,判断,如果松开的手是活动点的那一只手,就更新mLastX 、mLastY,并把另一触点设为活动点:

    case MotionEvent.ACTION_POINTER_UP: {
        //获取松开的触点的ID
        final int pointerIndex = MotionEventCompat.getActionIndex(ev);
        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
        //如果松开的是活动点,则把另一个点设为活动点,并更新mLastX/mLastY
        if (pointerId == mActivePointerId) {
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastX = MotionEventCompat.getX(ev, newPointerIndex);
            mLastY = MotionEventCompat.getY(ev, newPointerIndex);
            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
        }
        break;
    }

当用户松开另一只手时(此时只有一只手再触摸),触发ACTION_UP,我们清理活动点,设置为“无”
ACTION_CANCEL 也一同处理

    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_CANCEL:
         mActivePointerId = MotionEvent.INVALID_POINTER_ID;
         break;

以上就是多点触控的核心代码了。

总结一下思路:
在ACTION_DOWN中设置初始的活动点和位置信息,在多点按下和多点松开的事件ACTION_POINTER_DOWN、ACTION_POINTER_UP中根据用户按下和松开的情况,更新活动点和位置信息,始终保证最后按下和最后松开的点为活动点,在ACTION_MOVE中始终获取活动点的位置信息来计算拖拽距离,最后在ACTION_UP和ACTION_CANCEL中清除活动点。

剩下的就是拖拽和复位的功能:

重新onTouchEvent,在ACTION_MOVE中进行拖拽,在ACTION_UP和ACTION_CANCEL进行复位:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                doMove();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                resetPosition();
                break;
        }
        return true;
    }

拖拽和复位

最后给出拖拽和复位的方法,拖拽是通过layout方法来进行位移,复位则是利用位移动画来完成,没什么难点,不作介绍了。

    //拖拽方法
    private void doMove(){
        if (mRect.isEmpty()) {
            mRect.set(getLeft(), getTop(), getRight(), getBottom());
        }
        int movedx = (int) (dx/2);
        int movedy = (int) (dy/2);
        if (movedy!=0) {
            layout(getLeft() + movedx, getTop() + movedy, getLeft()+getWidth()+movedx, getTop() + getHeight() + movedy);
        }
    }

    //复位方法
    private void resetPosition() {
        Animation animation = new TranslateAnimation(getLeft()-mRect.left, 0, getTop()-mRect.top,0);
        animation.setDuration(200);
        animation.setFillAfter(true);
        startAnimation(animation);
        layout(mRect.left, mRect.top, mRect.right, mRect.bottom);
        mRect.setEmpty();
    }

下载

最后附上demo下载地址:

demo下载地址

版权声明:本文为博主原创文章,未经博主允许不得转载。

判断android设备是否支持多点触控

实现代码如下:  /** * 判断设备是否支持多点触控 * @param context * @return */ public static boolean isSupport...
  • shinay
  • shinay
  • 2012年05月21日 14:06
  • 4353

Android中的多点触摸

代码下载地址 代码一:自定义支持多点触摸的TextView http://download.csdn.net/detail/zhiyuan0932/9513852 什么是多点触摸允许计算机...
  • zhiyuan0932
  • zhiyuan0932
  • 2016年05月08日 22:02
  • 16981

[Android] Android开发中实现多点触摸

多点触摸(MultiTouch),指的是允许计算机用户同时通过多个手指来控制图形界面的一种技术。与多点触摸技术相对应的就是单点触摸,单点触摸的设备已经有很多年了,小尺寸的有触摸式的手机,大尺寸的最常见...
  • arui319
  • arui319
  • 2013年05月08日 13:05
  • 5980

Android多点触摸

  • 2011年05月15日 15:17
  • 784KB
  • 下载

Android中多点触控以及手势的基础知识

现在一般的android手机都会使用电容触摸屏,所以基本上都会支持多点触控,同样在android系统中应用程序可以使用多点触控的事件来完成各种手势和场景需求,下面简单讲一下如何使用多点触控: 1、相...
  • songjinshi
  • songjinshi
  • 2014年03月11日 12:15
  • 9724

Android多点触摸交互处理

一)触摸事件监听 监听手机屏幕的触摸事件,通过setOnTouchListener(),系统会回调onTouch(),在这里可以对触摸的不同状态进行监听。 触摸事件onTouch()最后返回值默认...
  • liujiawei00
  • liujiawei00
  • 2016年05月23日 21:44
  • 1705

Android多点触摸事件捕获

1 简介 Android多点触控在本质上需要LCD驱动和程序本身设计上支持,目前市面上HTC、Motorola和Samsung等知名厂商只要使用电容屏触控原理的手机均可以支持多点触控Multit...
  • ricefcc
  • ricefcc
  • 2014年12月21日 20:25
  • 1769

Linux/Android多点触摸协议

关于Linux多点触摸协议大家可以参考kernel中的文档:https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.tx...
  • mcgrady_tracy
  • mcgrady_tracy
  • 2015年07月02日 18:22
  • 2492

Android多点触控之——MotionEvent(触控事件)

今天晚上刚学习了一个多点触控的小程序,后面想对其做一个定制。在写的时候遇到很多问题,于是乎就查了一下API文档,又到网上查了一下高手的文章,最后自己又实践了一下。终于把多点触控事件监听的大概原理给弄清...
  • woshimalingyi
  • woshimalingyi
  • 2015年12月23日 01:44
  • 4493

Android 多点触控与简单手势(一)

现在一般的Android手机都会使用电容触摸屏最少可以支持两点触摸,多的可能是七八个,所以基本上都会支持多点触控, android系统中应用程序可以使用多点触控的事件来完成各种手势和场景需求。 And...
  • q610098308
  • q610098308
  • 2016年05月05日 18:19
  • 1921
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android开发游记:多点触控解析与运用
举报原因:
原因补充:

(最多只允许输入30个字)