android事件传递机制的详细了解
2015-09-19 13:13
597 查看
android项目开发的目的是为了让用户使用,那么用户使用的最直接的方式就是屏幕的点击操作,所以关于点击方面的事件我还是得好好了解的,所以,我开始去了解关于屏幕点击方面事件的传递机制
在屏幕接收触摸事件的情况中,总体来说是两种情况
1、View接收触摸事件
2、ViewGroup接收触摸事件
那么,了解到有哪些方法之后就可以对方法进行了解
第一步、我们需要知道这些方法的作用以及参数作用
那么在了解了这些方法的作用之后,我们还需要了解这些方法的执行顺序
第二步、方法的执行顺序
其实看了上面方法的注解后就应该能猜到一点调用的顺序了,但是实际我们还是需要在程序中验证的,首先来测试一下View
可以发现,在接受到点击事件之后,首先是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 return
true;
可以看到,MyView的dispatchTouchEvent
返回true之后对于MainActivity的dispatchTouchEvent 是没有影响的
这一点是比较好理解的,但是还可以发现,这样返回之后,MainActivity的onTouchEvent也接受不到点击事件了,那么在这里我们可以发现一点
dispatchTouchEvent不仅会影响自身以及下层View的onTouchEvent事件,还会影响上层的onTouchEvent事件
在这里也可以推论出一点,onTouchEvent的调用是从下层往上层的调用,dispatchTouchEvent可以打断这个过程
那么到这里就是View在Activity中的处理,并且对于dispatchTouchEvent 以及onTouchEvent的处理方式可以说是比较了解了,那么还有的就是关于ViewGroup的点击事件的处理了
在ViewGroup的点击实验中,我布局文件如下修改了下
就是在原本的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中触摸事件的传递就有了一个非常清晰的流程了解,这样在处理点击等事件的时候,相信就能很好地处理了
在屏幕接收触摸事件的情况中,总体来说是两种情况
1、View接收触摸事件
public boolean dispatchTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event)至于我们常用的onClick事件,则是在onTouchEvent之后的,这个可以后面分析
2、ViewGroup接收触摸事件
public boolean dispatchTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event)
public boolean onInterceptTouchEvent(MotionEvent event)
那么,了解到有哪些方法之后就可以对方法进行了解
第一步、我们需要知道这些方法的作用以及参数作用
/** * 方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。 * @param MotionEvent MotionEvent继承于InputEvent,用于标记各种动作事件。 * 按下(ACTION_DOWN) * 移动(ACTION_MOVE) * 抬起(ACTION_UP) * @return boolean 返回true表示不继续分发,事件没有被消费。返回false则继续往下分发, * 如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截该事件。 */ public boolean dispatchTouchEvent(MotionEvent event)
/** * 方法用于事件的处理 * @param MotionEvent MotionEvent继承于InputEvent,用于标记各种动作事件。 * 按下(ACTION_DOWN) * 移动(ACTION_MOVE) * 抬起(ACTION_UP) * @return boolean 返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。 */ public boolean onTouchEvent(MotionEvent event)
/** * 是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截 * @param MotionEvent MotionEvent继承于InputEvent,用于标记各种动作事件。 * 按下(ACTION_DOWN) * 移动(ACTION_MOVE) * 抬起(ACTION_UP) * @return boolean 返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。 * 返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能 还有子View,而在Android中View中是不能再包含子View的 */ public boolean onInterceptTouchEvent(MotionEvent ev)
那么在了解了这些方法的作用之后,我们还需要了解这些方法的执行顺序
第二步、方法的执行顺序
其实看了上面方法的注解后就应该能猜到一点调用的顺序了,但是实际我们还是需要在程序中验证的,首先来测试一下View
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i("main", "MainActivity---dispatchTouchEvent---DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("main", "MainActivity---dispatchTouchEvent---MOVE"); break; case MotionEvent.ACTION_UP: Log.i("main", "MainActivity---dispatchTouchEvent---UP"); break; default: break; } return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i("main", "MainActivity---onTouchEvent---DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("main", "MainActivity---onTouchEvent---MOVE"); break; case MotionEvent.ACTION_UP: Log.i("main", "MainActivity---onTouchEvent---UP"); break; default: break; } return super.onTouchEvent(event); } }
public class MyView extends View{ /** * 文本 */ private String mTitleText; /** * 文本的颜色 */ private int mTitleTextColor; /** * 文本的大小 */ private float mTitleTextSize; /** * 绘制时控制文本绘制的范围 */ private Rect mBound; private Rect mBound2; private Paint mPaint; private Paint mPaint2; public MyView(Context context) { this(context,null); } public MyView(Context context, AttributeSet attrs) { this(context, attrs,0); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initMyViewData(context, attrs); } private void initMyViewData(Context context,AttributeSet attrs){ //这里获取到我们在attrs.xml设置的declare-styleable TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView); //然后通过declare-styleable获取到之前定义在里面的几个属性 mTitleText = array.getString(R.styleable.MyView_titleText); mTitleTextColor = array.getColor(R.styleable.MyView_titleTextColor, Color.BLACK); mTitleTextSize = array.getDimension(R.styleable.MyView_titleTextSize, 36); array.recycle(); //一定要调用,否则这次的设定会对下次的使用造成影响 /** * 获得绘制文本的宽和高 */ mPaint = new Paint(); mPaint2 = new Paint(); mPaint.setTextSize(mTitleTextSize); mPaint2.setTextSize(mTitleTextSize); // mPaint.setColor(mTitleTextColor); mBound = new Rect(); mBound2 = new Rect(); //这句话我自己的理解是在mBound的矩形内,适配mTitleText第一个字符到最后一个字符的范围,这里是我第二和第三个参数填的0和字符串长度所以是第一个字符到最后一个字符的宽度范围 mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound); Log.i("main", "结果:" + mBound.left + ",右边:" + mBound.right + ",上面:" + mBound.top + ",下面:" + mBound.bottom); mPaint2.getTextBounds("我是第二行吗", 0, 6, mBound2); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); Log.i("main", "开始画画"); mPaint.setColor(Color.YELLOW); //在画布的x、y、right、bottom范围内画,画笔为mPaint canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); //设置画笔颜色 mPaint.setColor(mTitleTextColor); // canvas.drawText(text, x, y, paint),第一个参数是我们需要绘制的文本,第三个参数是我们的画笔, // 这两个不用多说,主要是第二和第三个参数的含义,这两个参数在不同的情况下的值还是不一样的,x默认是这个字符串的左边在屏幕的位置, // 如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心,y是指定这个字符baseline在屏幕上的位置 canvas.drawText(mTitleText, 0, mBound2.height(), mPaint); canvas.drawText("我是第二行吗", getWidth() / 2 - mBound2.width() / 2, getHeight() / 2 + mBound2.height() / 2, mPaint2); } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i("main", "MyView---dispatchTouchEvent---DOWN"); System.out.println("MyView---dispatchTouchEvent---DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("main", "MyView---dispatchTouchEvent---MOVE"); break; case MotionEvent.ACTION_UP: Log.i("main", "MyView---dispatchTouchEvent---UP"); break; default: break; } return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i("main", "MyView---onTouchEvent---DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("main", "MyView---onTouchEvent---MOVE"); break; case MotionEvent.ACTION_UP: Log.i("main", "MyView---onTouchEvent---UP"); break; default: break; } return super.onTouchEvent(event); } }我基本就加了一点点击的输出而已,得到的输出为:
可以发现,在接受到点击事件之后,首先是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 return
true;
可以看到,MyView的dispatchTouchEvent
返回true之后对于MainActivity的dispatchTouchEvent 是没有影响的
这一点是比较好理解的,但是还可以发现,这样返回之后,MainActivity的onTouchEvent也接受不到点击事件了,那么在这里我们可以发现一点
dispatchTouchEvent不仅会影响自身以及下层View的onTouchEvent事件,还会影响上层的onTouchEvent事件
在这里也可以推论出一点,onTouchEvent的调用是从下层往上层的调用,dispatchTouchEvent可以打断这个过程
那么到这里就是View在Activity中的处理,并且对于dispatchTouchEvent 以及onTouchEvent的处理方式可以说是比较了解了,那么还有的就是关于ViewGroup的点击事件的处理了
在ViewGroup的点击实验中,我布局文件如下修改了下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:myview="http://schemas.android.com/apk/res/com.example.testview" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <com.example.testview.MyViewGroup android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.testview.MyView android:layout_width="wrap_content" android:layout_height="wrap_content" myview:titleText="我是文字内容" myview:titleTextColor="#ff0000" myview:titleTextSize="40sp" /> </com.example.testview.MyViewGroup> </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中触摸事件的传递就有了一个非常清晰的流程了解,这样在处理点击等事件的时候,相信就能很好地处理了
相关文章推荐
- Android定调的发展
- android 内存优化
- Android开发之SurfaceView详解
- Android Studio如何关联SVN
- 【MDCC技术大咖秀】Android内存优化之OOM
- Android Studio——LinerLayout
- Android-MaterialRefreshLayout
- Android Studio安装之后简单设置
- android sdk更新
- android studio-友盟多渠道打包方式
- android sdk目录详解
- Android不同分辨率图片实际显示大小的计算
- 【MDCC技术大咖秀】Android内存优化之OOM
- Android 动画效果(二):四种基础动画的 **动态设置、动画监听、组合动画
- android 6 sdk/ndk下载地址
- Android ListView单选的实现总结
- android 图片缩放
- 贝塞尔曲线原理和android相关API接口
- Android学习简单总结
- android 蓝牙权限问题