本篇是在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. Android中onSaveInstanceState和onRestoreInstanceState的调用
  2. 使用DialogFragment遇到的一些问题
  3. Android(安卓)Button setTextColor()参数调用分析
  4. kotlin技巧和细节整理
  5. Android(安卓)AIDL实现两个APP间的跨进程通信实例
  6. Android(安卓)ViewRootImpl 解析
  7. 一只不务正业的程序猿玩出个Processing交互库
  8. android Service相关知识点
  9. 在Android(安卓)studio中调用python代码

随机推荐

  1. ScrollView实现界面自动滚动
  2. android 获取http网络图片保存png
  3. 10 个 Android开发人员必备的开发工具
  4. 使用IntelliJ IDEA 编译Android JNI
  5. MVP 笔记
  6. Android 文件系统移植
  7. Android菜鸟日记12 Gallery
  8. Android中常用基本控件的使用方法和步骤(.
  9. JavaEye Android client 收藏管理功能
  10. Android水平方向ListView的使用和注意点