android事件传递机制的详细了解

来源:互联网 发布:三阶矩阵的模怎么求 编辑:程序博客网 时间:2024/05/21 17:04
android事件传递机制的详细了解
http://blog.csdn.net/YANGDAHUAN/article/details/48467285?ref=myread
Android项目开发的目的是为了让用户使用,那么用户使用的最直接的方式就是屏幕的点击操作,所以关于点击方面的事件我还是得好好了解的,所以,我开始去了解关于屏幕点击方面事件的传递机制


在屏幕接收触摸事件的情况中,总体来说是两种情况

1、View接收触摸事件

[java] view plaincopy
  1. public boolean dispatchTouchEvent(MotionEvent event)  
  2. public boolean onTouchEvent(MotionEvent event)   
至于我们常用的onClick事件,则是在onTouchEvent之后的,这个可以后面分析

2、ViewGroup接收触摸事件

[java] view plaincopy
  1. public boolean dispatchTouchEvent(MotionEvent event)  
  2. public boolean onTouchEvent(MotionEvent event)   
  3. public boolean onInterceptTouchEvent(MotionEvent event)  




那么,了解到有哪些方法之后就可以对方法进行了解


第一步、我们需要知道这些方法的作用以及参数作用

[java] view plaincopy
  1. /** 
  2.  * 方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。 
  3.  * @param MotionEvent   MotionEvent继承于InputEvent,用于标记各种动作事件。 
  4.  *                      按下(ACTION_DOWN) 
  5.  *                      移动(ACTION_MOVE) 
  6.  *                      抬起(ACTION_UP) 
  7.  * @return  boolean 返回true表示不继续分发,事件没有被消费。返回false则继续往下分发, 
  8.  *                  如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截该事件。 
  9.  */  
  10. public boolean dispatchTouchEvent(MotionEvent event)  

[java] view plaincopy
  1. /** 
  2.      * 方法用于事件的处理 
  3.      * @param MotionEvent   MotionEvent继承于InputEvent,用于标记各种动作事件。 
  4.      *                      按下(ACTION_DOWN) 
  5.      *                      移动(ACTION_MOVE) 
  6.      *                      抬起(ACTION_UP) 
  7.      * @return  boolean 返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。 
  8.      */  
  9.     public boolean onTouchEvent(MotionEvent event)  

[java] view plaincopy
  1. /** 
  2.     * 是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截 
  3.     * @param MotionEvent    MotionEvent继承于InputEvent,用于标记各种动作事件。 
  4.     *                       按下(ACTION_DOWN) 
  5.     *                       移动(ACTION_MOVE) 
  6.     *                       抬起(ACTION_UP) 
  7.     * @return   boolean 返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。 
  8.     *                   返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能                   还有子View,而在Android中View中是不能再包含子View的 
  9.     */  
  10. public boolean onInterceptTouchEvent(MotionEvent ev)  

那么在了解了这些方法的作用之后,我们还需要了解这些方法的执行顺序


第二步、方法的执行顺序


其实看了上面方法的注解后就应该能猜到一点调用的顺序了,但是实际我们还是需要在程序中验证的,首先来测试一下View

