Android 编程下Touch 事件的分发和消费机制和OnTouchListener,OnClickListener和OnLongClickListener的关系*

1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

Touch 事件发生时 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法会以隧道方式(从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递)将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。dispatchTouchEvent 的事件分发逻辑如下:

如果 return true,事件会分发给当前 View 并由 dispatchTouchEvent 方法进行消费,同时事件会停止向下传递;
如果 return false,事件分发分为两种情况:
如果当前 View 获取的事件直接来自 Activity,则会将事件返回给 Activity 的 onTouchEvent 进行消费;
如果当前 View 获取的事件来自外层父控件,则会将事件返回给父 View 的 onTouchEvent 进行消费。
如果返回系统默认的 super.dispatchTouchEvent(ev),事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。


2.事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

在当前  View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系统默认的 super.dispatchTouchEvent(ev) 情况下,事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件拦截逻辑如下:

如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理;
如果 onInterceptTouchEvent 返回 false,则表示将事件放行,当前 View 上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发,如果当前事件没有点击在子View上而是在当前View其他空白区域,则发给当前 View 的 onTouchEvent 进行处理;
如果 onInterceptTouchEvent 返回super.onInterceptTouchEvent(ev)和返回false的处理逻辑一样。


3.事件响应:public boolean onTouchEvent(MotionEvent ev)

在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 返回 true  或者由子View 的 onTouchEvent返回false的情况下 onTouchEvent 会被调用。onTouchEvent 的事件响应逻辑如下:

如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了 false,那么这个事件会从当前 View 向上传递,并且都是由上层 View 的 onTouchEvent 来接收,如果传递到上面的 onTouchEvent 也返回 false,这个事件就会“消失”,而且接收不到下一次事件。

如果返回了 true 则会接收并消费该事件,如果没有其他特殊情况,下次事件不经过onInterceptTouchEvent(MotionEvent ev)。
如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时相同。

OnTouchListener,OnClickListener和OnLongClickListener的关系

在OnTouchListener事件里有MotionEvent.ACTION_DOWN,MotionEvent.ACTION_UP和MotionEvent.ACTION_MOVE三个事件,我们在这里只讨论MotionEvent.ACTION_DOWN,MotionEvent.ACTION_UP,与MOVE无关。在下面的文章中我们简称MotionEvent.ACTION_DOWN为TouchDown,简称MotionEvent.ACTION_UP为TouchUp。我们还知道Android有消化事件这个概念,在setOnTouchListener函数中如果是TouchDown我们返回true,在下文中我们称消化了TouchDown,返回false则没消化,TouchUp同理。

下面我们说明关系和证明(在用程序证明时每次运行都以 程序----操作----结果的顺序给出)。

1)OnTouchListener和OnClickListener的关系

关系:产生OnClick事件的条件(充要条件)是;TouchDown和TouchUp事件都没有被消化。事件处理的过程是:先处理TouchDown,之后处理TouchUp,如果这两个事件都没被消化,则处理OnClick事件。

程序结果:由于这个关系比较简单就不上图了。程序分4种情况分别为:

a)程序:消化TouchDown,不消化TouchUp------操作:单击按钮------结果:不产生OnClick事件

b)程序:不消化TouchDown,消化TouchUp------操作:单击按钮------结果:不产生OnClick事件

c)程序:消化TouchDown,消化TouchUp------操作:单击按钮------结果:不产生OnClick事件

d)程序:不消化TouchDown,不消化TouchUp----操作:单击按钮----结果:产生OnClick事件

2)OnTouchListener和OnLongClickListener的关系

关系:产生OnLongClick事件的条件(充要条件)是;在产生TouchDown事件,并且该TouchDown没有被消化,之后500毫秒内没有响应到TouchUp,则产生OnLongClick事件。

程序结果:对运行情况按TouchDown是否被消化,TouchUp是否被消化,我的操作是长按还是短按可以分为8种运行情况,在这里就不一一列举了,按我们的需要进行分类列举。

a)如果消化TouchDown,按照前面给出的关系,则不可能产生OnLongClick事件。事实是同过我的测试,在这个条件下我对TouchUp是否被消化和操作是长按还是短按4种情况都进行测试,结果都是不产生OnLongClick事件。

b)不消化TouchDown,下面对TouchUp是否被消化又分为两种情况:

bx)不消化TouchDown,不消化TouchUp,对长按和短按分别测试,根据前面给出的关系我们可以推断出长按产生OnLongClick,短按不产生OnLongClick

bx1)程序:不消化TouchDown,不消化TouchUp,----操作:短时间单击按钮----结果:不产生OnLongClick事件

bx2)程序:不消化TouchDown,不消化TouchUp,----操作:长时间单击按钮----结果:产生OnLongClick事件

测试结果满足我们的推断。

by)不消化TouchDown,消化TouchUp,对长按和短按分别测试,根据前面给出的关系我们可以推断出不论长按还是短按,都会产生OnLongClick事件。

by1)程序:不消化TouchDown,消化TouchUp,----操作:短时间单击按钮----结果:产生OnLongClick事件

by2)程序:不消化TouchDown,消化TouchUp,----操作:长时间单击按钮----结果:产生OnLongClick事件

测试结果满足我们的推断。

测试完全符合我们的预期,所以前面给出的关系是对的。

补充:OnClickListener和OnLongClickListener的关系,根据汉语的意思了,可能会感觉到这两个事件会有关系。但是事实是他们之间的关系是没有关系,前面已经给出这两个事件产生的条件了,而且是充分条件,这足已说明这两个事件没关系了。

scrollview嵌套HorizontalScrollView卡顿现象解决

开发中经验会遇到滑动里面嵌入滑动的问题,但是这种情况下触摸事件就会发生冲突。导致滑动非常卡,甚至出现程序停止响应。这种情况下我们一般需要重写view。下面给出重新scrollview的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CustomScrollView extends ScrollView {  
    private GestureDetector mGestureDetector;  
    View.OnTouchListener mGestureListener;  
     
    public CustomScrollView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        mGestureDetector = new GestureDetector(new YScrollDetector());  
        setFadingEdgeLength(0);  
    }  
     
    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) {  
        return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev);  
    }  
     
    // Return false if we're scrolling in the x direction    
    class YScrollDetector extends SimpleOnGestureListener {  
        @Override 
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {  
            if(Math.abs(distanceY) > Math.abs(distanceX)) {  
                return true;  
            }  
            return false;  
        }  
    }  
原文地址:https://www.cnblogs.com/chenxibobo/p/6136761.html