Android事件机制深入探讨(三)

737 阅读3分钟
原文链接: blog.csdn.net

《Android事件机制深入探讨(一)》
《Android事件机制深入探讨(二)》
阅读本文前,请先阅读上两篇文章,本文是以上的扩展、深入讲解,老司机请忽略。
上篇文章我们讲解了设置setOnTouchListener方法后对事件分发流程的影响,这篇我们再引入setOnClickListener方法,来加深对事件分发的理解,老样子,代码复原,把ViewGroup和View的onTouch的返回值改为false,且为ViewGroup和View设置点击事件setOnClickListener并且打印日志,Activity完整代码如下:

public class TouchEventActivity extends AppCompatActivity {

    private CustomLinearLayout mCustomLinearLayout;
    private CustomTextView mCustomTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_touch_event);

        mCustomLinearLayout = (CustomLinearLayout) findViewById(R.id.ll_touch_layout);
        mCustomTextView = (CustomTextView) findViewById(R.id.tv_touch_content);

        mCustomLinearLayout.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是ViewGroup的--->onTouch");
                return false;
            }
        });
        mCustomTextView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是View的--->onTouch");
                return false;
            }
        });

        mCustomLinearLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是ViewGroup的--->onClick");
            }
        });

        mCustomTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是View的--->onClick");
            }
        });
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(getClass().getSimpleName(), "这是Activity的--->dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(getClass().getSimpleName(), "这是Activity的--->onTouchEvent");
        return super.onTouchEvent(event);
    }
}

运行,点击,看一下日志:
事件分发日志
从日志我们可以看出三个事件都是传递到View的onTouchEvent事件而终止,但是View的onTouchEvent的代码我们并没有改:

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.e(getClass().getSimpleName(), "这是View的--->onTouchEvent");
    return super.onTouchEvent(event);
}

我们知道CustomTextView的onTouchEvent方法返回的默认值是false,这是为什么呢?细心的同学发现了,View(CustomTextView)的onClick被执行了,在ACTION_UP事件传递结束后,原因就是我们给View设置了setOnClickListener方法。 我们先来看看View 的setOnClickListener的方法做了什么事,源码如下:

public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}

上面的代码我们看到View不仅保存了OnClickListener对象,而且把标识clickable设为true,然后我们再看系统View的onTouchEvent的源码(这里我不贴出来了),简单说一下就好,系统会在onTouchEvent判断当前View是否可点击,也就是clickable标识,如果为true,返回值就会变成true,并且当前事件为ACTION_UP的时候,会去执行OnClickListener方法,这跟我们说的onTouchEvent默认的返回值为false是矛盾的,其实之前说的onTouchEvent默认的返回值为false只是为了方便大家容易理解,现在更正一下,onTouchEvent默认的返回值并不一定为false,跟当前的View有关系,比如TextView是false,Button(可点击的,clickable为true)是true,这样,我们做一个测试,自定义一个Button,放在ViewGroup里面,且该Button不设置setOnTouchListener和OnClickListener,Button的源码如下:

public class CustomButton extends Button {

    ...

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(getClass().getSimpleName(), "这是View(Button)的--->dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(getClass().getSimpleName(), "这是View(Button)的--->onTouchEvent");
        return super.onTouchEvent(event);
    }
}

运行,点击,打印日志:
事件分发日志
是不是跟CustomTextView不一样了,Button默认是消费onTouchEvent事件的,而TextView并没有。

还是使用CustomTextView,我们把CustomTextView的onTouch返回值改为true,其余不变,照样设置OnClickListener点击事件:

    mCustomTextView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是View的--->onTouch");
            return true;
        }
    });

运行,点击,查看日志:
事件分发日志
从上面的日志我们可以看出,事件传递到onTouch就结束了,onClick并没有被执行,原因刚才说过了,onClick是在onTouchEvent的action 为ACTION_UP的时候执行的,因为我们设置了setOnTouchListener并返回了true,系统并不会执行onTouchEvent方法,所以onClick不会被执行。读者要记住这一点,onTouch的返回值会影响onClick方法的执行。
本文就先讲解到这里了,求求求收藏、喜欢、关注。。。。