关于view的事件分发机制(第一卷)

前言:

在自定义 view 的时候,大家都遇到事件拦截的问题,比如最常见的就是:滑动冲突问题了。

那么,对于这样的问题,我们该如何解决呢?

首先要了解的就是 view 的事件分发机制了。


重要方法:

在介绍代码之前,先介绍三个重要的方法,这些方法也就是处理事件分发时需要重写的方法,在这些方法里写相应的代码来拦截相应的事件。

view的事件分发最重要的就是如下三个方法:

1、分发机制

@Override
    public boolean dispatchTouchEvent(MotionEvent ev)

该方法是用来进行事件的分发,如果事件传递到了当前 view,那么该方法就会被调用,如果返回为 true 表示顺序下发中断,false 表示继续下发事件。

2、拦截机制

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)

可使用该方法来判断是否拦截当前某个事件,如果当前 view 拦截了某个事件,则在同一个事件序列当中,此方法不会再次调用,返回为是否拦截当前事件。

3、处理机制

@Override
    public boolean onTouchEvent(MotionEvent event)

该方法是在 dispatchTouchEvent 方法中调用的,用来处理点击事件,返回是否消费点击事件,如果为 true 则自己消费事件,并可获取到 down、move、up事件,

而且父控件也就不会执行它自己的 onTouchEvent 方法了。这个就有助于屏蔽掉父控件的消费方法。


demo环节:

一个很简单的 demo,一个viewGroup为父控件(之后称为:父亲),父控件里面有一个子控件的 view(之后称为:孩子),

三个按钮:1、拦截孩子,父亲控制;2、允许孩子控制,父亲也可控制;3、允许孩子控制,父亲不可控制。

由此来实现一个简单的拦截机制。 

如果看官对这些都会了,但是想要更深入的学习事件分发机制的话可关注我,在后续会连载分发机制,并会深入到源码。

如果还不够,也可以加我qq,在旁边栏目里有个技术交流的栏目。


下面是 demo 截图:



下面根据 log 分析事件分发的过程,注意:此 log 是通过点击孩子的区域打出的 log,因为点击父亲不用处理分发问题,都是由父亲控制。

默认的情况下是[允许孩子控制,父亲也可控]的状态:

打出的 log:


由此可见,先执行了父亲的 dispatchTouchEvent 的方法进行事件的分发,然后执行到父亲的 onInterceptTouchEvent 方法(默认是不拦截,即:false)。

因为是不拦截,所以会向下分发事件到子类,并执行孩子的 dispatchTouchEvent,

孩子已经没有更多的孩子了,所以会执行孩子的 onTouchEvent方法(默认是不消费,即:false),因此执行完之后会执行父亲的 onTouchEvent。


[拦截孩子,父亲控制] 的状态:

打出的 log:


根据 log 分析,dispatchTouchEvent 返回的为 true,之前就已经介绍了,返回 true 代表顺序下发事件中断。

下发事件中断了,所以只会执行父亲的方法。可见已经没有执行孩子的方法了,成功拦截。


[允许孩子控制,父亲不可控] 的状态:

打出的 log:


根据 log 分析,父亲的 dispatchTouchEvent 返回为 false,继续下发事件给孩子。

执行到父亲的拦截,这里是 false,不拦截,然后执行孩子的 dispatchTouchEvent 接着执行孩子的 onTouchEvent,

上面的 action: 0 代表 down 事件(即:按下去的事件),由于这里孩子的 onTouchEvent 设置的返回为 true,孩子已经消费了。

所以会接着获取更多的事件,这里我就是按了一下,所以会获取到 down 事件和 up 事件,up 事件的 action 是 1。接着也就是跟上面过程一样了。


根据这些分析,各位看官是不是对事件分发机制更加了解了呢?

下面是源代码,最后有源代码下载链接,可以下下来自己分析一下。


demo 代码:

首先是 Activity 的代码:

public class IntercepterActivity extends Activity {

    private Parent parent;
    private Child child;

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

        parent = (Parent) findViewById(R.id.parent);
        child = (Child) findViewById(R.id.child);
    }

    public void doParent(View view){
        parent.setInterceoter(true);
    }

    public void doChild(View view){
        parent.setInterceoter(false);
        child.isConsume(false);
    }

    public void doChildUnParent(View view){
        parent.setInterceoter(false);
        child.isConsume(true);
    }
}
doParent 是拦截孩子,父亲控制;

doChild 是允许孩子控制,父亲也可控;

doChildUnParent 是允许孩子控制,父亲不可控。


下面是父亲的 view,为了简洁、方便,我直接继承 LinearLayout:

public class Parent extends LinearLayout{

    private static final String TAG = "Parent";
    private boolean isInterceote = false;

    public Parent(Context context) {
        this(context, null);
    }

    public Parent(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Parent(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        this.setOrientation(HORIZONTAL);
        this.setBackgroundColor(Color.BLUE);
    }

    /**
     * 是否拦截
     * @param isInterceote ture:拦截
     */
    public void setInterceoter(boolean isInterceote){
        this.isInterceote = isInterceote;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e(TAG,"onInterceptTouchEvent");
        return <span style="font-family: Arial, Helvetica, sans-serif;">isInterceote</span><span style="font-family: Arial, Helvetica, sans-serif;">;</span>
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e(TAG,"dispatchTouchEvent: " + isInterceote);

        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // ACTION_DOWN == 0; UP == 1; MOVE == 2
        Log.e(TAG,"onTouchEvent" + ", event.Action: " + event.getAction());
        return super.onTouchEvent(event);
    }
}

通过 isInterceote 来判断是否拦截,这样父亲就可以随意的拦截孩子的控制。


下面是孩子的 view:

public class Child extends TextView{

    private static final String TAG = "Child";
    private boolean isConsume = false;

    public Child(Context context) {
        this(context, null);
    }

    public Child(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Child(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init();
    }

    private void init() {
        this.setBackgroundColor(Color.RED);
    }

    public void isConsume(boolean isConsume){
        this.isConsume = isConsume;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e(TAG,"dispatchTouchEvent");

        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG,"onTouchEvent" + ", event.Action: " + event.getAction());
        return isConsume;
    }
}

使用 isConsume 来判断是否自己消费,如果为 ture 则不会再由父亲控制了。

layout 如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <view
        android:id="@+id/parent"
        class="com.zane.zhouzhan.test.view.Parent"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:orientation="vertical">

        <view
            android:id="@+id/child"
            class="com.zane.zhouzhan.test.view.Child"
            android:layout_width="100dp"
            android:layout_height="100dp" />

    </view>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拦截孩子,父亲控制"
        android:onClick="doParent" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="允许孩子控制,父亲也可控"
        android:onClick="doChild" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="允许孩子控制,父亲不可控"
        android:onClick="doChildUnParent" />
</LinearLayout>


点击下载源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值