多层view触摸传递分析

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;
		}
	};
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值