android事件传递机制的详细了解
来源:互联网 发布:三阶矩阵的模怎么求 编辑:程序博客网 时间:2024/05/21 17:04
在屏幕接收触摸事件的情况中,总体来说是两种情况
1、View接收触摸事件
- public boolean dispatchTouchEvent(MotionEvent event)
- public boolean onTouchEvent(MotionEvent event)
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 returntrue;
可以看到,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 的事件传递机制,详细解释
- 一篇详细的 Android onTouch事件传递机制
- Android事件传递机制(更加深入的了解事件的触发过程)
- Android事件传递机制(更加深入的了解事件的触发过程)
- Android事件传递机制(更加深入的了解事件的触发过程)
- Android的事件传递机制
- Android事件的传递机制
- Android 事件的传递机制
- Android 事件的传递机制
- Android事件的传递机制
- Android的事件传递机制
- android的事件传递机制
- Android的事件传递机制
- Android的事件传递机制
- Android的事件传递机制
- 详细剖析 android onInterceptTouchEvent(MotionEvent event) 和 onTouchEvent(MotionEvent event) 的事件传递机制
- Count Primes 筛选法求素数
- 获取泛型类的真实参数
- java.lang.IllegalArgumentException: Mapped Statements collection already contains value for
- LibRTMP源代码分析5:建立网络连接
- 统一设计,iOS6也玩扁平化
- android事件传递机制的详细了解
- ListView
- 使用xib的适配方法
- 深入理解计算机系统重点笔记
- EXP-00056: 遇到 ORACLE 错误 1455
- 自定义iOS的Back按钮(backBarButtonItem)和pop交互手势(interactivepopgesturerecognizer)
- Linux如何查看进程、杀死进程、启动进程等常用命令
- c# 在 ComboBox 控件中创建大小可变的文本
- hihoCoder 1233 Boxes(状态压缩 + bfs)——ACM-ICPC国际大学生程序设计竞赛北京赛区(2015)网络赛