您的位置:首页 > 移动开发 > Android开发

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

2016-04-13 18:23 726 查看
本篇是在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事件直接交给顶层处理.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: