面试之Android事件传递

 以上是测试结构;

firstl = this.findViewById(R.id.firstlinearLayout);
        secl = this.findViewById(R.id.seclinearLayout);
        testtv = this.findViewById(R.id.textView);

        firstl.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.d(TAG, "MeLinearLayout OnTouchListener eventtype = "+event.getAction());
                return false;
            }
        });
        secl.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.d(TAG, "SonLinearLayout OnTouchListener eventtype = "+event.getAction());
                //return false;
                return true;
            }
        });
        testtv.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.d(TAG, "SonTextView OnTouchListener eventtype = "+event.getAction());
                return false;
            }
        });

MeLinearLayout extends LinearLayout;

SonLinearLayout extends LinearLayout;

SonTextView extends SonTextVieww;

以上是测试基础控件,自己补全即可;

 

1.测试事件传递顺序(前提:只做视图事件的监听不拦截处理)-不拦截,不处理

事件类型:0:Down时间,1:抬起事件,2:移动事件;

12-21 12:22:39.172 32056-32056/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 0    Activity首先接受到按下事件;


12-21 12:22:39.172 32056-32056/com.mimhope.baike.touchhope D/MeLinearLayout: dispatchTouchEvent eventtype = 0 第一层MeLinearLayout接受Activity传递下来事件
12-21 12:22:39.172 32056-32056/com.mimhope.baike.touchhope D/MeLinearLayout: onInterceptEvent eventtype = 0 第一层MeLinearLayout判断是否拦截Activity传递下来事件


12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/SonLinearLayout: dispatchTouchEvent eventtype = 0 第二层SonLinearLayout接受MeLinearLayout传递下来事件
12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/SonLinearLayout: onInterceptEvent eventtype = 0 第二层SonLinearLayout判断是否拦截MeLinearLayout传递下来事件


12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/SonTextView: dispatchTouchEvent eventtype = 0  第三层SonTextView接受SonLinearLayout传递下来事件
12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/MainActivity: SonTextView OnTouchListener eventtype = 0

onTouchEvent返回false不处理Down事件;
12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/SonTextView: onTouchEvent eventtype = 0 第三层SonTextView处理SonLinearLayout传递下来事件 


12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/MainActivity: SonLinearLayout OnTouchListener eventtype = 0 第三层不处理按下事件,第二层SonLinearLayout处理;
12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/SonLinearLayout: onTouchEvent eventtype = 0


12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/MainActivity: MeLinearLayout OnTouchListener eventtype = 0 第二层不处理按下事件,第一层MeLinearLayout处理;
12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/MeLinearLayout: onTouchEvent eventtype = 0


12-21 12:22:39.173 32056-32056/com.mimhope.baike.touchhope D/MainActivity: MainActivity onTouchEvent0  第一层MeLinearLayout不处理,MainActivity处理;

 

12-21 12:26:46.775 32056-32056/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 2 当确认MainActivity视图不处理,UP,MOVE将会一直传递给MainActivity处理;
12-21 12:26:46.776 32056-32056/com.mimhope.baike.touchhope D/MainActivity: MainActivity onTouchEvent2
12-21 12:22:39.177 32056-32056/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 1
12-21 12:22:39.177 32056-32056/com.mimhope.baike.touchhope D/MainActivity: MainActivity onTouchEvent1

通过测试日志查看查看:

事件传递可以分为三个阶段 :

第一阶段Down事件递归方式传递,MainActivity->MeLinearLayout->SonLinearLayout->SonTextView(SonTextView不拦截处理触摸事件);

第二阶段Down事件冒泡方式调用:SonTextView->SonLinearLayout->MeLinearLayout->MainActivity;

第三阶段MOVE,UP等事件:MainActivity在onTouchEvent()中持续接受移动,抬起事件,确认接受事件的对象MainActivity以后,其他事件MainActivity会持续接受,不再将事件传递给其他视图;

 

2.测试事件传递顺序(前提:事件监听+事件拦截-在SonLinearLayout的onTouch事件进行处理,return true;)-只处理,不拦截

12-21 13:05:46.312 354-354/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 0  Activity首先接受到按下事件;


12-21 13:05:46.312 354-354/com.mimhope.baike.touchhope D/MeLinearLayout: dispatchTouchEvent eventtype = 0 第一层MeLinearLayout接受Activity传递下来事件
12-21 13:05:46.313 354-354/com.mimhope.baike.touchhope D/MeLinearLayout: onInterceptTouchEvent eventtype = 0 第一层MeLinearLayout判断是否拦截Activity传递下来事件


