最近在做东西的时候发现自己对事件的分发(传递)机制理解的并不清楚,可以说是错误的理解了事件分发机制。对此,自己找了一些实例的例子,来加深对Android的时间分发机制的理解。废话不多说,直入主题。
下图是这个案例的实现界面:
对于上面实现的xml代码很简单,下图注明其容器和控件的结构:
如果上面的示意图表达的不清楚,请看以下代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context="com.test.youkumenu.MainActivity">
<RelativeLayout
android:id="@+id/rl_level1"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/level1">
<ImageButton
android:id="@+id/ib_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@null"
android:src="@drawable/icon_home" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_level2"
android:layout_width="180dp"
android:layout_height="90dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/level2">
<ImageButton
android:id="@+id/ib_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:background="@null"
android:src="@drawable/icon_menu" />
<ImageButton
android:id="@+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:background="@android:color/transparent"
android:src="@drawable/icon_search" />
<ImageButton
android:id="@+id/question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="5dp"
android:background="@android:color/transparent"
android:src="@drawable/icon_myyouku" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_level3"
android:layout_width="280dp"
android:layout_height="140dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/level3">
<ImageButton
android:id="@+id/house"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:background="@null"
android:src="@drawable/channel4" />
<ImageButton
android:id="@+id/music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="11dp"
android:layout_marginStart="14dp"
android:background="@null"
android:src="@drawable/channel1" />
<ImageButton
android:id="@+id/microphone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/music"
android:layout_marginEnd="12dp"
android:background="@null"
android:src="@drawable/channel7" />
<ImageButton
android:id="@+id/media"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/tv_show"
android:layout_toEndOf="@+id/music"
android:background="@null"
android:src="@drawable/channel2" />
<ImageButton
android:id="@+id/tv_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/microphone"
android:layout_marginBottom="14dp"
android:layout_toStartOf="@+id/microphone"
android:background="@null"
android:src="@drawable/channel5" />
<ImageButton
android:id="@+id/editing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/monkey"
android:layout_marginEnd="15dp"
android:layout_toStartOf="@+id/tv_show"
android:background="@null"
android:src="@drawable/channel6" />
<ImageButton
android:id="@+id/monkey"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/house"
android:layout_marginStart="11dp"
android:layout_toEndOf="@+id/media"
android:background="@null"
android:src="@drawable/channel3" />
</RelativeLayout>
</RelativeLayout>
上面的布局结构是为了实现优酷菜单的动画效果,但是本次不是针对动画,所以就不做实现的说明了。
对上图而言,结构上level2部分覆盖了level1,level3部分覆盖了level2,对于level3而言没有谁覆盖它,所以它的事件传递序列很简单,
即:根布局容器RelativeLayout—>level3的RelativeLayout容器—>level3上上面的原子View(不能拆分的控件,最基本结构的控件)
那么,如果要设置level2和level1容器(RelativeLayout)
的子控件的点击事件,怎么办呢?
直接添加View.OnClickListener接口,使用setOnClickListener设置监听?
运行结果就不截图了,每次的点击事件都直接被level3容器视图消费了,只有level3的OnClick事件打印出了log,为什么呢?那就要分析下Android事件分发机制的原理了。借助本次的实例,做以下分析,如图:
相信看了上面这张图,应该很容易理解为什么点击事件会被level3拦截,而不继续向下层传递了,对于每一个level,其内部的事件传递跟上面一样,只是原子控件没有onInterceptTouchEvent方法,因为他是最基本的控件,所以上面让level1,level2响应level3传递下来的事件就很容易解决了.
想让level2及其控件响应事件,就让level3的onTouch返回false,不消费事件,让其向level2传递,一次类推,让level1及其控件响应事件,则然level2的onTouch返回false,不消费事件即可,下面看实际代码。
private void initView() {
//事件拦截处理
//rl_level3需要把事件传递给rl_level2和rl_level1
rl_level1 = (RelativeLayout) findViewById(R.id.rl_level1);
rl_level1.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
/*
rl_level1.onInterceptTouchEvent(MotionEvent ev); 拦截TouchEvent
rl_level1.dispatchTouchEvent(); 分发TouchEvent
rl_level1.onTouchEvent() 触摸事件
*/
rl_level2 = (RelativeLayout) findViewById(R.id.rl_level2);
rl_level2.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
rl_level3 = (RelativeLayout) findViewById(R.id.rl_level3);
//让上层的RelativeLayout不消费
rl_level3.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
ib_home = (ImageButton) findViewById(R.id.ib_home);
ib_home.setOnClickListener(this);
ib_menu = (ImageButton) findViewById(R.id.ib_menu);
ib_menu.setOnClickListener(this);
findViewById(R.id.search).setOnClickListener(this);//搜索
findViewById(R.id.question).setOnClickListener(this);//问题
findViewById(R.id.media).setOnClickListener(this);//多媒体
findViewById(R.id.editing).setOnClickListener(this);//剪辑
findViewById(R.id.house).setOnClickListener(this);//
findViewById(R.id.microphone).setOnClickListener(this);
findViewById(R.id.music).setOnClickListener(this);
findViewById(R.id.monkey).setOnClickListener(this);
findViewById(R.id.tv_show).setOnClickListener(this);
}
运行结果:
可以看出,所有的图标按钮都响应了对应的事件。