當我們做項目時經常會遇到整體界面需要滑動,此頁面中又有ListView的 情況。比如說:某法院系統的提交訴訟材料界面,這種界面要提交很多元素,其中想證人可能有多個,就會用到ListView。但這種ScrollView裏 包含ListView的情況下,ListView是無法滾動的。很多人都知道這個現象,但爲什麼會出現這個問題呢?這就要追溯到我們今天要講的 android View的事件處理機制。
首先我們先看這個問題爲什麼會產生呢?我們調查ScrollView的代碼會發現,如下
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
return true;
}
}
這個方法是touch事件的攔截方法,當用戶拖動屏幕,就會被這個方法攔截下來,返回true,所以ListView的滾動就失效了。這裏又扯出onInterceptTouchEvent方法,這個方法又是什麼呢?
View整個體系提供了3個對事件操作:
- dispatchTouchEvent。事件分發,主要是體系的上層向下層,層級形式分發事件的機制。
- onInterceptTouchEvent。事件攔截,主要是攔截事件,並阻止其進一步傳遞到體系下層的機制。
- onTouchEvent。事件處理,主要是事件接收並做出處理,並向體系上層傳遞處理結果的機制。
通過這三種操作,對View體系中的事件作出管理。那我們現在已經知道了問題出現的原因:是因爲用戶的拖動事件被ScrollView的onInterceptTouchEvent方法攔截住了,那事件又是怎麼傳遞過來的呢?
當手機屏幕被用戶觸摸,就會形成一套事件序列,每一套事件序列都有三種事件動作(down,move,up)組合完成。 這套序列會被傳遞到系統framework層,再傳入Activity,然後Activity會把事件傳給Window下的DecorView進行分發, 分發給每個View。而某個View的onTouchEvent處理了這個事件,並返回true,就說明此事件已傳遞完畢。
說到返回值,這三個方法的返回值含義如下:
- dispatchTouchEvent
true:事件被以該節點爲根節點的View樹成功處理,此時該事件就算是處理完成了,事件不會再向上返還給View的父節點。
false:以該節點爲根節點的View樹中,沒有一個View成功處理了此事件,所以事件會向上返還給View的父節點。 onInterceptTouchEvent
true:當前ViewGroup希望該事件不再傳遞給其下層,而是希望自己處理。
false:當前ViewGroup不準備攔截該事件,事件將正常向下分發給其下層。onTouchEvent
true:表示該View成功處理了該事件,該處理結果會向上通知給其上層。
false:表示該View沒有成功處理該事件。
到此,瞭解了這麼多再回顧一下開始的問題,原因是事件被ScrollView攔截到了,那解決方法有很多,最直接的一種就是當我們滑動ListView時不讓ScrollView攔截到。那要怎麼做呢?
通知其上層不要攔截此事件。代碼如下:
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
getParent().requestDisallowInterceptTouchEvent(false);
break;
default:
break;
}
return true;
}
以上就是從ListView與ScrollView的衝突調查android view的事件處理機制,希望對大家有所幫助。