Android事件

Posted by Young Ken on 2016-08-14

Android事件探索

在人机交互之后,出现了我们要探索的事件的概念,这个事件是用来和计算机进行交互的。

页面布局基于树形结构的思考

事件是人机交互的重要媒介。

现在我接触过的大部分前段页面都是这样的,一个分层的结构,都会有个最底层的根,根下有控件集合(能放多个控件的控件)和控件

基于事件的战争

netscape 和 微软 曾经的战争还是比较火热的,当时, netscape 主张捕获方式,微软主张冒泡方式。后来 w3c 采用折中的方式,平息了战火,制定了统一的标准——先捕获再冒泡。

Android事件探索

Android事件的触发是底层的传感器,然后把这事件交给了Acitvity,Activity通dispatchTouchEvent()把事件派发出来。交给ViewRoot,ViewRoot在把事件派发出去,如果子控件是ViewGroup就对ViewGroup进行递归派发,直到最上层的控件,然后事件从最上层的控件再往底层流动,这个时候掉用的是onTouchEvent(),如有控件对这个事件感兴趣,也就是onTouchEvent()返回true,这个事件就认为被消费掉了,如果没有控件对这个事件感兴趣最后就回到了Activity。

MotionEvent

这个类存储了关于事件的全部信息。当你的手指按下的时候,会产生一个事件,这个事件包括当前你事件的时候,几个手指按下,按下的坐标位置等等。详情看Android事件-MotionEvent,这个可以算是事件的一个基础。

onTouchEvent()

没有被消费的事件

现在我们有一个Activity,加载了一个ViewGroup容器,ViewGroup里面有一个View。当点击View的时候。事件会从Activity的dispatchTouchEvent()开始分发,分发到Activity的子类,也就是ViewGroup,因为ViewGroup是一个容器,事件会递归分发到ViewGroup的每个子控件,因为这例子中ViewGroup就一个子类,分发给View,View是控件的最上级。View调用自己的onTouchEvent(),一直往上调用,直到最底层的Activity。

如果这个事件没被消费,当移动和抬起的时候,Android直接从Activity的dispatchTouchEvent()到Activity的onTouchEvent()

View消费事件

如果View的onTouchEvent()返回true,那么这个事件就认为被消费掉了,事件将不再继续网上冒泡。但是一定要注意几个点。

  • 针对ACTION_MOVEACTION_UP返回true
1
2
3
4
5
6
7
8
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
return true;
}
return super.onTouchEvent(event);
}

这个是ViewGroup的代码,我们重写了onTouchEvent()方法。这个方法在ACTION_MOVE的时候返回true。但是在ACTION_DOWN的时候Android认为这个事件没有被关注,所以在ACTION_MOVE的时候从Activity的dispatchTouchEvent()直接到Activity的onTouchEvent()。也就是说这ViewGroup的onTouchEvent()ACTION_MOVE的时候根本没有被调用。

  • 针对ACTION_DOWN返回true
1
2
3
4
5
6
7
8
9
10
11
12
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
return true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
return true;
}
return false;
}

这个还是ViewGroup得代码,我们重写了onTouchEvent()方法。在ACTION_DOWN中返回true,这样就认为ViewGroup对这个事件感兴趣,不会在Acitvity的dispatchTouchEvent(),直接调用Activity的onTouchEvent(),看下面的流程。也就是说,你对你对ACTION_DOWN不感兴趣,那么之后的事件就会走捷径。

当ViewGroup变成ScrollView

当把ViewGroup变成ScrollView之后会发生什么,当手指按下的时候,同样View把事件消费掉,ACTION_DOWN事件不再冒泡。但是这个时候你移动手指,事件不会从ScrollView的dispatchTouchEvent()分发到View,而是直接跳转到了ScrollView的onTouchEvent(),这是为什么呢?其实是这样的,在ScrollView中有个函数是这个onInterceptTouchEvent(),这是分发事件的拦截,当你的事件是ACTION_MOVEACTION_UP的时候,onInterceptTouchEvent()返回true,这事件就不继续分发,直接把事件给ScrollView的onTouchEvent()

dispatchTouchEvent()

上面讲的都是关于onTouchEvent()的事件,但是dispatchTouchEvent()也很重要。

Activity的dispathTouchEvent()返回true/false

  • 整体返回

    dispathTouchEvent()直接返回,事件接着结束了。

  • 根据不同的事件返回

    1
    2
    3
    4
    5
    6
    7
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_MOVE) {
    return true;
    }
    return super.dispatchTouchEvent(ev);
    }

    因为Acitvity的dispatchTouchEvent()是程序事件的入口,那么首先执行的是ACTION_DOWN,因为这个事件调研的是super的dispatchTouchEvent(),那么这个事件将正常的走完事件的流程,在行ACTION_MOVE的时候事件被消费掉,当执行到ACTION_UP的时候,因为当执行ACTION_DOWN的候,onTouchEvent(),都不对这个事件感兴趣,那么事件就是走捷径的。自己想一下如是ACTION_DOWN的时候返回true时什么样子的。

ViewGroup的dispathTouchEvent()返回true/false

ViewGroup的dispatchTouchEvent()和Activity的dispatchTouchEvent()还是有点区别的。都知道在ViewGroup中还有一个重要的函数,onInterceptTouchEvent(),这个事件是拦截事件的,当不想事件继续往下分发了,可以让这个函数返回true,这样就可以调用ViewGroup的onTouchEvent()了。onInterceptTouchEvent()函数在什么时候执行。只有当ViewGroup的dispatchTouchEvent()调用super的时候才调用这个方法。那么ViewGroup的dispatchTouchEvent()返回true和false事件有怎么分发呢。

这很酷了,首先事件从黑点开始,当ViewGroup的dispatchTouchEvent()返回true,很简单事件被消费了,但是当返回false的时候,事件调用上一层(也就是Activity的)的onTouchEvent(),记得这个概念,除了最上层的Activity的dispatchTouchEvent()返回false,事件被消费掉,其他的都是调用上层的onTouchEvent()。只有调用super才能掉用onInterceptTouchEvent(),当这个事件返回true的时候,调用ViewGroup的onTouchEvent()事件,当返回false和返回suer的时候,会继续分发事件。

View的dispathTouchEvent()返回true/false

那么当View的dispathTouchEvent()返回false/true怎么样呢。

当View的dispathTouchEvent()的返回false,会调用上层的onTouchEvent(),当返回true的时候会被消费掉,当调用super的时候回调用View的onTouchEvent()

总结

dispathTouchEvent()

  1. 当Activity返回true/false的时候,事件直接就被消费掉了。
  2. 当ViewGroup和View返回true的时候,事件就被消费掉了,当返回false的时候,调用上一层的onTouchEvent(),当ViewGroup调用super的时候,会调用onInterceptTouchEvent(),当View调用super的时候,会调用onTouchEvent()

onInterceptTouchEvent()

  1. 只有ViewGroup才会有这个方法。
  2. 返回true的时候是事件拦截,调用ViewGroup的onTouchEvent()
  3. 调用super/false的时候调用View的dispathTouchEvent()

onTouchEvent()

  1. 当返回true的时候事件被消费掉。
  2. 当返回false/super的时候,事件继续冒泡。
  3. ACTION_DOWN不被消费的时候,事件会走捷径,也就是ACTION_MOVEACTION_UP不被调用。