关于Android的触摸事件交互,其实就是在View和ViewGroup之间的交互,view主要是指一下基础组件,比如button,TextView等,ViewGroup主要指的是LinearLayout、RelativeLayout、ListView等一些布局控件。当然事件的传递需要控件中的一些方法进行响应。
在View中的方法:
public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
public boolean onInterceptTouchEvent(MotionEvent ev)
dispatchTouchEvent()方法:
1.dispatchTouchEvent()方法,这个返回值决定是否屏蔽后续事件。
false,屏蔽后续事件ACTION_MOVE,ACTION_UP。
true,不屏蔽后续事件ACTION_MOVE,ACTION_UP。
2.dispatchTouchEvent()方法,是否执行super.dispatchTouchEvent(ev)。
执行,调用onInterceptTouchEvent()方法和onTouchEvent()方法。
不执行,不调用onInterceptTouchEvent()方法和onTouchEvent()方法。
onInterceptTouchEvent()方法分析:
1.onInterceptTouchEvent()方法,对于这个返回值
true,拦截,不向下一层次的dispatchTouchEvent()方法传递
false,不拦截,向下一层次的dispatchTouchEvent()方法传递
这两种情况与父类的方法的是否执行都无关。
onTouchEvent()方法分析:
1.onTouchEvent()方法,对于这个返回值
false,屏蔽后续事件ACTION_MOVE,ACTION_UP。
true,不屏蔽后续事件ACTION_MOVE,ACTION_UP。
这两种情况与父类的方法的是否执行都无关。
首先说明一下针对dispatchTouchEvent()方法返回值为true或者false的情况,真的不知道什么时候用
下面对事件写下工程代码,首先是自定义的布局控件LayoutView1和LayoutView2,都继承了LinearLayout,需要对上面的函数进行重写,LayoutView1和LayoutView2代码相同,下面只贴出LayoutView2的
LayoutView2:
package com.lianxi.touchstudy;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class LayoutView2 extends LinearLayout {
private final String TAG = "LayoutView2";
public LayoutView2(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, TAG);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onInterceptTouchEvent action:ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onInterceptTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "onInterceptTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "onInterceptTouchEvent ACTION_CANCEL");
break;
}
return super.onInterceptTouchEvent(ev);
// return false;
// return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouchEvent action:ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "onTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "onTouchEvent ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
// return true;
// return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "dispatchTouchEvent action:ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "dispatchTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "dispatchTouchEvent ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(ev);
// return true;
}
}
TestButton:
package com.lianxi.touchstudy;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;
public class TestButton extends Button {
private final static String tag = "TestButton";
public TestButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(tag, "TestButton-onTouchEvent-ACTION_DOWN...");
break;
case MotionEvent.ACTION_UP:
Log.d(tag, "TestButton-onTouchEvent-ACTION_UP...");
break;
default:
break;
}
return super.onTouchEvent(event);
// return true;
// return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(tag, "TestButton-dispatchTouchEvent-ACTION_DOWN...");
break;
case MotionEvent.ACTION_UP:
Log.d(tag, "TestButton-dispatchTouchEvent-ACTION_UP...");
break;
default:
break;
}
return super.dispatchTouchEvent(event);
// return true;
}
}
<com.lianxi.touchstudy.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.lianxi.touchstudy.LayoutView2 android:id="@+id/linearlayout2" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" > <com.lianxi.touchstudy.TestButton android:id="@+id/testBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按钮" android:textColor="#0000FF" android:textSize="40sp" /> </com.lianxi.touchstudy.LayoutView2> </com.lianxi.touchstudy.LayoutView1>
最后是MainActivity.java:
package com.lianxi.touchstudy; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.Window; public class MainActivity extends Activity { private TestButton testBtn; private final static String tag = "MainActivity"; private LayoutView2 testLinelayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); testBtn = (TestButton) findViewById(R.id.testBtn); testLinelayout = (LayoutView2) findViewById(R.id.linearlayout2); testBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { Log.d(tag, "testBtn---onClick..."); } }); testBtn.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View arg0, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(tag, "testBtn-onTouch-ACTION_DOWN..."); break; case MotionEvent.ACTION_UP: Log.d(tag, "testBtn-onTouch-ACTION_UP..."); break; default: break; } return false; } }); testLinelayout.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub switch(event.getAction()){ case MotionEvent.ACTION_DOWN: Log.d(tag, "testLinelayout-onTouch-ACTION_DOWN..."); break; case MotionEvent.ACTION_UP: Log.d(tag, "testLinelayout-onTouch-ACTION_UP..."); break; default:break; } return false; } }); testLinelayout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Log.d(tag, "testLinelayout---onClick..."); } }); } public boolean dispatchTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(tag, "MainActivity-dispatchTouchEvent-ACTION_DOWN..."); break; case MotionEvent.ACTION_UP: Log.d(tag, "MainActivity-dispatchTouchEvent-ACTION_UP..."); break; default: break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(tag, "MainActivity-onTouchEvent-ACTION_DOWN..."); break; case MotionEvent.ACTION_UP: Log.d(tag, "MainActivity-onTouchEvent-ACTION_UP..."); break; default: break; } return super.onTouchEvent(event); } }
现在针对不同的情况进行说明:事件发生,由MainActivity中的dispatchTouchEvent()来向下进行分发----->L1中的dispatchTouchEvent()中来向子view分发,onInterceptTouchEvent方法根据返回值来判断是否需要拦截,是否调用onTouchEvent()---->.......----->TestButton中的dispatchTouchEvent()根据返回值判断是否需要调用onTouchEvent()
首先针对TestButton
1.dispatchTouchEvent()返回值设为true,onTouchEvent()返回值为super.onTouchEvent(),点击TestButton按钮,控制台显示:
可以在打印信息中发现,onTouchEvent()没有被调用,因为只有dispatchTouchEvent()返回值为super.dispatchTouchEvent(),才会调用onTouchEvent()方法
2.1 onTouchEvent()返回值为true,dispatchTouchEvent()返回值为super.dispatchTouchEvent()
2.2 onTouchEvent()返回值为super.TouchEvent,dispatchTouchEvent()返回值为super.dispatchTouchEvent()
通过2.1和2.2比较可以发现,因为onTouchEvent返回值的不同,testbutton的onClick()方法被调用了。根据源码(此地不贴源码,因为太长,自己也不太懂,后面附上大神的分析帖子),发现onClick()方法是在super.onTouchEvent()中被调用的,返回值为true则说明触摸事件被testButton消耗掉了,后续的ACTION_MOVE、ACTION_UP会继续传到testButton做处理
2.3.1 此时onTouchEvent()返回值为false,dispatchTouchEvent()返回值为super.dispatchTouchEvent()
可以看出LayoutView2的onTouch()和onClick()方法被调用,testButton()没有对ACTION_UP做出反应,当textButton()的onTouchEvent()返回值为false时,说明,testButton不处理触摸事件,然后继续往上一层传,让父容器来判断是否需要处理
下面是LayoutView2分析
1.接着上部分,testButton的onTouchEvent()返回值为false,dispatchTouchEvent()的返回值为super.dispatchTouchEvent(),testButton对事件不做处理,然后LayoutView2的onTouchEvent()返回值为true,其他方法返回值仍为父类方法
此时为LayoutView2对触摸事件做出消耗,如果LayoutView2不处理,让onTouchEvent返回false,事件继续往上层走
2.1 dispatchTouchEvent()的返回值为true,其他返回父类的方法
发现每次dispatchTouchEvent()的返回值为为true的时候,onTouch()就不会调用,根据源码分析(其实是大神根据源码分析)onTouch是在super.dispatchTouchEvent()中调用的,同时如果返回值不为super.dispatchTouchEvent(),那么当前ViewGroup的onInterceptTouchEvent()和onTouchEvent()都不会调用,根据onTouchEvent和onClick()的关系,可以判断调用顺序,dispatchTouchEvent(返回值为super.dispatchTouchEvent())--->onInterceptTouchEvent()--->onTouch()--->onTouchEvent(返回值为super.onTouchEvent())--->onClick()
2.2 dispatchTouchEvent()的返回值为super.dispatchTouchEvent(),onInterceptTouchEvent()返回true进行拦截,其他返回其父类方法
可以发现,onTouchEvent()没有对事件做处理,调用了onClick()方法
2.3 dispatchTouchEvent()的返回值为super.dispatchTouchEvent(),onInterceptTouchEvent()返回true进行拦截,onTouchEvent()返回true,事件由layoutView2来处理消耗,其他返回其父类方法
事件被消耗,如果layoutView2不想处理,onTouchEvent()返回false,事件继续往上走
总结:现在依然不知道dispatchTouchEvent()返回true或者false的作用,大致流程就是以上所写,如果onTouchEvent()不对事件做处理,其控件所绑定的onClick()就会执行,onTouch()只要dispatchTouchEvent()返回super.dispatchTouchEvent(),就会执行。
上边关于源码的分析,详见大神的帖子:
android事件传递机制以及onInterceptTouchEvent()和onTouchEvent()详解二之小秘与领导的故事