View的事件分发简单总结

本文详细解析了自定义TextView如何继承并重写dispatchTouchEvent和onTouchEvent方法,以及在MainActivity中设置点击和触摸事件监听的过程。通过分析源码,解释了事件分发机制,特别是当触摸事件发生时,如何判断并处理这些事件。同时,探讨了如果不在MainActivity中设置监听,事件处理的流程将如何变化。
摘要由CSDN通过智能技术生成

一、自定义一个MyCustomTextView类继承View的子类TextView。

public class MyCustomTextView extends TextView {

    private static final String TAG = "MyCustomTextView";

    public MyCustomTextView(Context context) {
        super(context);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d(TAG, "dispatchTouchEvent: ");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

}

二、在MinaActivity的布局文件activiy_main.xml中加入自定义的MyCustomTextView。

<com.ray.myviewtouchevent.custom.MyCustomTextView
    android:id="@+id/my_custom_tv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:ignore="MissingConstraints"
    android:text="触摸我"/>

三、在MinaActivity中实现自定义TextView的OnTouch和OnClick事件的监听。
public class MainActivity extends AppCompatActivity {

    private MyCustomTextView my_custom_tv;

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

    private void initView() {
        my_custom_tv = findViewById(R.id.my_custom_tv);
        my_custom_tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        my_custom_tv.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return false;
            }
        });
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

}

四、准备就绪,进入View的dispatchTouchEvent方法查看源码(在自定义重写的dispatchTouchEvent方法中,按住ctrl,点击super.dispatchTouchEvent(event)进入)。

copy主要代码

public boolean dispatchTouchEvent(MotionEvent event) {
    // If the event should be handled by accessibility focus first.
    boolean result = false; // 定义dispatchTouchEvent方法返回boolean值的结果result

    if (onFilterTouchEventForSecurity(event)) { // 符合androiud触摸的安全策略,进入判断。
        
        //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo; 
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }

        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }

    return result;
}

分析关键代码:

1、ListenerInfo li = mListenerInfo; 

把View类中的mListenerInfo赋值给局部ListenerInfo的li。

2、查看ListenerInfo代码。

static class ListenerInfo {

    @UnsupportedAppUsage
    ListenerInfo() {
    }

    @UnsupportedAppUsage
    public OnClickListener mOnClickListener;

    @UnsupportedAppUsage
    protected OnLongClickListener mOnLongClickListener;

    @UnsupportedAppUsage
    private OnTouchListener mOnTouchListener;

}

其中我们关心的包括点击的监听mOnClickListener、长按的监听mOnLongClickListener、和触摸的监听mOnTouchListener。

3、在View类中搜索mListenerInfo赋值初始化代码如下。

ListenerInfo getListenerInfo() {
    if (mListenerInfo != null) {
        return mListenerInfo;
    }
    mListenerInfo = new ListenerInfo();
    return mListenerInfo;
}

那我触摸屏幕的什么时候调用了getListenerInfo,我们看在MainActivity设置的点击和触摸的监听方法:

private void initView() {
    my_custom_tv = findViewById(R.id.my_custom_tv);
    my_custom_tv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

        }
    });

    my_custom_tv.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return false;
        }
    });
}

按住ctrl进入点击setOnClickListener进入View的代码如下:

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

红色代码getListenerInfo().mOnClickListener就可以拿到mListenerInfo并且设置mOnClickListener的值为1。

相同原理setOnTouchListener方法就可以拿到mListenerInfo并且设置mOnTouchListener的值为1。

public void setOnTouchListener(OnTouchListener l) {
    getListenerInfo().mOnTouchListener = l;
}

4、接着看下面的if判断:

if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}

当我们在MainActivity中设置了自定义的TextView后,经过上面的分析:

li != null 成立,自定义TextView设置触摸和点击事件的时候已经赋值;

li.mOnTouchListener != null成立,自定义TextView设置触摸事件的时候已经赋值为1;

(mViewFlags & ENABLED_MASK) == ENABLED成立,这句不用管,不是我们今天的关键;

li.mOnTouchListener.onTouch(this, event):这句代码,需要看我们在MainActivity中setOnTouchListener

设置触摸事件时候怎么处理,默认返回false。如果默认返回false的话,这样li.mOnTouchListener.onTouch(this, event)就不成立,if判断失败。

5、如果上面的if判断失败,result为false:

if (!result && onTouchEvent(event)) {
result = true;
}

if判断中的!result成立。

然后查看简化后的View中的onTouchEvent方法

public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int action = event.getAction();

    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                .........
                break;

            case MotionEvent.ACTION_DOWN:
                ........
                break;

            case MotionEvent.ACTION_CANCEL:
                ........
                break;

            case MotionEvent.ACTION_MOVE:
                break;
        }

        return true;
    }

    return false;
}

这段代码关键的内容就是if (clickable || (viewFlags & TOOLTIP) == TOOLTIP)判断成立,进入到switct,然后根据action,也就是当前事件是按下(Down)、移动(Move)还是抬起(Up)来对事件进行不同的处理,最后返回的true表示该View对事件进行了消费。反之if判断不成立,返回false,对事件不进行处理。

五、总结和验证。

1、自定义MyCustomTextView后,不再MainActivity中进行setOnClickListener和setOnTouchListener。

分析:我们点击MyCustomTextView后,调用MyCustomTextView中重写的dispatchTouchEvent方法,然后调用View中的dispatchTouchEvent方法:

ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
        && (mViewFlags & ENABLED_MASK) == ENABLED
        && li.mOnTouchListener.onTouch(this, event)) {
    result = true;
}

if (!result && onTouchEvent(event)) {
    result = true;
}

因为li为空,所以调用MyCustomTextView中重写的onTouchEvent方法。

结论:

2、自定义MyCustomTextView后,在MainActivity中进行setOnTouchListener并且返回true。

private void initView() {
        my_custom_tv = findViewById(R.id.my_custom_tv);
//        my_custom_tv.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//
//            }
//        });
//
        my_custom_tv.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return true;
            }
        });
    }

分析:我们点击MyCustomTextView后,调用MyCustomTextView中重写的dispatchTouchEvent方法,然后调用View中的dispatchTouchEvent方法:

ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
        && (mViewFlags & ENABLED_MASK) == ENABLED
        && li.mOnTouchListener.onTouch(this, event)) {
    result = true;
}

if (!result && onTouchEvent(event)) {
    result = true;
}

li != null 成立,自定义TextView设置触摸和点击事件的时候已经赋值;

li.mOnTouchListener != null成立,自定义TextView设置触摸事件的时候已经赋值为1;

(mViewFlags & ENABLED_MASK) == ENABLED成立,这句不用管,不是我们今天的关键;

li.mOnTouchListener.onTouch(this, event):这句代码,在MainActivity中setOnTouchListener时候返回了ture,成立。

所以if中的所有条件都成立,result返回ture,即消费了事件。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值