Android事件機制深入探討(三)

《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方法的執行。
本文就先講解到這裏了,求求求收藏、喜歡、關注。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章