onTouchEvent / MotionEvent(轨迹球事件、触摸屏事件、键盘事件)

-- 轨迹球事件  Android
  触摸屏驱动,了解一下Android对触摸屏、键盘、轨迹球事件的响应过程。
  事件的传入是从EventHub开始的,EventHub是事件的抽象结构,维护着系统设备的运行情况,设备类型包括Keyboard、Touchscreen、TraceBall等。它在系统启动的时候会通过open_device()方法将系统提供的输入设备都增加到这个抽象结构中,并维护一个所有设备的文件描述符,如果输入设备是键盘的话还会读取XXXXX-Keypad.kl对应的键盘设备的映射文件,另外getEvent()方法是对EventHub中的设备文件描述符使用poll操作等待驱动事件的发生,如果发生的事件是键盘事件,则调用KeyLayoutMap的map()方法按照映射文件转换成相应的键值并将扫描码和键码返回给KeyInputQueue。
  根据读取的事件类型的不同分成三类(KEYBOARD、TOUCHSCREEN、TRACKBALL),分别进行处理,例如键盘事件会调用dispatchKey((KeyEvent)ev.event, 0, 0)以将事件通过Binder发送给具有焦点的窗口应用程序,然后调用mQueue.recycleEvent(ev)继续等待键盘事件的发生;如果是触摸屏事件则调用dispatchPointer(ev, (MotionEvent)ev.event, 0, 0),这里会根据事件的种类(UP、DOWN、MOVE、OUT_SIDE等)进行判断处理,如取消或将事件发送到具有权限的指定的窗口中去。 EventHub对输入设备进行了封装。

Android SurviceView的触控和轨迹球事件- https://rw1314.iteye.com/blog/1254891
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            x=event.getX();//获取触控的X坐标
            y=event.getY();//获取触控的Y坐标
            Draw();
            return true;
        }
        
        @Override
        public boolean onTrackballEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            x=event.getX();//获取轨迹的X坐标
            y=event.getY();//获取轨迹的Y坐标
            Draw();
            return true;
        }

-- Android在MotionEvent里定义了一系列的手势事件,其中包括:
 MotionEvent.ACTION_DOWN:当屏幕检测到第一个触点按下之后就会触发到这个事件。
 MotionEvent.ACTION_MOVE:当触点在屏幕上移动时触发,触点在屏幕上停留也是会触发的,主要是由于它的灵敏度很高,而我们的手指又不可能完全静止(即使我们感觉不到移动,但其实我们的手指也在不停地抖动)。
 MotionEvent.ACTION_POINTER_DOWN:当屏幕上已经有触点处于按下的状态的时候,再有新的触点被按下时触发。
 MotionEvent.ACTION_POINTER_UP:当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)触发。
 MotionEvent.ACTION_UP:当最后一个触点松开时被触发。
 MotionEvent.ACTION_SCROLL:非触摸滚动,主要是由鼠标、滚轮、轨迹球触发。
 MotionEvent.ACTION_CANCEL:不是由用户直接触发,有系统再需要的时候触发,例如当父view通过使函数onInterceptTouchEvent()返回true,从子view拿回处理事件的控制权是,就会给子view发一个ACTION_CANCEL事件,这里了view就再也不会收到事件了。可以将其视为ACTION_UP事件对待。

-- onInterceptTouchEvent()函数与onTouchEvent()的区别:
  1、onInterceptTouchEvent()是用于处理事件(类似于预处理,当然也可以不处理)并改变事件的传递方向,也就是决定是否允许Touch事件继续向下(子view)传递,一但返回True(代表事件在当前的viewGroup中会被处理),则向下传递之路被截断(所有子view将没有机会参与Touch事件),同时把事件传递给当前的view的onTouchEvent()处理;返回false,则把事件交给子view的onInterceptTouchEvent()
  2、onTouchEvent()用于处理事件,返回值决定当前view是否消费(consume)了这个事件,也就是说在当前view在处理完Touch事件后,是否还允许Touch事件继续向上(父view)传递,一但返回True,则父view不用操心自己来处理Touch事件。返回true,则向上传递给父view(注:可能你会觉得是否消费了有关系吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。)

-- 事件分发流程
(1)整个View的事件分发流程:View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent
在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。
(2)onTouchEvent中的DOWN,MOVE,UP
 1)DOWN时:
    a、首先设置标志为PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个115ms后的mPendingCheckForTap;
    b、如果115ms内没有触发UP,则将标志置为PRESSED,清除PREPRESSED标志,同时发出一个延时为500-115ms的,检测长按任务消息;
    c、如果500ms内(从DOWN触发开始算),则会触发LongClickListener:
    此时如果LongClickListener不为null,则会执行回调,同时如果LongClickListener.onClick返回true,才把mHasPerformedLongPress设置为true;否则mHasPerformedLongPress依然为false;
 2)MOVE时:
 主要就是检测用户是否划出控件,如果划出了:
  115ms内,直接移除mPendingCheckForTap;
  115ms后,则将标志中的PRESSED去除,同时移除长按的检查:removeLongPressCallback();
 3)UP时:
 a、如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;
 b、如果是115ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;
 c、如果是500ms以后,那么有两种情况:
    i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;
    ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,则点击事件OnClick事件依然可以触发;
 d、最后执行setPressed刷新背景,然后将PRESSED标识去除;

猜你喜欢

转载自blog.csdn.net/ShareUs/article/details/90735002
今日推荐