onInterceptTouchEvent/onTouch/onLongClick/onClick等,VelocityTracker滑动速率,事件分发机制,View中的performClick((4)

-- onClick, onTouch哪个先执行?  Android
  首先会执行dispatchTouchEvent(MotionEvent event) ,所以onTouch方法肯定是早于onClick方法的,如果在onTouch里返回false,就会出现下面的现象:
10-20 18:57:49.670: DEBUG/MainActivity(20153): onTouch execute, action event 0
10-20 18:57:49.715: DEBUG/MainActivity(20153): onTouch execute, action event 1
10-20 18:57:49.715: DEBUG/MainActivity(20153): onClick execute
即先执行了onTouch,再执行了onClick事件,而且onTouch执行了两次,一个是action_down,一个是action_up事件;

  如果onTouch里返回true,则出现下面的现象:
10-20 19:01:59.795: DEBUG/MainActivity(21010): onTouch execute, action event 0
10-20 19:01:59.860: DEBUG/MainActivity(21010): onTouch execute, action event 1
结果是onClick事件没有执行了,原因是如果onTouch返回true的话,则dispatchEvent(MotionEvent event)方法直接返回true了,相当于不往下传递事件了,所以onClick不会执行,相反如果onTouch返回false的话(此时会执行onClick方法),则会执行 onTouchEvent(MotionEvent event)方法,由此可以得出这样一个结论,onClick事件的具体调用执行肯定是在onTouchEvent(MotionEvent event)方法源码中

 关于OnTouchEvent(MotionEvent事件)事件的层级传递。我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。

> requestDisallowInterceptTouchEvent(),onInterceptTouchEvent()

> 滑动冲突拦截
1.滑动冲突内部拦截:requestDisallowInterceptTouchEvent(),
2.滑动冲突外部拦截:onInterceptTouchEvent();dispatchTouchEvent()

-- 滑动冲突解决套路:
1.外部拦截法,重写父View的onInterceptTouchEvent,父View决定是否拦截
2.内部拦截法,子View使用requestDisallowInterceptTouchEvent方法,父View需要重写onInterceptTouchEvent

Android - requestDisallowInterceptTouchEvent() 阻止父层的View截获touch事件(事件处理机制)- http://blog.csdn.net/CL18652469346/article/details/53184508
android事件之onInterceptTouchEvent,dispatchTouchEvent,onTouchEvent,requestDisallow- http://blog.csdn.net/kongbaidepao/article/details/47342937

-- Android:View中的performClick()触发条件, 先看看performClick()源码:
public boolean performClick() {
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        if (mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            mOnClickListener.onClick(this);
            return true;
        }
 
        return false;
    }
 由源码可以看出,只要是使用了view.setOnClickListener()方法设置监听器,就会自动触发view.performClick()。
需要注意的是,如果同时使用了view.setOnTouchListener()方法,则有可能存在拦截view.performClick()的响应事件,因为当view.OnTouchEvent()在event.getAction() == MotionEvent.ACTION_DOWN时返回false,系统会认为view不需要处理Touch事件,则后续的Touch事件(move、up、click)就不会被传进来,所以也不会触发view.performClick(),而view.setOnTouchListener()相当于是重写了view.OnTouchEvent(),所以在写view的TouchListener处理时,需要留意view是否存在点击事件监听,如果有,则在适当的位置使用view.performClick()触发点击事件。

-- onInterceptTouchEvent和onTouchEvent的调用顺序:
  假设最高层View叫OuterLayout,中间层View叫InnerLayout,最底层View叫MyVIew。调用顺序是这样的(假设各个函数返回的都是false):OuterLayout.onInterceptTouchEvent->InnerLayout.onInterceptTouchEvent->MyView.onTouchEvent->InnerLayout.onTouchEvent->OuterLayout.onTouchEvent。

  对于底层的View来说,有一种方法可以阻止父层的View截获touch事件,就是调用getParent().requestDisallowInterceptTouchEvent(true) 方法。一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action。
  requestDisallowInterceptTouchEvent(boolean disallowIntercept),当子View 不想被 父View 拦截的时候,就可以调用requestDisallowInterceptTouchEvent(MotionEvent)方法,这样,可以放父View的 onInterceptTouchEvent(MotionEvent)失效。

只有继承viewgroup的控件才有onInterceptTouchEvent()方法。

-- requestDisallowInterceptTouchEvent()与dispatchTouch() 
View事件的传递顺序是Activity→Window→View,在Activity中的dispatchTouchEvent方法:
public boolean dispatchTouchEvent(MotionEvent ev) {  
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
        onUserInteraction();  
    }  
    if (getWindow().superDispatchTouchEvent(ev)) {  
        return true;  
    }  
    return onTouchEvent(ev);  
}  

