在開發時候我們會有一種情景,類似於新聞客戶端的,就是在主頁面的viewpager裏面的view或者frament中再嵌套了一個viewpager,這時候如果不做任何處理的話,我們父view中的viewpager和子view的viewpager會產生衝突,比如滑動的卡頓等。
所以爲了解決這個情況我們需要了解一下安卓中的事件傳遞機制。如果有對安卓的事件傳遞機制的人不那麼熟悉的話,那請自己去研究一下,在這裏我只簡單說一下時間傳遞機制的基本原理。
說明:
首先觸摸事件發生時(ACTION_DOWN),由系統調用Activity的dispatchTouchEvent方法,分發該事件。根據觸摸事件的座標,將此事件傳遞給out的dispatchTouchEvent處理,out則調用onInterceptTouchEvent 判斷事件是由自己處理,還是繼續分發給子View。此處由於out不處理Touch事件,故根據事件發生座標,將事件傳遞給out的直接子View(即middle)。
Middle及Center中事件處理過程同上。但是由於Center組件是clickable 表示其能處理Touch事件,故center中的onInterceptTouchEvent方法將事件傳遞給center自己的onTouchEvent方法處理。至此,此Touch事件已被處理,不繼續進行傳遞。
關於事件傳遞自己有以下總結
2. 關鍵方法
1. dispatchTouchEvent:是否派發,常見處理-->不做任何的處理.
2. onInterceptTouchEvent:是否攔截
1. 是(true)-->調用自己的onTouchEvent去處理事件,`如果你對MotionEvent進行了拆分(action_down,action_move,action_up,action_cancel),這個時候傳遞流程不能說的那麼絕對`
2. 否(false)-->事件傳遞給孩子
1. 孩子是view-->ontouchEvent
2. 孩子是viewGroup-->和之前的情況一樣
3. onTouchEvent:是否消費
1. return true:消費事件
2. return false:不消費事件
以下是在自己寫的程序中打印的一些測試log
---viewGroudHook---onTouchEvent---ACTION_DOWN return true
----ScrollableViewGroup(父親)---onInterceptTouchEvent---MotionEvent.ACTION_MOVE--- return true,onInterceptTouchEvent返回true的時候.肯定回來自己的onTouchevent裏面,但是可能會去孩子那裏執行以下action_cancel
---viewGroudHook---onTouchEvent---ACTION_CANCEL--->這個地方很容易產生疑惑
---ScrollableViewGroup(父親)---onTouchEvent-----MotionEvent.ACTION_MOVE------- 父親在onInterceptTouchEvent裏面的action_move分支下return true,所以我們進入的是ontouchEvent的Action_move
---ScrollableViewGroup(父親)---onTouchEvent-----MotionEvent.ACTION_MOVE------- 這裏我們用打斷點的形式.沒有看到action_move.所以我們常用打日誌的形式進行了分析
---ScrollableViewGroup(父親)---onTouchEvent-----MotionEvent.ACTION_MOVE-------
---ScrollableViewGroup(父親)---onTouchEvent-----MotionEvent.ACTION_UP-------
以上就是個人對安卓的事件傳遞機制的一些小總結,可能也借鑑了一下別人的圖,請見諒
大家看了上面事件傳遞機制,應該也基本有所瞭解,所以我就直接貼源碼好了。
這裏我重寫了viewpager,實現的邏輯是當滑動到子view的viewpager的頭或者尾的時候就請求父控件進行事件攔截,然後實現父view的viewpager的滑動事件。但是我看了網易新聞的客戶端,貌似頭條新聞並不是到尾它就切換到父viewpaer的下一個view,所以大家根據自己的需求改一下以下的代碼吧
/**
* 頭條新聞水平滑動的ViewPager
* @author Administrator
*
*/
public class TopNewsViewPager extends ViewPager{
private int startX;
private int startY;
public TopNewsViewPager(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public TopNewsViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
/**
* 事件分發,請求父控件及祖宗控件是否攔截事件
* 1.用戶右滑,而且是第一個頁面,就需要父控件攔截,顯示上一個標籤或者側邊欄
* 2.用戶左滑,而且是最後一個頁面,就需要父控件攔截,顯示下一個標籤
* 3.上下滑動,需要父控件攔截
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// //用getParent去請求,希望父控件不攔截這個OnToch事件
// getParent().requestDisallowInterceptTouchEvent(true);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//用getParent去請求,希望父控件不攔截這個OnToch事件
//這樣爲了保證ACTION_MOVE調用
getParent().requestDisallowInterceptTouchEvent(true);
startX = (int) ev.getRawX();
startY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int endX=(int) ev.getRawX();
int endY=(int) ev.getRawY();
if(Math.abs(endX-startX)>Math.abs(endY-startY)){//左右滑動
if(endX>startX){//右滑
if(getCurrentItem()==0){//第一個頁面,需要父控件攔截
getParent().requestDisallowInterceptTouchEvent(false);
}
}else{//左滑
if(getCurrentItem()==getAdapter().getCount()-1){//最後一個頁面,需要攔截
getParent().requestDisallowInterceptTouchEvent(false);
}
}
}else{//上下滑動,需要父控件攔截
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
return super.dispatchTouchEvent(ev);
}
}
以上大概就是我對多個viewpager嵌套的事件傳遞的解決吧。