android 事件分发 拦截 (onInterceptTouchEvent dispatchTouchEvent onTouchEvent)

本篇是在http://www.cnblogs.com/linjzong/p/4191891.html 基础上的实践和延伸.


Touch事件分发中只有两个主角:ViewGroup和View。Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。

ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。

View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。

先分析ViewGroup的处理流程:首先得有个结构模型概念:ViewGroup和View组成了一棵树形结构,最顶层为Activity的ViewGroup,下面有若干的ViewGroup节点,每个节点之下又有若干的ViewGroup节点或者View节点,依次类推。如图:


当一个Touch事件(触摸事件为例)到达根节点,即Acitivty的ViewGroup时,它会依次下发,下发的过程是调用子View(ViewGroup)的dispatchTouchEvent方法实现的。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件。


话说还是上代码验证,写过了就

 

ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,而View的dispatchTouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理,而处理的方法,便是onTouchEvent事件. 

public class Btn extends Button {
    public Btn(Context context) {
        super(context);
    }

    public Btn(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("Btn","dispatchTouchEvent "+ev.getAction()+"");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("Btn","onTouchEvent "+event.getAction()+"");
        return super.onTouchEvent(event);
    }

}

public class BtnLinerLayout extends LinearLayout {
    public BtnLinerLayout(Context context) {
        super(context);
    }

    public BtnLinerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("BtnLinerLayout","onInterceptTouchEvent "+ev.getAction()+"");
        return true;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("BtnLinerLayout","dispatchTouchEvent "+ev.getAction()+"");
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("BtnLinerLayout","onTouchEvent "+event.getAction()+"");
        return super.onTouchEvent(event);
    }

}


public class RootLinerLayout extends LinearLayout {
    public RootLinerLayout(Context context) {
        super(context);
    }

    public RootLinerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("RootLinerLayout","onInterceptTouchEvent "+ev.getAction()+"");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("RootLinerLayout","dispatchTouchEvent "+ev.getAction()+"");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("RootLinerLayout","onTouchEvent "+event.getAction()+"");
        return super.onTouchEvent(event);
    }

}
public class TestActivity extends Activity {

    private Btn btn;
    private BtnLinerLayout btnLinerLayout;
    private RootLinerLayout rootLinerLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        init();
    }
    private void init(){
        btn = (Btn) findViewById(R.id.btn);
        btnLinerLayout = (BtnLinerLayout) findViewById(R.id.btn_ll);
        rootLinerLayout = (RootLinerLayout) findViewById(R.id.rootll);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i("btn","setOnClickListener");
            }
        });
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("TestActivity","onTouchEvent "+event.getAction()+"");
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("TestActivity","dispatchTouchEvent "+ev.getAction()+"");
        return super.dispatchTouchEvent(ev);
    }
}






 main.xml

<com.reactnative.RootLinerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:id="@+id/rootll"
    android:layout_height="match_parent">

    <com.reactnative.BtnLinerLayout
        android:id="@+id/btn_ll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <com.reactnative.Btn
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:text="button"
            android:layout_height="wrap_content" />

    </com.reactnative.BtnLinerLayout>

</com.reactnative.RootLinerLayout>



日志都添加完毕,现在我们点击btn 看看执行顺序.

 


可见默认情况下 viewgroup会通过 dispatchTouchEvent一直向下分发事件.dispatchTouchEvent 过程中调用onInterceptTouchEvent看viewgroup自己会不会对事件进行拦截.
先是down 然后是up.直到叶子节点view 时候调用其onTouchEvent 然后 在down的时候调用 onclicklistener. 


现在我们来验证dispatchTouchEvent 返回值对事件分发的影响. 正常情况 dispatchTouchEvent 默认为super.dispatchTouchEvent  .return true; 则事件不再往底层传递.而ontouch 事件也应该由改view 或者viewgroup的来消费.

测试ONE (btnLinerLayout的dispatchTouchEvent  分别 返回 false 和 true

false 情况:


 可以看到down事件在btnLinerLayout 停止分发了,  事件首先被rootLinerLayout的onTouchEvent处理 然后 返回给上级的TestActivity onTouchEvent处理. 最后一个onTouchEvent 被定为后续事件的处理着,up 事件就 直接交给TestActivity来处理.

 btn的 onclickListener 不执行了 ,也就是事件也没有下发到Btn上

true 情况:


 true 就与预想的一样 事件不在往底层传递.并且没有地方消费onTouchEvent...|| 



总结 只要dispatchTouchEvent 不为super 那么就会中断事件的传递.只不过false的情况 事件的响应父级会默认一次往上都响应到onTouchEvent里面. 并且随后的up事件会直接交给顶层目标来处理.  true的话事件也会依次回传但是都不响应.


测试two(BtnLinerLayout的onInterceptTouchEvent 返回 true ,dispatchTouchEvent 为 super | true | false  ):

正常情况.onInterceptTouchEvent  返回true 代表事件被当前viewgroup拦截,和消费


dispatchTouchEvent  默认super():


  BtnLinerLayout执行顺序为dispatchTouchEvent- onInterceptTouchEvent -onTouchEvent  的确自己能处理onTouchEvent了.并且依次传递给上层的onTouchEvent


dispatchTouchEvent   为 true 


事件被拦截 并且 没有继续下发.到BtnLinerLayout 截至了.


dispatchTouchEvent   为 false



事件被拦截.没有继续下发.当前也没有消费 依次次交由父类 onTouchEvent响应. 最后 事件交由顶层的TestActicty onTouchEvent执行 后续down事件直接转给 TestActivity  

。。。这情况和直接设置为dispatchTouchEvent 为false  onInterceptTouchEvent   为默认一样.向上传递相应 默认都执行onTouchEvent.


 由上面2轮测试可以看出


onInterceptTouchEvent  为true . 事件会被当前的viewgruop消费到onTouchEvent中.默认情况下依次响应到父类的onTouchEvent中.最后到顶层.随后的up事件直接交给顶层处理.



















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值