简单解决ScrollView 与 WebView 的冲突,dispatchTouchEvent- https://github.com/2954722256/demo_event

-- Android的事件分发机制已经分析的差不多了,我们来总结一下:
   1,事件的传递顺序是从Activity→Window→View
  2,默认情况下View中的dispatchTouchEvent方法会调用onTouchEvent方法(如果OnTouchListener拦截就不会再调用onTouchEvent方法)
   3,View中事件的顺序为dispatchTouchEvent→OnTouchListener→onTouchEvent→OnLongClickListener(在DOWN中触发)→OnClickListener(在UP中触发)
   4,在ViewGroup中事件的触发顺序为dispatchTouchEvent→onInterceptTouchEvent→onTouchEvent
  5,默认情况下Button,ImageButton等Button的子控件都是会消耗事件的,即onTouchEvent默认返回true,而ImageView,TextView,LinearLayout等一些控件默认是不消耗事件的,即onTouchEvent默认返回为false
  6,事件是从上往下传递的,如果其中的一个onInterceptTouchEvent返回了true,则表示事件拦截,此后的MOVE,UP都不会再调用onInterceptTouchEvent方法,然后调用自己的onTouchEvent方法,则它下面的控件都不会再获取触发事件
  7,如果在子控件中不让父控件拦截,可以调用父控件的requestDisallowInterceptTouchEvent(boolean disallowIntercept)方法,
  8,如果子控件不处理,则会往上抛,交给父控件处理,如果都不处理,默认会抛到Activity的onTouchEvent方法,Activity的onTouchEvent方法默认是消耗控件的,且默认返回为false,DOWM,Move,UP都会执行
  9,如果有一个控件处理了事件,则后续的一系列事件(MOVE,UP)也都会执行。

-- Button的OnTouch事件传递机制(和onClick,onLongClick事件发生先后顺序)
Android-事件分发(OnTouchEvent,OnTouch,OnClick)-https://www.cnblogs.com/qlky/p/6675882.html
事件分发机制之-Button的onTouch()事件分析- https://www.jianshu.com/p/40e7a8e9090e
 https://github.com/ruanjiankeji/ButtonEventAnalysis

 Touch事件分发中只有两个主角:ViewGroup和View。Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。
 View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。
 ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。

  一、当一个点击事件发生时,调用顺序如下:
1.事件最先传到Activity的dispatchTouchEvent()进行事件分发;

2.调用Window类实现类PhoneWindow的superDispatchTouchEvent();

3.调用DecorView的superDispatchTouchEvent();

4.最终调用DecorView父类的dispatchTouchEvent(),即ViewGroup的dispatchTouchEvent();

5.ViewGroup的dispatchTouchEvent()根据情况调用View的dispatchTouchEvent;

6.如果DecorView的dispatchTouchEvent()返回true就不执行Activity的onTouchEvent()方法;如果返回false,就执行Activity的onTouchEvent()方法。
所以,先从Acitivity的dispatchTouchEvent()方法看起,就能把整个流程串联起来。

  二、ViewGroup的onInterceptTouchEvent方法,该方法一旦返回一次true,以后就再也不会被调用了。该事件列的其他事件(Move、Up)将直接传递给该ViewGroup的onTouchEvent()方法。

  三、前提:ViewGroup包含一个View,而且假设ViewGroup没有拦截DOWN事件,View 处理了DOWN事件(即DOWN事件传递到View的onTouchEvent方法,返回了true),但ViewGroup半路拦截了接下来的MOVE事件。