[java] view plaincopy
  1. public class MainActivity extends Activity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.     }  
  8.   
  9.     @Override  
  10.     public boolean onCreateOptionsMenu(Menu menu) {  
  11.         getMenuInflater().inflate(R.menu.main, menu);  
  12.         return true;  
  13.     }  
  14.       
  15.     @Override  
  16.     public boolean dispatchTouchEvent(MotionEvent event) {  
  17.         switch (event.getAction()) {  
  18.         case MotionEvent.ACTION_DOWN:  
  19.             Log.i("main""MainActivity---dispatchTouchEvent---DOWN");  
  20.             break;  
  21.         case MotionEvent.ACTION_MOVE:  
  22.             Log.i("main""MainActivity---dispatchTouchEvent---MOVE");  
  23.             break;  
  24.         case MotionEvent.ACTION_UP:  
  25.             Log.i("main""MainActivity---dispatchTouchEvent---UP");  
  26.             break;  
  27.         default:  
  28.             break;  
  29.         }  
  30.         return super.dispatchTouchEvent(event);  
  31.     }  
  32.         
  33.       
  34.     @Override  
  35.     public boolean onTouchEvent(MotionEvent event) {  
  36.         switch (event.getAction()) {  
  37.         case MotionEvent.ACTION_DOWN:  
  38.             Log.i("main""MainActivity---onTouchEvent---DOWN");  
  39.             break;  
  40.         case MotionEvent.ACTION_MOVE:  
  41.             Log.i("main""MainActivity---onTouchEvent---MOVE");  
  42.             break;  
  43.         case MotionEvent.ACTION_UP:  
  44.             Log.i("main""MainActivity---onTouchEvent---UP");  
  45.             break;  
  46.         default:  
  47.             break;  
  48.         }  
  49.         return super.onTouchEvent(event);  
  50.     }  
  51.   
  52. }  

