歡迎指教!!!
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