那么:在这后续到来的第一个MOVE事件中,ViewGroup的onInterceptTouchEvent方法返回true拦截该MOVE事件,但该事件并没有传递给ViewGroup的onTouchEvent方法;这个MOVE事件将会被系统变成一个CANCEL事件传递给View的onTouchEvent方法。后续又来了一个或几个MOVE或者UP事件,(不会再传递给ViewGroup的onInterceptTouchEvent方法),该MOVE、UP事件才会直接传递给ViewGroup的onTouchEvent()进行处理,View再也不会收到该事件列产生的后续事件。

所以自定义控件最好考虑一下CANCEL事件,可能与UP处理逻辑一致,不然滑动嵌套复杂时可能有隐藏的bug。
 
 onTouch和onTouchEvent有什么区别,又该如何使用?从源码中可以看出,这两个方法都是在View的dispatchTouchEvent中调用的,onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,onTouchEvent将不会再执行。
 setOnLongClickListener和setOnClickListener是否只能执行一个? 不是的,只要setOnLongClickListener中的onClick返回false,则两个都会执行;返回true则会屏蔽setOnClickListener

> onTouch/onLongClick/onClick,VelocityTracker

Android触摸屏事件派发机制详解与源码分析三(Activity篇)- http://blog.csdn.net/yanbober/article/details/45932123

1.Android中,控件的Enable和Clickable属性有何区别?

  当一个控件的clickable属性无论是true还是false,控件看起来都是没有任何区别的。而你设置enable的话,那么就有区别了。enable是false的话,控件看起来都不可点击了!
  设置了enable为false的话,onClick事件是完全屏蔽的,而clickable属性就要看设置属性和设置OnClicListener的先后顺序了。enable的优先级要高于clickable。如果enable为false的控件,是不会对事件进行消费的。

    View 的onTouchEvent 方法默认都会消费掉事件(返回true),除非它是不可点击的(clickable和longClickable同时为false),View的longClickable默认为false,clickable需要区分情况,如Button的clickable默认为true,而TextView的clickable默认为false。

2.父parent View的onClick事件与子child View的onTouch事件?

  当child设置click事件时,长按child不会触发parent的longClick事件。解决方案: 
  a.当时我的解决方案是为child设置一个和parent一样的LongClick事件,这样能够解决该问题,但是实际应用中,parent不止一个child,会有3个以上的child,那么将要为所有的child设置longClick事件,并且写出的代码不美观。
  b.第二种解决方式能否让代码自动识别你的意图,是想要触发parent的longClick还是child的click事件。

-- 关于view的onTouch和onClick同时触发解决方案  监听不到OnClick事件是因为在onTouch中这个事件被消费了
  onTouch的return值为true时不能响应onClick事件,设置为false后,就会同时触发两个事件;

办法其实很简单:
  定义一个boolean的全局变量isMove = false,然后在onTouch方法里的MotionEvent.ACTION_MOVE:里边设置isMove = true;

  在MotionEvent.ACTION_UP:判断isMove的值

if (isMove == false) {

   //对click事件的处理

} else if (isMove == true) {

   //对onTouch事件的处理,我仅仅是更新坐标

}

记得一定要设置在break之前再次设置isMove = false;

3.View的onClick事件与onTouch事件 Android?

  点击事件的分发过程如下:dispatchTouchEvent—>onTouchListener的OnTouch方法—>onTouchEvent—>onClickListener的onClick方法。
-- 这三个事件的调用顺序是:onTouch->onLongClick->onClick

  只有onClick是void,而onTouch和onLongClick是boolean,原因是系统对这些事件的处理是有条件,必须满足条件才会触发相应的事件处理函数。
  事件在事件链中进行传递,如果一级没有处理,将向下一级传递,直到传遍整个事件链。
  onTouch如果返回true,则表明对该事件做了处理,不会继续传递该事件在事件链中;onLongClick如果返回true,则表名对该事件做了处理,不会继续传递该事件在事件链中。
  如果希望View中onClick事件和onTouch事件并存,且这两个事件在一个按钮上分别触发,则可以直接去掉onClick事件,直接在onTouch事件中细分,如果按下的位置和抬起的位置相同,即startX==lastX时,认为是onClick事件。