[java] view plaincopy
  1. public class MyView extends View{    
  2.     /**  
  3.      * 文本  
  4.      */    
  5.     private String mTitleText;    
  6.     /**  
  7.      * 文本的颜色  
  8.      */    
  9.     private int mTitleTextColor;    
  10.     /**  
  11.      * 文本的大小  
  12.      */    
  13.     private float mTitleTextSize;    
  14.     
  15.     /**  
  16.      * 绘制时控制文本绘制的范围  
  17.      */    
  18.     private Rect mBound;    
  19.     private Rect mBound2;  
  20.     private Paint mPaint;    
  21.     private Paint mPaint2;    
  22.       
  23.     public MyView(Context context) {  
  24.         this(context,null);  
  25.     }  
  26.         
  27.     public MyView(Context context, AttributeSet attrs) {    
  28.         this(context, attrs,0);    
  29.     }    
  30.       
  31.     public MyView(Context context, AttributeSet attrs, int defStyleAttr) {  
  32.         super(context, attrs, defStyleAttr);  
  33.         initMyViewData(context, attrs);  
  34.     }  
  35.       
  36.     private void initMyViewData(Context context,AttributeSet attrs){  
  37.         //这里获取到我们在attrs.xml设置的declare-styleable  
  38.         TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);  
  39.         //然后通过declare-styleable获取到之前定义在里面的几个属性  
  40.         mTitleText = array.getString(R.styleable.MyView_titleText);  
  41.         mTitleTextColor = array.getColor(R.styleable.MyView_titleTextColor, Color.BLACK);  
  42.         mTitleTextSize = array.getDimension(R.styleable.MyView_titleTextSize, 36);  
  43.         array.recycle(); //一定要调用,否则这次的设定会对下次的使用造成影响    
  44.         /**  
  45.          * 获得绘制文本的宽和高  
  46.          */    
  47.         mPaint = new Paint();    
  48.         mPaint2 = new Paint();  
  49.         mPaint.setTextSize(mTitleTextSize);    
  50.         mPaint2.setTextSize(mTitleTextSize);    
  51.         // mPaint.setColor(mTitleTextColor);    
  52.         mBound = new Rect();    
  53.         mBound2 = new Rect();  
  54.         //这句话我自己的理解是在mBound的矩形内,适配mTitleText第一个字符到最后一个字符的范围,这里是我第二和第三个参数填的0和字符串长度所以是第一个字符到最后一个字符的宽度范围  
  55.         mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);  
  56.         Log.i("main""结果:" + mBound.left + ",右边:" + mBound.right + ",上面:" + mBound.top + ",下面:" + mBound.bottom);  
  57.           
  58.         mPaint2.getTextBounds("我是第二行吗"06, mBound2);  
  59.     }  
  60.       
  61.     @Override  
  62.     protected void onLayout(boolean changed, int left, int top, int right,  
  63.             int bottom) {  
  64.         super.onLayout(changed, left, top, right, bottom);  
  65.     }  
  66.       
  67.     @Override  
  68.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  69.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  70.     }  
  71.       
  72.       
  73.     @Override  
  74.     protected void onDraw(Canvas canvas) {  
  75.         // TODO Auto-generated method stub  
  76.         super.onDraw(canvas);  
  77.         Log.i("main""开始画画");  
  78.         mPaint.setColor(Color.YELLOW);    
  79.         //在画布的x、y、right、bottom范围内画,画笔为mPaint  
  80.         canvas.drawRect(00, getMeasuredWidth(), getMeasuredHeight(), mPaint);    
  81.           
  82.         //设置画笔颜色  
  83.         mPaint.setColor(mTitleTextColor);    
  84. //        canvas.drawText(text, x, y, paint),第一个参数是我们需要绘制的文本,第三个参数是我们的画笔,  
  85. //        这两个不用多说,主要是第二和第三个参数的含义,这两个参数在不同的情况下的值还是不一样的,x默认是这个字符串的左边在屏幕的位置,  
  86. //        如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心,y是指定这个字符baseline在屏幕上的位置  
  87.         canvas.drawText(mTitleText, 0, mBound2.height(), mPaint);   
  88.         canvas.drawText("我是第二行吗", getWidth() / 2 - mBound2.width() / 2, getHeight() / 2 + mBound2.height() / 2, mPaint2);    
  89.           
  90.     }  
  91.       
  92.     @Override  
  93.     public boolean dispatchTouchEvent(MotionEvent event) {  
  94.         switch (event.getAction()) {  
  95.         case MotionEvent.ACTION_DOWN:  
  96.             Log.i("main""MyView---dispatchTouchEvent---DOWN");  
  97.             System.out.println("MyView---dispatchTouchEvent---DOWN");  
  98.             break;  
  99.         case MotionEvent.ACTION_MOVE:  
  100.             Log.i("main""MyView---dispatchTouchEvent---MOVE");  
  101.             break;  
  102.         case MotionEvent.ACTION_UP:  
  103.             Log.i("main""MyView---dispatchTouchEvent---UP");  
  104.             break;  
  105.         default:  
  106.             break;  
  107.         }  
  108.         return super.dispatchTouchEvent(event);  
  109.     }  
  110.       
  111.     @Override  
  112.     public boolean onTouchEvent(MotionEvent event) {  
  113.         switch (event.getAction()) {  
  114.         case MotionEvent.ACTION_DOWN:  
  115.             Log.i("main""MyView---onTouchEvent---DOWN");  
  116.             break;  
  117.         case MotionEvent.ACTION_MOVE:  
  118.             Log.i("main""MyView---onTouchEvent---MOVE");  
  119.             break;  
  120.         case MotionEvent.ACTION_UP:  
  121.             Log.i("main""MyView---onTouchEvent---UP");  
  122.             break;  
  123.         default:  
  124.             break;  
  125.         }  
  126.         return super.onTouchEvent(event);  
  127.     }  
  128. }   
我基本就加了一点点击的输出而已,得到的输出为:


可以发现,在接受到点击事件之后,首先是Activity的dispatchTouchEvent接收事件后分发给MyView,MyView接收到之后又返回给MainActivity处理,但是对于UP事件,MyView却始终没有接收到


然后,我再MyView的onTouchEvent事件中固定写了返回true之后


可见,当MyView的onTouchEvent中返回了true,表示了自己消化点击事件后,则MyView就可以接收到接下来的MOVE以及UP事件了,但是,MainActivity就接受不到了


说明:

1、在onTouchEvent事件中,上下层中,都可以接受到DOWN事件,但是如果某一层消化了点击事件,另一层就接收不到接下来的点击事件了,可以推断中间有一个对于下层是否接受事件有一个中间存储的变量

2、在dispatchTouchEvent事件中,上层总是可以接收到所有的点击事件,而下层能否接受到所有的点击事件取决于下层是否消化了onTouchEvent事件(返回true)



