当前位置: 代码迷 >> Android >> android 事件散发 拦截 (onInterceptTouchEvent dispatchTouchEvent onTouchEvent)
  详细解决方案

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

热度:580   发布时间:2016-04-24 11:08:58.0
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事件直接交给顶层处理.



















  相关解决方案