-- View调用顺序大致可以梳理成:onTouch->onLongClick->onClick
  onTouch是Android系统中整个事件机制的基础。在Android中的其他事件,如onClick、onLongClick等都是以onTouch为基础的。在onTouch的ACITON_DOWN事件中,会触发长按事件开启一个新的线程中去判断按压的时间,条件满足则调用performLongClick()函数,并及时回调onLongClick()函数。我们这边可以清楚知道,onLongClick的发生是由单独的线程完成的,并且在从触发到响应这个过程是在ACTION_UP之前,而触发onClick的发生是在ACTION_UP后,当我们调用了ACTION_UP,才会调用performClick()方法,并及回调onclick执行。
  如果在Touch事件的down action的时候返回false,则表示这个组件不会吃掉这个down event,并且对touch的后续动作都不感兴趣。那么整个touch的动作,从一开始刚点击下去的时候,你就不会再被回调到了。只有在onTouch返回false的情况下,onClick才能被执行.

-- android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:
1)public boolean dispatchTouchEvent(MotionEvent ev)  这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
  -- 当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由  dispatchTouchEvent 方法进行分发,
  如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理;
  如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件;
  如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理;
  如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发;
  如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收;
  而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。

> 自定义View的问题

  在安卓里自定义一个View 。同时实现了Activity和View是onTouchEvent监听。
发现只响应了Activity 里的ACTION_UP 和 ACTION_DOWN; View里只有DOWN的事件,怎么都是调不出来。

 后来我在view里设置chickable =true;问题解决了; 最后的返回值是return false; mRootLayout.requestDisallowInterceptTouchEvent(true)不起作用。

//不要父控件拦截
getParent().requestDisallowInterceptTouchEvent(true);(mVideoRootLayout.requestDisallowInterceptTouchEvent(true);)
//需要父控件考虑拦截
getParent().requestDisallowInterceptTouchEvent(false);


dispatchTouchEvent etc- https://github.com/2954722256/use_little_demo
demo_event- https://github.com/2954722256/demo_event
Android中onTouch与onClick两种监听的完全解析- https://blog.csdn.net/ll530304349/article/details/53036276
一个view如何同时响应onTouch和onClick事件- https://blog.csdn.net/jack8068469/article/details/75080395

> VelocityTracker

  VelocityTracker通过跟踪一连串事件实时计算出当前的速度,这样的用法在android系统空间中随处可见,比如Gestures中的Fling, Scrolling等。

  VelocityTracker主要用跟踪触摸屏事件(flinging事件和其他gestures手势事件)的速率。用addMovement(MotionEvent)函数将Motion event加入到VelocityTracker类实例中.你可以使用getXVelocity() 或getXVelocity()获得横向和竖向的速率到速率时,但是使用它们之前请先调用computeCurrentVelocity(int)来初始化速率的单位 。
    public boolean onTouchEvent(MotionEvent ev) {
            if (null == mVelocityTracker) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);
            switch (ev.getAction()) {
                case MotionEvent.ACTION_UP:
                    // 隐藏在左边的宽度
                    int scrollX = getScrollX();
                    Loger.e(ObjEarth.TAG, "V=" + mVelocityTracker.getXVelocity());
                    if (Math.abs(mVelocityTracker.getXVelocity()) > 4000f) {
                        if (mVelocityTracker.getXVelocity() < 0f) {
                            //正向逻辑代码
                        } else {
                            //反向逻辑代码
                        }
                    }
                    return true;
                case MotionEvent.ACTION_MOVE:
                    mVelocityTracker.computeCurrentVelocity(1000); //设置units的值为1000,意思为一秒时间内运动了多少个像素
            }
            return super.onTouchEvent(ev);
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值