android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。
一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP
当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?我只能很肯定的对你说不一定。呵呵,为什么呢?看看下面我的调查结果你就明白了。
android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:
1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View, TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。
下面作者给出个例子,demo如下图显示:
demo分为3层,最外层scrollview,中间是listview,最里面是包裹了scrollview的textview。
源码中最外层的scrollview和listview都为自定义view,重载了
dispatchKeyEvent(KeyEvent event) ,onInterceptTouchEvent(MotionEvent ev),并且都返回false,根据上图来看,就是说明都交给下层处理,scrollview先交给listview处理,listview不管,又交给list-item处理,所以最后一层的控件响应了滑动事件。
下面贴上源码以供后期参考方便:
布局文件:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.boredream.demo.tedispatch.MyScrollView
android:id="@+id/sv_top"
android:layout_width="fill_parent"
android:layout_height="400dp"
android:background="#fff" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<com.boredream.demo.tedispatch.MyListView
android:id="@+id/lv_mid"
android:layout_width="fill_parent"
android:layout_height="600dp"
android:layout_marginBottom="100dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="100dp"
android:background="#000" />
</LinearLayout>
</com.boredream.demo.tedispatch.MyScrollView>
</LinearLayout>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_bottom"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:textColor="#fff" />
<ScrollView
android:layout_width="fill_parent"
android:layout_height="150dp"
android:layout_marginLeft="50dp" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#060"
android:text="bottom"
android:textSize="200sp" />
</ScrollView>
</LinearLayout>
最外层自定义scrollview:
package com.boredream.demo.tedispatch;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.ScrollView;
public class MyScrollView extends ScrollView {
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
自定义listview:
package com.boredream.demo.tedispatch;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.ListView;
public class MyListView extends ListView {
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
MainActivity:
package com.boredream.demo.tedispatch;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class TouchEventDispatchDemoActivity extends Activity {
private ListView lv_mid;
private MyAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv_mid = (ListView) findViewById(R.id.lv_mid);
adapter = new MyAdapter();
lv_mid.setAdapter(adapter);
}
private class MyAdapter extends BaseAdapter {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = View.inflate(TouchEventDispatchDemoActivity.this, R.layout.listview_item, null);
TextView tv_bottom = (TextView) view.findViewById(R.id.tv_bottom);
tv_bottom.setText("bottom item textview " + (position + 1));
return view;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public int getCount() {
return 20;
}
};
}