Android 进阶之事件交互 一 触摸事件和键盘事件
MotionEvent
我们在Android进阶之绘图中介绍了如何在界面上进行绘制的部分,但是这仅仅是静态的画面,它属于输出部分,但是只有输出往往是不够的,还需要通过某种方式接收用户的输入,这才能构成交互。
在我们解除屏幕到离开屏幕会产生一系列触摸事件,要响应这些事件必须复写onTouchEvent(MotionEvent e):
这里传入的参数MotionEvent是一个很重要的参数,它包含了当前触摸点的坐标,以及触摸事件的类型:
当用户触摸屏的时候,就会触发onTouchEvent事件处理函数,每当位置发生改变的时候触发一次,当触摸结束的时候还会触发一次。
它有一个返回值如果当前的onTouchEvent已经处理了屏幕触摸,就返回true,否则,就返回false。从而通过View或者Activity堆栈来传递事件,直到成功处理触摸为止。这部分涉及到事件的传递和分发机制
在后面会详细介绍,我们先来认识下MotionEvent:
通过event.getAction() 可以获取事件的类型:
MotionEvent.ACTION_UP 触摸结束,ACTION_UP
MotionEvent.ACTION_DOWN 开始触摸,第一个手指头按下的时候触发
MotionEvent.ACTION_POINT_DOWN 其他手指再按下的时候触发
MotionEvent.ACTION_POINT_UP 其他手指抬起来的时候触发
MotionEvent.ACTION_MOVE 按下手指在屏幕上移动的时候
MotionEvent.ACTION_CANCEL 触摸事件取消的时候
MotionEvent.ACTION_OUTSIDE 移动操作发生在被监控的屏幕元素边界之外的时候
通过event.getX() event.getY() event.getRawX() event.getRawY() 可以分别获得当前触点距离父容器以及屏幕的坐标位置;这个可以在绘图介绍中的坐标体系中看到每个方法的返回值意义
还可以通过下面的方法获取到事件开始和结束的时间信息:
getDownTime() 事件开始时间
getEventTime() 事件结束时间
多点触控事件:
现在的电容屏一般都支持多点触控技术,判断当前触摸事件为单点触控还是多点触控可以使用getPointerCount()方法,如果大于1表示为多点触控。
多点触控每个点称为一个pointer,MotionEvent包含该时刻所有point的信息。每个point都有唯一的ID,这个ID是在该point刚触碰屏幕的时候赋值的。有效期至该点离开屏幕或者取消。
我们要获得每个点的信息,必须通过它的index,而要获得index必须使用ACTION_POINTER_ID_MASK从Action中获得Action Point id
多点触控的处理:
int xpos = -1; |
跟踪触摸的移动:
每当当前的触摸接触位置,压力或者区域大小发生改变的时候,将会触发ACTION_MOVE。MotionEvent中包含有着这系列变化的历史值。可以使用如下方式获取历史值:
单点触控: |
处理触摸屏移动事件:
处理移动事件一般首先处理每个历史事件,然后再处理当前的MotionEvent值
switch (action) { |
为控件添加触摸事件监听器:
可以使用setOnTouchLinstener方法为某个View对象监听触摸事件。
键盘事件:
除了上述的显示屏事件外Android中还提供了键盘事件的处理:
所有的硬件按键产生的事件都是由处于活动状态的Activity或者当前前台View进行处理。Android中控件在处理物理按键事件时,提供的回调方法有onKeyUp() ,onKeyDown(),onKeyLongPress()
要想让Activity或者View对按键的按下做出响应,需要重写onKeyDown和onKeyUp事件处理方法:
方法中的keyCode包含了被按下的键的值,把它和KeyEvent类中的静态的Keycode进行比较,就可以执行特定按键的处理。
要在Activity的View中对按键按下做出响应,需要实现OnKeyListener并使用setOnKeyListener方法将其分配给一个View。
OnKeyListener并不是为了单独的按键按下和释放分别实现独立的方法,而是使用单一的onKey事件。ACTION_DOWN表示按下按键,ACTION_UP表示释放按键
事件分发和事件拦截机制
上面所讲述的情况是相对理想的情况,也就是事件已经传送到当前的View的情形,但是一般一个View 往往都是处在某个ViewGroup中,这就引入了一个新的问题,事件是如何传递分发和拦截的。
我们先看下下面这张图:
绿色的代表一个View,它被layout在两个ViewGoup内,当一个事件发生的时候,首先从底层的父ViewGroup开始传输的,在介绍整个传输过程之前先介绍下面两个方法:
onInterceptTouchEvent()是用于事件的预处理并改变事件的传递方向,也就是决定是否允许Touch事件继续子控件传递,如果返回True表示事件在当前的viewGroup中会被处理,
则向下传递被截断,所有子控件将没有机会参与Touch事件,同时把事件传递给当前的控件的onTouchEvent()处理;返回false,则把事件交给子控件的onInterceptTouchEvent()
onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截.不能包含子view的控件是没有这个方法的.onTouchEvent()这个上面介绍了用于处理事件,返回值决定当前控件是否消费了这个事件,也就是说在当前控件在处理完Touch事件后,是否还允许Touch事件继续向父控件传递,一但返回True,
则父控件不用操心自己来处理Touch事件。返回false,则向上传递给父控件.
如果layoutview1,layoutview2 onInterceptTouchEvent都返回false,也就是不拦截,那么整个事件的传递如下图所示:
如果layoutview1 onInterceptTouchEvent 返回false ,layoutview2 onInterceptTouchEvent都返回true,那么整个事件的传递如下图所示:
如果layoutview1,layoutview2 onInterceptTouchEvent都返回false,也就是不拦截,childview onTouchEvent 返回true 那么整个事件的传递如下图所示: