本篇是在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事件直接交给顶层处理.