android事件处理机制

来源:互联网 发布:g92车直螺纹编程实例 编辑:程序博客网 时间:2024/05/17 03:07

  谈到android事件处理,最复杂的就是对Touch事件的处理,因为Touch事件包括:down, move, up, cancle和多点触摸等多种情况,多点触摸的情况先不讨论,因为Touch有这么多的状态,所以Touch相对来说是最难处理的,下面就来讨论一下android系统是如何处理Touch事件的.

  1.说到事件处理,首先我们要明白,为什么要处理事件,要了解android系统本身对事件的一个处理过程.在实际的开发中,我们如果都用系统的基本控件,那是不需要去处理事件的,但是如果我们用复杂的布局嵌套去做一些特殊的需求,例如:ScrollView中嵌套ListView,ScrollView嵌套ViewPager等,则会产生事件冲突,所以,由于事件冲突的存在,我们要去处理这些冲突,只有了解android的事件处理机制,才能有效的去处理事件冲突.还有就是如果我们要新开发一个组件,则组件的所有事件都要我们自己去做处理,这种情况也需要我们去处理事件.所以:由于存在以上说到的两种情况,我们要自己处理事件.

  2.有了处理事件的动机后,接下来就要了解android系统本身是如何处理复杂的事件的.android系统为所有的事件提供了三个相关的方法,以下只以Touch事件为例说明.

这三个方法分别是:

   dispatchTouchEvent(MotionEvent ev);  (Activity, ViewGroup, View都有此方法)

            onInterceptTouchEvent(MotionEvent ev); (只有ViewGroup有)

            onTouchEvent(MotionEvent);      (Activity, ViewGroup, View都有此方法)

要想了解android系统是如何对事件进行一步一步的处理,这三个方法是必须要掌握的.其中:dispatchTouchEvent(MotionEvent ev);方法是用来对事件进行分发的,即将事件分发到目标控件,onInterceptTouchEvent(MotionEvent ev)是用来过滤事件的,即进行事件的拦截,也就是是否要向下传递事件,onTouchEvent(MotionEvent ev)才是最终用来处理事件的,也就是说我们平常重写onTouchEvent时,其实,系统已经默认帮我们调用了前两个方法.下面就来详细分析一下三个方法.

  首先要提的是,android系统对本件的处理是一层一层向下传递处理(树形处理).那这棵树是从那来的呢..就是我们的布局树,一个布局,无论是代码编写的布局还是xml生成的布局,android系统对它进行解析时都是将其组装成一棵UI树,最外层布局是整个UI树的根.知道这个以后,再来分析事件的处理.

  处理流程:当我们的手指触摸到手机屏幕时,当前处于onStart()状态的Activity最先接收到此Touch事件下的ACTON_DOWN,然后开始调用它自己dispatchTouchEvent()开始进行DOWN事件分发,如果此方法返回true,则Activity不向下分发事件,则整个布局都不会收到DOWN事件,TouchEvent直接到Activity的onTouchEvent()方法进行事件处理.如果返回false,则表示DOWN是要被分发到下层的,此时DOWN事件被直接分发(因为没有过滤方法)到UI树的根布局(即最外层的布局),根布局拿到DOWN事件时,执行自己的dispatchTouchEvent方法,返回true,则事件直接交到根布局的onTouchEvent()中进行处理,false则表示还得向下分发,此时事件被传递到根布局的onInterceptTouchEvent()方法中,如果此方法返回true,表示要对此事件进行过滤,则此DOWN事件又直接进行到根布局的onTouchEvent()方法直接处理,false则,要根布局不对事件进行过滤,DOWN事件继续向下传递,直到达到目标组件后,目标组件调用自己的dispatchTouchEvent()方法,由于是目标组件,直接分发事件到自己的onTouchEvent方法中,目标组件如果处理完这个DOWN事件后返回true,表示该事件被消费完毕,不再向上层传递,如果返回false,则表示没有消费完这个DOWN事件,DOWN向上传递到自己的父组件中,父组件再进行DOWN事件的处理.一直向上传递直到事件被扔到虚拟机.DOWN事件才算处理完成,接着调用MOVE,MOVE完了UP,整个流程与DOWN是一樣的.

        这里要强调一点的是:如果一个组件没有接收到DOWN事件,那么一定接收不到MOVE,UP事件。      

 

       通过以上的流程,我们可以明白:android系统对任何一个事件的处理都是这样的,分发事件,过滤事件,处理事件,下一个事件, 分发事件,过滤事件,处理事件……一直这样循环去处理所有的事件的。即:事件的分发,过滤是从根到叶的,处理则是从叶再到根的。

      下面是我将上面的文字流程画的一张处理流程图:

     

      从图上看,我们可以更直观的感受整个Touch事件的处理流。


      3.讲了android系统是如何处理事件的整个流程,那我们实际工作中如何去处理事件呢……下面将我之前处理过的一个例子来分析。

      首先我们从根到叶去处理事件,代码如下:

     

