關於解決多個viewpager嵌套所遇到的事件傳遞問題

    在開發時候我們會有一種情景,類似於新聞客戶端的,就是在主頁面的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事件已被處理,不繼續進行傳遞。

關於事件傳遞自己有以下總結


1. 事件首先傳遞給父控件-->父控件最新接收到事件,`擁有事件處理的最高優先級`;
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

ScrollableViewGroup以及ViewHook事件傳遞流程分析
 ---ScrollableViewGroup(父親)----onInterceptTouchEvent---MotionEvent.ACTION_DOWN--- return false
---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嵌套的事件傳遞的解決吧。


發佈了17 篇原創文章 · 獲贊 15 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章