android 事件处理
用到的例子工程介绍:在viewGroup中加入子view,即LinerLayout中存在一子view,在MyLinerLayout中重写了 dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
在MyTestView中重写了dispatchTouchEvent、onTouchEvent。
分别研究各种返回值对事件传递的路径影响。
MyLinerLayout.java
- public class MyLinearLayout extends LinearLayout implements View.OnClickListener,View.OnLongClickListener {
- private final static String TAG = "TouchEvent";
- public MyLinearLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- // TODO Auto-generated constructor stub
- Log.v(TAG , "MyLinearLayout");
- //this.setOnClickListener(this);
- //this.setOnLongClickListener(this);
- }
- public void onClick(View v) {
- // TODO Auto-generated method stub
- Log.v(TAG , "MyLinearLayout onClick [" + "] test.................view:"+v.getId() );
- }
- @Override
- public boolean onLongClick(View v) {
- // TODO Auto-generated method stub
- boolean b = true;
- Log.v(TAG , "MyLinearLayout onLongClick [" + "] test.................view:"+v.getId() +"return :"+b);
- return true;
- }
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "MyLinearLayout --dispatchTouchEvent action:ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "MyLinearLayout --dispatchTouchEvent action:ACTION_MOVE");
- return false;
- //break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "MyLinearLayout --dispatchTouchEvent action:ACTION_UP");
- break;
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "MyLinearLayout --dispatchTouchEvent action:ACTION_CANCEL");
- break;
- }
- // Log.v(TAG , "dispatchTouchEvent "+super.dispatchTouchEvent(ev));
- boolean b = super.dispatchTouchEvent(ev);
- //boolean b = false;
- Log.v(TAG , "MyLinearLayout --dispatchTouchEvent ["+action+ "] test.................return:"+b);
- return b;
- }
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "MyLinearLayout --onInterceptTouchEvent action:ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "MyLinearLayout --onInterceptTouchEvent action:ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "MyLinearLayout --onInterceptTouchEvent action:ACTION_UP");
- break;
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "MyLinearLayout --onInterceptTouchEvent action:ACTION_CANCEL");
- break;
- }
- //boolean b = super.onInterceptTouchEvent(ev);
- boolean b = true;
- Log.v(TAG , "MyLinearLayout --onInterceptTouchEvent ["+action+ "] test.................return: "+b);
- return b;
- }
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- //boolean b = super.onTouchEvent(ev);
- boolean b = true;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "MyLinearLayout---onTouchEvent action:ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "MyLinearLayout---onTouchEvent action:ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "MyLinearLayout---onTouchEvent action:ACTION_UP");
- break;
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "MyLinearLayout---onTouchEvent action:ACTION_CANCEL");
- break;
- }
- Log.v(TAG , "MyLinearLayout --onTouchEvent ["+action+ "] test.................return: "+b);
- return b;
- }
- }
- TextView.java
- package com.lee.stony;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.widget.SimpleAdapter.ViewBinder;
- import android.widget.TextView;
- import android.view.View;
- public class MyTestView extends TextView{
- public static final String TAG = "TouchEvent";
- public MyTestView(Context context, AttributeSet attrs) {
- super(context, attrs);
- // TODO Auto-generated constructor stub
- Log.v(TAG, "MyTestView");
- /*this.setOnClickListener(this);
- this.setOnLongClickListener(this);*/
- }
- /*@Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- Log.v(TAG , "MyTestView onClick [" + "] test.................view:"+v.getId() );
- }
- @Override
- public boolean onLongClick(View v) {
- // TODO Auto-generated method stub
- boolean b = true;
- Log.v(TAG , "MyTestView onLongClick [" + "] test.................view:"+v.getId() +"return :"+b);
- return true;
- }
- */
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "MyTestView-- dispatchTouchEvent action:ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "MyTestView-- dispatchTouchEvent action:ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "MyTestView-- dispatchTouchEvent action:ACTION_UP");
- break;
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "MyTestView-- dispatchTouchEvent action:ACTION_CANCEL");
- break;
- }
- boolean b = super.dispatchTouchEvent(ev);
- //boolean b = true;
- Log.v(TAG , "MyTestView dispatchTouchEvent ["+action+ "] test.................return:"+b);
- return b;
- }
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- boolean b = super.onTouchEvent(ev);
- int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "MyTestView-- ---onTouchEvent action:ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "MyTestView-- ---onTouchEvent action:ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "MyTestView-- ---onTouchEvent action:ACTION_UP");
- break;
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "MyTestView-- ---onTouchEvent action:ACTION_CANCEL");
- break;
- }
- //boolean b = true;
- Log.v(TAG , "MyTestView onTouchEvent ["+action+ "] test.................return:"+b);
- return b;
- }
- }
1、 android中用于事件处理的方法和类主要有如下几种:
onTouchEvent
onInterceptTouchEvent//在viewgroup才有的方法,用于分发子view
dispatchTouchEvent
实现View.OnClickListener 的onClick
实现View.OnLongClickListener 的 onLongClick
实现View.OnTouchClickListener 的 onTouch
说明:在事件处理中,其实我们一般遇到事件有3中,分别是down、move、up,其中move事件在一个操作中(这里说的一个操作就是用户与屏幕的交互,即由down到up的动作序列)可能会发生多次,但是,我们认为一个动作序列会包含以上三种事件,因此,在事件处理中就是要处理好这个过程,而最重要的就是down事件,这是一个动作序列的起始,没有down谈不上后面的事件了。所以,我们把消耗down事件的类当做是这个动作序列的最终载体。
2、 各种返回值的含义和作用
(1)dispatchTouchEvent()
返回值 | 作用 |
True | 继续接受动作序列中的后续事件,如move、move、up |
False | 不接受动作序列中的后续事件,因此本次后续操作不起作用,如:down后返回false,则move和up都不会被接受,只能接受下个动作。这里为什么特别指定的down事件呢,因为如果down返回true,说明后续事件会被传递于此,但是move返回false呢?哈哈,这个就不会影响了,因此说down才是关键 |
总结:此方法一般用于初步处理事件,因为动作是由此分发,所以通常会调用super.dispatchTouchEvent。这样就会继续调用onInterceptTouchEvent,再由onInterceptTouchEvent决定事件流向。
例子:
当在DispatchTouchEvent中调用了super.dispatchTouchEvent,则会继续向下执行到onInterceptTouchEvent方法。
(2)onInterceptTouchEvent():
返回值 | 作用 |
True | 事件会传递到自己的onTouchEvent() |
False | 传递到下一个view的dispatchTouchEvent() |
例子1:
类 | 方法 | 返回值 | 说明 |
MyLinerLayout | dispatchTouchEvent | True | 调用super. dispatchTouchEvent |
onInterceptTouchEvent | true | True 则调用自己的onTouchEvent | |
onTouchEvent | true | True 则后续动作序列直接发到此处,不经过onInterceptTouchEvent | |
MyTestView | dispatchTouchEvent | True | 这里因为MyLinerLayout. onInterceptTouchEvent返回true,因此不会传递于此 |
onTouchEvent | True |
|
图:点击父和子view效果一样
说明:Down事件在MyLinerLayout.onInterceptTouchEvent()后返回true,则传递到onTouchEvent,当其返回true时,动作序列的后续事件不会再通过onInterceptTouchEvent了,而是在dispatchTouchEvent中直接传递于onTouchEvent。注意:此处点击MylinerLayout和MytestView效果一致。
例子2:
类 | 方法 | 返回值 | 说明 |
MyLinerLayout | dispatchTouchEvent | True | 调用super. dispatchTouchEvent |
onInterceptTouchEvent | False | False则点击非子view(没有子view处理)时会传递于onTouchEvent,但是如果点击MyTestView,即它的子view,则会传递到子view的onDispatchTouchEvent | |
onTouchEvent | true | True 则后续动作序列直接发到此处,不经过onInterceptTouchEvent | |
MyTestView | dispatchTouchEvent | True | 调用super. dispatchTouchEvent |
onTouchEvent | True |
|
图1:点击父view
说明:onInterceptTouchEvent返回false本应该传给下个子view的dispatchTouchEvent,但是,点击的是父view,因此不存在子view而直接传给自己的onTouchEvent,并且onTouchevent返回true,所以,后续事件不经过onInterceptTouchEvent(确定了自己要处理就不必再考虑如何分发给子view了)
图2:点击子view
说明:可以看到虽然MytestView.onTouchEvent接受并返回了true,事件还是需要经过MyLinerLayout.onInterceptTouchEvent(这是一个通道,分发给子view就要经过),
总结:onInterceptTouchEvent要做的就是确定事件传递到哪个子view,如果返回false,但是又没有子view处理(因为根本就没有点击到子view),就自己处理了,而自己在onTouchEvent处理的后续事件就不必经过onInterceptTouchEvent了,它是判断传递给子view的,都不是子view处理,就不用经过了。(以上说明,android事件处理机制中,明确谁要处理,确定后就会形成一条通道,后续事件就走这条通道吧)
注意:正常情况下dispatchTouchEvent会调用super. dispatchTouchEvent,并将其作为返回值,其实这会传递下一个接受者,让下个接受者处理并将最终的处理的返回值(在最后的onTouchEvent中,让其决定是否还需要接受后续动作序列)作为自己的返回值。而下个调用者即是onInterceptTouchEvent,不过它只是决定分发方向的,所以它的返回值对dispatchTouchEvent不影响,而onInterceptTouchEvent也可以调用super. onInterceptTouchEvent,但是这总返回false,即总希望让子view来处理。一般不用
(2)onTouchEvent():
返回值 | 作用 |
True | 事件由自己处理消耗,后续动作序列让其处理,因为在down事件时确定了路径,因此后续事件就不会迷路了 |
False | 自己不消耗事件了,向上返回让其他来父view的onTouchEvent接受处理吧 |
例子3:
类 | 方法 | 返回值 | 说明 |
MyLinerLayout | dispatchTouchEvent |
| 调用super. dispatchTouchEvent,根据不同的view的返回值会不同,不过此处总是返回false |
onInterceptTouchEvent | False | False则点击非子view(没有子view处理)时会传递于onTouchEvent,但是如果点击MyTestView,即它的子view,则会传递到子view的onDispatchTouchEvent | |
onTouchEvent | False | False 则后续动作序列一般不发到此处(要看dispatchTouchEvent的返回值,如果其使用super. dispatchTouchEvent,会使用本方法的返回值,即false)。 | |
MyTestView | dispatchTouchEvent |
| 调用super. dispatchTouchEvent,也会返回false |
onTouchEvent | False |
|
图1:点击父view
说明:当传递到自己的onTouchEvent时,返回false,而dispatchTouchEvent也使用这返回值false,因此,本次操作的后续动作序列就不会再传递了,没有view处理后续的move、up。除非再次点击才会有事件传递,不过,结果跟上图一样,处理了down返回false就没下文了。
图2:点击子view
说明:当事件流向MytestView.onTouchEvent时,返回了false,说明自己不处理了,让父view的onTouchEvent处理吧,而父view也返回false不处理,就继续向上抛,直到消失,当down事件没有被view确认处理,则后续动作序列也就不会再传递了。
例子4:跟例子3差不多,只是MyLinerLayout.onTouchEvent返回true
类 | 方法 | 返回值 | 说明 |
MyLinerLayout | dispatchTouchEvent |
| 调用super. dispatchTouchEvent,根据不同的view的返回值会不同,不过此处总是返回false |
onInterceptTouchEvent | False | False则点击非子view(没有子view处理)时会传递于onTouchEvent,但是如果点击MyTestView,即它的子view,则会传递到子view的onDispatchTouchEvent | |
onTouchEvent | True | True 则后续动作序列直接发到此处,不经过onInterceptTouchEvent | |
MyTestView | dispatchTouchEvent |
| 调用super. dispatchTouchEvent,也会返回false |
onTouchEvent | False | 事件没有被消耗,传给父view的onTouchEvent |
注意:这里点击父view会跟例子2中图1一样,因为,事件根本没有传递到子view的机会。
图1:点击子view
说明:当到达MyTestView. onTouchEvent时返回false,则传递给父view的onTouchEvent,而其返回true,说明就让其处理吧,所以后续动作序列直接发到此处。
总结:onTouchEvent被认为是动作的最终处理的函数,不管是父view还是子view处理,只要onTouchEvent消耗了事件(返回true),则认为后续动作序列的通路已经确定,否则,向上处理,直到消失。