12-21 13:05:46.313 354-354/com.mimhope.baike.touchhope D/SonLinearLayout: dispatchTouchEvent eventtype = 0 第二层SonLinearLayout接受MeLinearLayout传递下来事件
12-21 13:05:46.313 354-354/com.mimhope.baike.touchhope D/SonLinearLayout: onInterceptTouchEvent eventtype = 0 第二层SonLinearLayout判断是否拦截MeLinearLayout传递下来事件


12-21 13:05:46.313 354-354/com.mimhope.baike.touchhope D/SonTextView: dispatchTouchEvent eventtype = 0第三层SonTextView接受SonLinearLayout传递下来事件
12-21 13:05:46.313 354-354/com.mimhope.baike.touchhope D/MainActivity: SonTextView OnTouchListener eventtype = 0
12-21 13:05:46.313 354-354/com.mimhope.baike.touchhope D/SonTextView: onTouchEvent eventtype = 0 第三层不处理按下事件,第二层SonLinearLayout处理;

(在SonLinearLayout的onTouch事件进行处理,return true;)
12-21 13:05:46.313 354-354/com.mimhope.baike.touchhope D/MainActivity: SonLinearLayout OnTouchListener eventtype = 0. 第二层SonLinearLayout处理,则事件不再向上冒泡


12-21 13:05:46.759 354-354/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 2  MainActivity将接续接受移动,抬起事件,递归传递事件给SonLinearLayout处理;
12-21 13:05:46.759 354-354/com.mimhope.baike.touchhope D/MeLinearLayout: dispatchTouchEvent eventtype = 2
12-21 13:05:46.759 354-354/com.mimhope.baike.touchhope D/MeLinearLayout: onInterceptTouchEvent eventtype = 2
12-21 13:05:46.759 354-354/com.mimhope.baike.touchhope D/SonLinearLayout: dispatchTouchEvent eventtype = 2
12-21 13:05:46.759 354-354/com.mimhope.baike.touchhope D/MainActivity: SonLinearLayout OnTouchListener eventtype = 2


12-21 13:05:47.407 354-354/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 1
12-21 13:05:47.407 354-354/com.mimhope.baike.touchhope D/MeLinearLayout: dispatchTouchEvent eventtype = 1
12-21 13:05:47.407 354-354/com.mimhope.baike.touchhope D/MeLinearLayout: onInterceptTouchEvent eventtype = 1
12-21 13:05:47.407 354-354/com.mimhope.baike.touchhope D/SonLinearLayout: dispatchTouchEvent eventtype = 1
12-21 13:05:47.407 354-354/com.mimhope.baike.touchhope D/MainActivity: SonLinearLayout OnTouchListener eventtype = 1

通过测试日志查看查看:

事件传递可以分为三个阶段 :

第一阶段Down事件递归方式传递,MainActivity->MeLinearLayout->SonLinearLayout->SonTextView(SonTextView不处理触摸事件);

第二阶段Down事件冒泡方式调用:SonTextView->SonLinearLayout;(SonLinearLayout onTouch() return true;)SonLinearLayout返回true说明处理onTouch事件,不再向上冒泡传递;

第三阶段MOVE,UP等事件:SonLinearLayout在onTouchEvent()中持续接受移动,抬起事件,确认接受事件的对象SonLinearLayout以后,其他事件SonLinearLayout会持续接受,不再将事件传递给其他视图;

 

3.测试事件传递顺序(前提:事件监听+事件拦截-在SonLinearLayout的onInterceptTouchEvent事件进行处理,return true;):

3.1在SonLinearLayout的onTouch事件进行处理,return false;-只拦截,不处理

12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 0
12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/MeLinearLayout: dispatchTouchEvent eventtype = 0
12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/MeLinearLayout: onInterceptTouchEvent eventtype = 0
12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/SonLinearLayout: dispatchTouchEvent eventtype = 0
12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/SonLinearLayout: onInterceptTouchEvent eventtype = 0 第二层SonLinearLayout拦截MeLinearLayout传递下来事件,onTouch()处理事件,不再向下派发;


12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/MainActivity: SonLinearLayout OnTouchListener eventtype = 0 第二层SonLinearLayout的onTouch处理,返回false,则事件再向上冒泡
12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/SonLinearLayout: onTouchEvent eventtype = 0 第二层只拦截不处理按下事件,第一层MeLinearLayout处理;