package com.micen.buyers.view.category;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.ScrollView;import com.micen.buyers.util.Util;public class MyScrollView extends ScrollView {private float mLastMotionY;private float mLastMotionX;public MyScrollView(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}/** * 通过重写此方法,达到对事件的处理 */@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {// TODO Auto-generated method stubfinal float x = ev.getX();final float y = ev.getY();switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:mLastMotionX = x;mLastMotionY = y;break;case MotionEvent.ACTION_MOVE:if (Math.abs(y - mLastMotionY) > Util.dip2px(20)&& Math.abs(x - mLastMotionX) < Util.dip2px(5)) {return true; // 如果MOVE事件的纵坐标超过20px, 横向小于5dp// 则认为是滑动scrollview,返回true,则事件不向下分发,直接传入到// onTouchEvent方法中,否则,认为滑动事件不属于scrollview处理,允许分发}break;case MotionEvent.ACTION_UP:break;case MotionEvent.ACTION_CANCEL:break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {// TODO Auto-generated method stubreturn super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {Log.e("--------->", "user want to scroll");return super.onTouchEvent(ev);}}
上述代码,我们是重写了ScrollView的dispatchTouchEvent()达到对事件的一个特殊处理,如果满足了我们的规则,则直接到onTouchEvent()中处理,否则,事件被发送到 onInterceptTouchEvent()中执行过滤,由于此方法中没有过滤,则下发传递事件.

    同样的效果,我们从叶子开始处理事件冲突,代码如下:

   

package com.micen.buyers.view.category;import android.content.Context;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.MotionEvent;import com.micen.buyers.util.Util;public class MyViewPager extends ViewPager{private boolean flag = true;private float mLastMotionY;private float mLastMotionX;public MyViewPager(Context context){super(context);}public MyViewPager(Context context, AttributeSet attrs){super(context, attrs);        }@Overridepublic boolean dispatchTouchEvent(MotionEvent ev){final float x = ev.getX();final float y = ev.getY();switch (ev.getAction()){case MotionEvent.ACTION_DOWN:setPullToScrollViewStatus(true); //先默认父控件不接收滑动事件,事件直接传递到子滑动组件flag = true;mLastMotionX = x;mLastMotionY = y;mHandler.sendEmptyMessage(1);break;case MotionEvent.ACTION_MOVE:if (flag){if (Math.abs(y - mLastMotionY) > Util.dip2px(20) && Math.abs(x - mLastMotionX) < Util.dip2px(5)){flag = false;setPullToScrollViewStatus(false); //满足条件,父滑动控件将事件过滤掉了,不再传到ViewPager中了.}}break;case MotionEvent.ACTION_UP:setPullToScrollViewStatus(false);case MotionEvent.ACTION_CANCEL:setPullToScrollViewStatus(false);break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event){return super.onInterceptTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event){return super.onTouchEvent(event);}private void setPullToScrollViewStatus(boolean disallowIntercept){//调用父控件的requestDisallowInterceptTouchEvent()方法,传入true,则等将于父控件的onTInterceptTouchEvent返回false,//不过滤,否则,父控件过滤掉事件,不再向下传递.getParent().getParent().requestDisallowInterceptTouchEvent(disallowIntercept);}}

总结:android事件处理流程,是每一个搞android的人应该熟练掌握的.

     

3 0
原创粉丝点击