这样的话,在我们代码中涉及到上下层都需要处理的点击事件时就比较明了了


那么到这里是对onTouchEvent的接收影响比较了解,还有dispatchTouchEvent事件,从它的注释来理解的话,是掌控事件的对子控件的分发,还是比较好理解的,这个也可以实验

1、MainActivity   dispatchTouchEvent    retrun   true

     MyView dispatchTouchEvent     return false;


可以看到当MainActivity   的dispatchTouchEvent    返回true之后其后的所有点击事件都被截断了


2、MainActivity   dispatchTouchEvent    retrun   false

     MyView dispatchTouchEvent     returntrue;


可以看到,MyView的dispatchTouchEvent    返回true之后对于MainActivity的dispatchTouchEvent   是没有影响的

这一点是比较好理解的,但是还可以发现,这样返回之后,MainActivity的onTouchEvent也接受不到点击事件了,那么在这里我们可以发现一点


dispatchTouchEvent不仅会影响自身以及下层View的onTouchEvent事件,还会影响上层的onTouchEvent事件

在这里也可以推论出一点,onTouchEvent的调用是从下层往上层的调用,dispatchTouchEvent可以打断这个过程



那么到这里就是View在Activity中的处理,并且对于dispatchTouchEvent   以及onTouchEvent的处理方式可以说是比较了解了,那么还有的就是关于ViewGroup的点击事件的处理了



在ViewGroup的点击实验中,我布局文件如下修改了下

[java] view plaincopy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     xmlns:myview="http://schemas.android.com/apk/res/com.example.testview"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical"  
  7.     tools:context=".MainActivity" >  
  8.   
  9.     <TextView  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content"  
  12.         android:text="@string/hello_world" />  
  13.       
  14.     <com.example.testview.MyViewGroup   
  15.         android:layout_width="match_parent"  
  16.         android:layout_height="match_parent"  
  17.         >  
  18.     <com.example.testview.MyView  
  19.         android:layout_width="wrap_content"  
  20.         android:layout_height="wrap_content"  
  21.         myview:titleText="我是文字内容"  
  22.         myview:titleTextColor="#ff0000"  
  23.         myview:titleTextSize="40sp"  
  24.         />  
  25.     </com.example.testview.MyViewGroup>  
  26. </LinearLayout>  

就是在原本的MyView外面包了一层,然后在没有指定那一层确定返回true还是false的情况下点击后产生如下效果




那么,ViewGroup的调用顺序可以看到,onInterceptTouchEvent的调用是在dispatchTouchEvent之后的,并且onTouchEvent的执行顺序也是如我之前得出的结论,从最底层开始往上面调用


而我们这次的目的主要是测试onInterceptTouchEvent的用途,按照注释来看的话,主要是截断其内部子View的点击事件的接收,那么就来测试一下


我修改了onInterceptTouchEvent的返回值固定为true之后


可以看到,MyView所有点击事件都没有接收到,即使是dispatchTouchEvent也没有调用到,然而MainActivity的事件是都能接收到的


那么这一步可以看出,完全和注释一模一样,那么结合之前的测试,可以看出onInterceptTouchEvent的确有着其独特的作用

一、onInterceptTouchEvent与dispatchTouchEvent   比较

1、onInterceptTouchEvent与dispatchTouchEvent   的区别是它不会影响自身以及上层的onTouchEvent事件的执行

2、dispatchTouchEvent   比onInterceptTouchEvent多屏蔽了一层onTouchEvent的执行


二、onInterceptTouchEvent与onTouchEvent比较

1、onInterceptTouchEvent不会影响上层以及自身onTouchEvent的调用执行

2、onInterceptTouchEvent只会向下影响,而影响不到上层


这样,onInterceptTouchEvent的特性就比较明显了,到了这里,对于android中触摸事件的传递就有了一个非常清晰的流程了解,这样在处理点击等事件的时候,相信就能很好地处理了



0 0
原创粉丝点击