12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/MainActivity: MeLinearLayout OnTouchListener eventtype = 0 第二层不处理按下事件,第一层MeLinearLayout处理
12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/MeLinearLayout: onTouchEvent eventtype = 0


12-21 13:32:46.333 1259-1259/com.mimhope.baike.touchhope D/MainActivity: MainActivity onTouchEvent0 第一层MeLinearLayout不处理,MainActivity处理;


12-21 13:32:46.454 1259-1259/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 1  当确认MainActivity视图不处理,UP,MOVE将会一直传递给MainActivity处理;
12-21 13:32:46.454 1259-1259/com.mimhope.baike.touchhope D/MainActivity: MainActivity onTouchEvent1

通过测试日志查看查看:

事件传递可以分为三个阶段 :

第一阶段Down事件递归方式传递,MainActivity->MeLinearLayout->SonLinearLayout(SonLinearLayout拦截触摸事件);

第二阶段Down事件冒泡方式调用:SonLinearLayout->MeLinearLayout->MainActivity;

前提:事件监听+事件拦截-在SonLinearLayout的onInterceptTouchEvent事件进行处理,return true;-事件不再向下传递;

在SonLinearLayout的onTouch事件进行处理,return false;-事件继续冒泡继续向上调用;

第三阶段MOVE,UP等事件:MainActivity在onTouchEvent()中持续接受移动,抬起事件,确认接受事件的对象MainActivity以后,其他事件MainActivity会持续接受,不再将事件传递给其他视图;

3.2在SonLinearLayout的onTouch事件进行处理,return true;-拦截+处理

12-21 13:52:52.778 1621-1621/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 0
12-21 13:52:52.778 1621-1621/com.mimhope.baike.touchhope D/MeLinearLayout: dispatchTouchEvent eventtype = 0
12-21 13:52:52.778 1621-1621/com.mimhope.baike.touchhope D/MeLinearLayout: onInterceptTouchEvent eventtype = 0


12-21 13:52:52.778 1621-1621/com.mimhope.baike.touchhope D/SonLinearLayout: dispatchTouchEvent eventtype = 0
12-21 13:52:52.778 1621-1621/com.mimhope.baike.touchhope D/SonLinearLayout: onInterceptTouchEvent eventtype = 0


12-21 13:52:52.778 1621-1621/com.mimhope.baike.touchhope D/MainActivity: SonLinearLayout OnTouchListener eventtype = 0


12-21 13:52:54.492 1621-1621/com.mimhope.baike.touchhope D/MainActivity: MainActivity dispatchTouchEvent = 1
12-21 13:52:54.492 1621-1621/com.mimhope.baike.touchhope D/MeLinearLayout: dispatchTouchEvent eventtype = 1
12-21 13:52:54.492 1621-1621/com.mimhope.baike.touchhope D/MeLinearLayout: onInterceptTouchEvent eventtype = 1
12-21 13:52:54.492 1621-1621/com.mimhope.baike.touchhope D/SonLinearLayout: dispatchTouchEvent eventtype = 1
12-21 13:52:54.492 1621-1621/com.mimhope.baike.touchhope D/MainActivity: SonLinearLayout OnTouchListener eventtype = 1

通过测试日志查看查看:

事件传递可以分为三个阶段 :

第一阶段Down事件递归方式传递,MainActivity->MeLinearLayout->SonLinearLayout(SonLinearLayout拦截触摸事件,不再向下传递);

第二阶段Down事件冒泡方式调用:SonLinearLayout;在SonLinearLayout的onTouch事件进行处理,return true;

前提:事件监听+事件拦截-在SonLinearLayout的onInterceptTouchEvent事件进行处理,return true;-事件不再向下传递;

在SonLinearLayout的onTouch事件进行处理,return true;-事件不再继续冒泡继续向上调用;

第三阶段MOVE,UP等事件:SonLinearLayout在onTouchEvent()中持续接受移动,抬起事件,确认接受事件的对象SonLinearLayout以后,其他事件SonLinearLayout会持续接受,不再将事件传递给其他视图;

4.dispatchTouchEvent和onInterceptTouchEvent区别;

dispatchTouchEvent:决定了事件是否继续分发下去和是否响应事件,false:继续分发,true:不继续分发,此次事件到此结束,也不会有任何控件执行onTouchEvent方法。

