欢迎指教!!!
http://blog.csdn.net/hemeng2009/article/details/40076463
由于点击屏幕空白处隐藏软键盘中接触到了dispatchtouchevent,onInterceptTouchEvent,ontouchevent,故而对这方面知识进行了详细了解。
一、View与ViewGroup
所有的UI控件例如Button、TextView都是继承于View,而所有的布局控件、容器控件继承于ViewGroup。
二、事件
在Android中,事件主要包括点按、长按、拖拽、滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作。所有这些都构成了Android中的事件响应。总的来说,所有的事件都由如下三个部分作为基础:
· 按下(ACTION_DOWN)
· 移动(ACTION_MOVE)
· 抬起(ACTION_UP)
三、事件响应函数(回调函数)
View.java ViewGroup.java
public booleandispatchTouchEvent(MotionEvent event) public boolean dispatchTouchEvent(MotionEvent event)
public booleanonTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event) public boolean onInterceptTouchEvent(MotionEvent event)
我们可以看出ViewGroup多了一个onInterceptTouchEvent方法,返回值均为boolean。
返回值: 事件传递,传递的过程就是一个接一个,具体就是根据响应函数的返回值(true/false)判断事件是否继续传递下去。在Android中,所有的事件都是从开始经过传递到完成事件的消费,这些方法的返回值决定了事件------继续往下传/还是被拦截了/被消费了。
形 参:MotionEvent继承于InputEvent,用于标记各种动作事件。之前提到的ACTIONDOWN、ACTIONMOVE、ACTION_UP都是MotinEvent中定义的常量。我们通过MotionEvent传进来的事件类型来判断接收的是哪一种类型的事件。
功 能:
· dispatchTouchEvent方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。返回true表示不继续分发,事件被消费。返回false则继续往下分发,如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截该事件。
· onTouchEvent方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。
· onInterceptTouchEvent是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能还有子View,而在Android中View中是不能再包含子View的(iOS可以)。
四、应用
例子关注与不同组件的调用顺序,实际中根据需要选择。
view:
新建一个类RTButton继承Button,用来实现我们对按钮事件的跟踪。
RTButton.java
public classRTButton extends Button {
public RTButton(Context context,AttributeSet attrs) {
super(context, attrs);
}
//基于回调函数实现组件的方法重载(2个)
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
//下面用****************************代替switch内容
caseMotionEvent.ACTION_DOWN:
System.out.println("RTButton---onTouch---DOWN");
break;
caseMotionEvent.ACTION_MOVE:
System.out.println("RTButton---onTouch---MOVE");
break;
caseMotionEvent.ACTION_UP:
System.out.println("RTButton---onTouch---UP");
break;
default:
break;
}
returnsuper.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEventevent) {
switch (event.getAction()) {
******************************
}
return super.onTouchEvent(event);
}
}
activity_main.xml在根布局下放入自定义的按钮RTButton。
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/myLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.ryantang.eventdispatchdemo.RTButton
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"/>
</LinearLayout>
Activity:
在Activity中为RTButton设置onTouch和onClick的监听器来跟踪事件传递的过程,另外,Activity中也有一个dispatchTouchEvent方法和一个onTouchEvent方法,重写他们并输出打印信息。
MainActivity.java
public classMainActivity extends Activity {
private RTButton button;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button =(RTButton)this.findViewById(R.id.btn);
//完全可以做一个layout的监听,对layout加入id,然后和button相同即可,参看
//http://blog.csdn.net/hemeng2009/article/details/40076463
//基于button监听实现事件响应
button.setOnTouchListener(newOnTouchListener() {
//onTouch方法
@Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
***************************
}
return false;
}
});
//click事件监听
button.setOnClickListener(newOnClickListener() {
@Override
public voidonClick(View v) {
System.out.println("RTButtonclicked!");
}
});
}
//activity中的俩个方法----适用于全屏操作
@Override
publicboolean dispatchTouchEvent(MotionEvent event) {
switch(event.getAction()) {
*************************************
}
returnsuper.dispatchTouchEvent(event);
}
@Override
publicboolean onTouchEvent(MotionEvent event) {
switch(event.getAction()) {
************************************
}
returnsuper.onTouchEvent(event);
}
}
点击按钮--结果:
首先执行了Activity的dispatchTouchEvent方法进行事件分发,dispatchTouchEvent方法的返回值是super.dispatchTouchEvent(event),因此调用了父类方法,我们进入Activity.java的源码中看看具体实现。
dispatchTouchEvent方法开始只处理了ACTIONDOWN事件,因为一个事件以down为开始。
//
Android事件拦截
Android嵌套布局事件传递
嵌套布局:RTLayout(ViewGroup)继承于LinearLayout,重写dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent方法。
那么,事件是先传递到View呢,还是先传递到ViewGroup的?
RTLayout.java
public class RTLayout extends LinearLayout {
public RTLayout(Context context,AttributeSet attrs) {
super(context, attrs);
}
@Override
public booleandispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
********************************************
}
returnsuper.dispatchTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
*********************************************
}
returnsuper.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEventevent) {
switch (event.getAction()) {
************************************************
}
returnsuper.onTouchEvent(event);
}
}
同时,在布局文件中为RTButton添加一个父布局,指明为自定义的RTLayout,修改后的布局文件如下。
activity_main.xml
<LinearLayoutxmlns: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" >
//嵌套布局引入
<com.ryantang.eventdispatchdemo.RTLayout
android:id="@+id/myLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.ryantang.eventdispatchdemo.RTButton
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"/>
</com.ryantang.eventdispatchdemo.RTLayout>
</LinearLayout>
最后,我们在Activity中也为RTLayout设置onTouch和onClick事件,在MainActivity中添加如下代码。
MainActivity.java
//采用的事件监听的方法
//同样可以为主layout监听。
rtLayout.setOnTouchListener(newOnTouchListener() {
@Override
public booleanonTouch(View v, MotionEvent event) {
switch(event.getAction()) {
***************************************
}
return false;
}
});
rtLayout.setOnClickListener(newOnClickListener() {
@Override
public voidonClick(View v) {
System.out.println("RTLayoutclicked!");
}
});
代码修改完毕后,编译运行工程,同样,点击按钮,查看日志输出结果如下:
从日志输出结果我们可以看到,嵌套了RTLayout以后,事件传递的顺序变成了Activity->RTLayout->RTButton,这也就回答了前面提出的问题,Android中事件传递是从ViewGroup传递到View的,而不是反过来传递的。
从输出结果第三行可以看到,执行了RTLayout的onInterceptTouchEvent方法,该方法的作用就是判断是否需要拦截事件,我们到ViewGroup的源码中看看该方法的实现。
ViewGroup.java
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
该方法的实现很简单,只返回了一个false。如果onInterceptTouchEvent返回false则不拦截,如果返回true则拦截当前事件。
总结
现总结如下:
根据上面的内容,我们知道,点击屏幕空白处,隐藏键盘,实现对布局文件的监听即可。
· Android中事件传递按照从上到下进行层级传递,事件处理从Activity开始到ViewGroup再到View。
· 事件传递方法包括dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent,其中前两个是View和ViewGroup都有的,最后一个是只有ViewGroup才有的方法。这三个方法的作用分别是负责事件分发、事件处理、事件拦截。
· onTouch事件要先于onClick事件执行,onTouch在事件分发方法dispatchTouchEvent中调用,而onClick在事件处理方法onTouchEvent中被调用,onTouchEvent要后于dispatchTouchEvent方法的调用。
· ps:http://www.infoq.com/cn/articles/android-event-delivery-mechanism