View事件处理机制

来源:互联网 发布:特许网络的组织结构 编辑:程序博客网 时间:2024/06/05 09:56

View事件处理机制

   在View中有两个对象可以对touch事件进行处理,分别是OnTouchListener和OnClickListener(包含OnLongClickListener),当View被touch时,该View的父容器会调用View的 boolean dispatchTouchEvent(MotionEvent event)方法,即View处理touch事件的入口。根据OnTouchListener和OnClickListener是否存在可以将touch处理分为四种情况:   T空,C空:dispatchTouchEvent()返回false;   T空,C不为空:返回true;   T不为空,C空:如果T return false 则 dispatchTouchEvent() 返回false;false 则false;   T、C都不为空:T return true 则不执行C,返回true;T return false 则执行C并且返回true。

也就是说只要C不为空则返回true,T存在且return true 则不执行C并且返回true。

ViewGroup事件处理机制

 ViewGroup继承并重写了boolean dispatchTouchEvent(MotionEvent event)方法。ViewGroup处理touch事件的逻辑有点类似与链表。从父容器一直往下传递。在Event.down事件时,父容器会保存处理了touch事件的下一级容器的引用,如果没有子容器处理则,调用自身super.dispatchTouchEvent(MotionEvent event)处理。也可以重写onInterceptTouchEvent(ev) return true 拦截处理。

综合案例分析

以下摘自:http://www.longdw.com/android-onintercepttouchevent-ontouchevent/
源码:
[java] view plaincopy
public class MainActivity extends Activity {
Group1 group1;
Group2 group2;
MyTextView myTv;

/** Called when the activity is first created. */  @Override  public void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      //--group1      //----|      //-------group2      //---------|      //------------myTv      group1 = new Group1(this);      group2 = new Group2(this);      myTv = new MyTextView(this);      group2.addView(myTv, new LayoutParams(LayoutParams.FILL_PARENT,              LayoutParams.FILL_PARENT));      group1.addView(group2, new LayoutParams(LayoutParams.FILL_PARENT,              LayoutParams.FILL_PARENT));      setContentView(group1);  }  

}

[java] view plaincopy
public class Group1 extends FrameLayout {

public Group1(Context context) {      super(context);      // TODO Auto-generated constructor stub  }  @Override  public boolean onInterceptTouchEvent(MotionEvent ev) {      // TODO Auto-generated method stub      Log.d(Constant.LOGCAT, "Group1 onInterceptTouchEvent触发事件:"+Constant.getActionTAG(ev.getAction()));      return false;  }  @Override  public boolean onTouchEvent(MotionEvent event) {      // TODO Auto-generated method stub      Log.d(Constant.LOGCAT, "Group1 onTouchEvent触发事件:"+Constant.getActionTAG(event.getAction()));      return false;  }  

}

[java] view plaincopy
public class Group2 extends FrameLayout {

public Group2(Context context) {      super(context);      // TODO Auto-generated constructor stub  }  @Override  public boolean onInterceptTouchEvent(MotionEvent ev) {      // TODO Auto-generated method stub      Log.d(Constant.LOGCAT, "Group2 onInterceptTouchEvent触发事件:"+Constant.getActionTAG(ev.getAction()));      return false;  }  @Override  public boolean onTouchEvent(MotionEvent event) {      // TODO Auto-generated method stub      Log.d(Constant.LOGCAT, "Group2 onTouchEvent触发事件:"+Constant.getActionTAG(event.getAction()));      return false;  }  

}

[java] view plaincopy
public class MyTextView extends TextView {

public MyTextView(Context context) {      super(context);      this.setGravity(Gravity.CENTER);      this.setText("点击我!");      // TODO Auto-generated constructor stub  }  @Override  public boolean onTouchEvent(MotionEvent event) {      // TODO Auto-generated method stub      Log.d(Constant.LOGCAT, "MyTextView onTouchEvent触发事件:"+Constant.getActionTAG(event.getAction()));      return false;  }  

}

[java] view plaincopy
public class Constant {
public static final String LOGCAT = “logcat”;

public static String getActionTAG(int action) {      switch (action) {      case 0:          return "ACTION_DOWN";      case 1:          return "ACTION_UP";      case 2:          return "ACTION_MOVE";      default:          return "NULL";      }  }  

}

分别重写Group1和Group2的onInterceptTouchEvent和onTouchEvent方法,重写MyTextView的onTouchEvent方法,最终得到的控件层次结构如下:

1.在默认返回值情况下logcat输出如下:

测试后可知默认情况下和所有方法返回值为false的结果一致,down事件的捕获顺序onInterceptTouchEvent先于onTouchEvent,由于onTouchEvent返回值为false,down事件没被消化,后续的move和up事件没有出现,同时逆序返回到父控件的onTouchEvent方法来捕获,如下图所示:

2.所有onTouchEvent返回值为true情况下logcat输出如下:

输出结果可以看出子控件MyTextView消化了down事件,后续的move和up事件正常捕获,由于down事件被消化,上层的onTouchEvent方法不执行,如下图所示:(三箭头分别指down、move、up事件)

既然如此,如果MyTextView中onTouchEvent方法返回为false,而group1和group2的onTouchEvent方法返回true的结果自然也就如下图的顺序了:

测试输出结果证明了这一猜测顺序:

注意:可能有人对这种情况比较疑惑,ACTION_DOWN还好理解,但是ACTION_MOVE为什么没有经历myTv,而且ACTION_MOVE只经历了group1的onInterceptTouchEvent和group2的onTouchEvent而没有经历group2的onInterceptTouchEvent?我开始也费解,后来想想也是,大家对比第1条,由于onTouchEvent返回了false而没有消耗down事件导致后续的move和up都没有出现,这里也是一样由于myTv中onTouchEvent返回了false也就是说没有消耗down事件,那么后面的move和up也都不会出在这个view里面,但是group2截获到了down事件,但后来的move为什么group2中的onInterceptTouchEvent没有执行到呢,原因大家不要忘记了onInterceptTouchEvent的初衷是什么,返回false是让它的子view或viewgroup类处理,而group2的子控件显然是myTv而myTv的onTouchEvent返回了false也就是接收不到后续的move和up事件,也就没必要经过onInterceptTouchEvent来继续分发了(因为分发了也还是接收不到),经过group2的onTouchEvent因为它返回的是true,截获了事件并且消耗了事件。

3.当某个GroupView中的onInterceptTouchEvent方法返回值为true情况下logcat输出如下(如group2):

如果在该方法返回值中返回true,那么子控件将获取不到任何点击事件,转而向自身的onTouchEvent方法转发,如下图所示:

如果onTouchEvent方法返回值都为true,那么根据规律结果就如下图顺序触发:

最后logcat的结果证实了这一猜测:

还有一篇文章也比较好,可作为这个案例的补充,http://orgcent.com/android-touch-event-mechanism/

0 0
原创粉丝点击