onInterceptTouchEvent:决定了是否拦截该事件,false:不拦截,true:拦截,此时当前控件执行onTouchEvent方法。

onTouchEvent:决定了是否消费该事件,false:不消费,true:消费。

 

5.OnTouchListener、OnTouchEvent、OnClickListener事件分发顺序

  • 调用顺序,OnTouchListener.onTouch()——>onTouchEvent()——>OnClickListener.onClick(),其中若onTouch返回值为true,则会屏蔽掉onTouchEvent方法和OnClickListener.onClick方法,若onTouchEvent方法可以被调用,则OnClickListener.onClick方法也一定可以被调用。
  • 一系列点击事件,肯定是以down事件开始,以up事件结束,中间若干个move事件。如果一旦某个元素拦截了某个事件,那么同一个事件序列内的所有事件都会直接交给它处理。
  • 首先,考虑OnTouchListener.onTouch返回的是false,此种情况下,首先down事件按照顺序先经过OnTouchListener.onTouch方法,由于返回false不屏蔽onTouchEvent方法,所以又被分发到onTouchEvent方法,由于该方法默认返回true,即消费了down事件,所以此后的所有事件都由该方法处理,并且不再经过OnTouchListener.onTouch。

6.滑动冲突:分为三种

  • 外部滑动方向和内部滑动方向不一致

典型一个左右滑动的Viewpager+上下滑动的Fragment(Viewpager已经处理了滑动冲突)

  • 外部滑动方向和内部滑动方向一致
  • 上述两种情况的嵌套

对于情况一,判断用户是上下滑动还是左右滑动,可以根据角度,距离差,速度差来进行判断

对于情况二,要根据业务情况来处理规则

外部拦截法:父容器覆写onIntercepted方法,根据情况是否拦截消耗事件(通常选用该方法)

内部拦截法:父容器不做处理,由子控件是否拦截和消耗(要配合requestDisallowInterceptTouchEvent()来使用,父容器不能拦截ACTION_DOWN,因为该事件不受requestDisallowInterceptTouchEvent()控制)

7.super.xxx作用

super.dispatchTouchEvent表示继续分发该事件。

如果return super.onInterceptTouchEvent(ev), 事件拦截分两种情况:

1.如果该View(ViewGroup)存在子View且点击到了该子View, 则不拦截, 继续分发
给子View 处理, 此时相当于return false。
2.如果该View(ViewGroup)没有子View或者有子View但是没有点击中子View(此时ViewGroup
相当于普通View), 则交由该View的onTouchEvent响应,此时相当于return true。 
注意:一般的LinearLayout、 RelativeLayout、FrameLayout等ViewGroup默认不拦截, 而
ScrollView、ListView等ViewGroup则可能拦截,得看具体情况。

如果return super.onTouchEvent(ev),事件处理分为两种情况:

1.如果该View是clickable或者longclickable的,则会返回true, 表示消费
了该事件, 与返回true一样;
2.如果该View不是clickable或者longclickable的,则会返回false, 表示不
消费该事件,将会向上传递,与返回false一样.

8.ACTION_CANCEL

ACTION_CANCEL 的触发条件是事件被上层拦截,当事件被上层 View 拦截的时候,ChildView 是收不到任何事件的,ChildView 收不到任何事件,自然也不会收到 ACTION_CANCEL 了,所以说这个 ACTION_CANCEL 的正确触发条件并不是这样,那么是什么呢?

事实上,只有上层 View 回收事件处理权的时候,ChildView 才会收到一个 ACTION_CANCEL 事件。

这样说可能不太容易理解,咱举个例子?

例如:上层 View 是一个 RecyclerView,它收到了一个 ACTION_DOWN 事件,由于这个可能是点击事件,所以它先传递给对应 ItemView,询问 ItemView 是否需要这个事件,然而接下来又传递过来了一个 ACTION_MOVE 事件,且移动的方向和 RecyclerView 的可滑动方向一致,所以 RecyclerView 判断这个事件是滚动事件,于是要收回事件处理权,这时候对应的 ItemView 会收到一个 ACTION_CANCEL ,并且不会再收到后续事件。

 

 

参考:

https://segmentfault.com/a/1190000021534817

https://blog.csdn.net/yh18668197127/article/details/84568175

https://www.gcssloop.com/customview/motionevent

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值