首页 > 技术文章 > Android 快速理解事件分发

gmm283029 2015-01-29 00:28 原文

什么是事件分发?

控件之间有嵌套关系,一般简单的控件点击和触摸不需要考虑事件分发问题,如果需要处理复杂的触摸、点击、滑动等逻辑的时候就需要对事件进行拦截和分发处理。

事件分发是用来解决控件嵌套在一起的时候需要去判断你点击的是父控件呢还是子控件,对事件由上到下从外到里的传递。

涉及的方法有:

dispatchTouchEvent(MotionEvent ev)
onInterceptTouchEvent(MotionEvent ev)
onTouch(MotionEvent ev)

控件接收到点击之后先执行dispatchTouchEvent(MotionEvent ev)

该方法的意思是是不是要对事件进行向下分发,源码有一对逻辑,大概就是你点击的时候的位置有没有子控件,有的话就向下分发,没有就拦截不发了

当然还可以通过重写onInterceptTouchEvent(MotionEvent ev)来决定要不要分发


如下是一个例子:重写一个LinearLayout 并且重写onInterceptTouchEvent(MotionEvent ev),该方法默认返回false

public class TestLayout extends LinearLayout {

	@SuppressLint("NewApi")
	public TestLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public TestLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public TestLayout(Context context) {
		super(context);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		return false;
	}
	

}

把LinearLayout作为布局文件添加给Activity,添加两个Button测试用

<?xml version="1.0" encoding="utf-8"?>
<com.example.abc.TestLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/Button01" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/Button02" />

</com.example.abc.TestLayout>
java
public class MainActivity extends ActionBarActivity {
	private View mView;
	private Button mButton1, mButton2;

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		mView = LayoutInflater.from(this).inflate(R.layout.main, null);
		setContentView(mView);
		initView();
	}

	private void initView() {
		mButton1 = (Button) findViewById(R.id.button1);
		mButton2 = (Button) findViewById(R.id.button2);
		mButton1.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				Toast.makeText(getBaseContext(), "button1", Toast.LENGTH_LONG)
						.show();
			}
		});
		mButton2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				Toast.makeText(getBaseContext(), "button2", Toast.LENGTH_LONG)
						.show();
			}
		});
		mView.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View arg0, MotionEvent arg1) {
				Toast.makeText(getBaseContext(), "layout", Toast.LENGTH_LONG)
						.show();
				return false;
			}
		});

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

}
结论:点击按钮时响应,点击布局空白处也响应,事件由LinearLayout分发给Button处理

测试:如果把onIntercentTouchEvent(MotionEvet ev)  返回true

public boolean onInterceptTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		return false;
	}
结论:点击按钮时无效,点击空白有效,说明了触摸事件被LinearLayout拦截掉了。



推荐阅读