微信扫一扫

028-83195727 , 15928970361
business@forhy.com

【Android事件分发】从简单的onTouch理解Android事件分发

android,事件分发2016-06-07

简单的Touch其实里面有机制在处理着用户这一简单的操作,这个机制是一个规则,将事件一层层的传递下去。下面来看个实例:
下图是一个很常见的布局,Activity由LinearLayout直接填满,然后LinearLayout中只有一个Button。

现在用户对Button进行Touch操作。我们在Button做了Touch的监听。


若是只对button做监听,那么以下就是我们常见的写法

public class MainActivity extends AppCompatActivity {
    private String TAG=MainActivity.class.getSimpleName();
    private LinearLayout linearLayout;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initEvent();
    }

    private void initView(){
        linearLayout=(LinearLayout)findViewById(R.id.layout);
        button=(Button)findViewById(R.id.button);
    }

    private void initEvent(){
        button.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "事件传到  button ");
                return false;
            }
        });
    }
}

若是对LinearLayout就进行Touch监听呢?保留button监听,再加上个linearlayout的监听。如下:

button.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "事件传到  button ");
                return false;
            }
        });
        linearLayout.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "事件传到  linearLayout ");
                return false;
            }
        });

以上代码会有个现象:当linearlayout被触发时,button不一定被触发,而button被触发时,linearlayout一定会被触发。
那么为什么会这样呢?
因为button在linearlayout布局内。事件从上至下的分发下来。
我们可以仔细看看OnTouchListener。

 @Override
 public boolean onTouch(View v, MotionEvent event) {
     return false;
 }

重写时,都会默认返回false。那么返回false意味着什么呢?返回true又会发生什么事呢?
将linearlayout的touch返回值写为true,如下:

  linearLayout.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "事件传到  linearLayout ");
                return true;
            }
        });

通过上面的代码,你会发现button的touch将不会被执行,而这一切的原因就是那个“true”!


现在我们重新理一下,整个事件执行的过程。
手指点击屏幕上的按钮,Activity中的一个dispatchTouchEvent(MotionEvent ev) 方法会一隧道的方式将事件传递给最外层View对应的dispatchTouchEvent(MotionEvent ev)方法,并且最后由View的dispatchTouchEvent(MotionEvent ev)对事件进行分发。
若 dispatchTouchEvent 返回的是true,那么事件将停止向下分发(这就是上面不执行button事件的原因)。表示当前View消费掉事件。
若 dispatchTouchEvent 返回的是false,可以分为两种情况,如下:

  1. 如果该View 获取的事件直接来自 Activity,则会将事件返回给 Activity 的 onTouchEvent 进行消费。
  2. 如果该 View 获取的事件来自父控件,则会将事件返回给上层 View(父控件) 的 onTouchEvent 进行消费。

其他扩展

事件拦截:

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

如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的
onTouchEvent 进行处理; 如果 onInterceptTouchEvent 返回 false,则表示将事件放行,当前 View
上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发; 如果
onInterceptTouchEvent 返回
super.onInterceptTouchEvent(ev),事件默认会被拦截,并将拦截到的事件交由当前 View 的
onTouchEvent 进行处理。

事件响应:

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

如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了 false,那么这个事件会从当前 View
向上传递,并且都是由上层 View 的 onTouchEvent 来接收,如果传递到上面的 onTouchEvent 也返回
false,这个事件就会“消失”,而且接收不到下一次事件。 如果返回了 true 则会接收并消费该事件。 如果返回
super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时相同。