問題:
對android的事件機制一直不怎么了解,最近android項目中運用viewpager+listview (就是viewpager的子view中嵌套了listview),出現了觸摸手勢沖突
吐槽:
問題一來很是捉急,於是執行傻瓜式問題解決,各種谷歌大神求支援,按照網上的解決方案,也不管對不對應我的情況,一頓亂搞....
顯然無數次失敗之后還是沒給我足夠的教訓,這次結果依然是沒找到現成的,(;一_一) ...
機制:
糾結了一天,決定好好理一理android的事件機制,找到下面這3張圖(原地址我沒找到,已經在在谷歌大神那留下N個副本..實在沒找到原作者,如有知道原創地址請與我聯系),覺得豁然開朗
圖1 圖2 圖3
onInterceptTouchEvent 和 onTouchEvent 清楚了,還有個 dispatchTouchEvent 不太清楚,網上說是分發事件的,在調試時,我發現 viewgroup 的分發機制似乎不同(dispatchTouchEvent在onInterceptTouchEvent后調用)。
最終找到這篇文章 "Android事件處理第一節(View對Touch事件的處理)",看到其中 View.dispatchTouchEvent() 的源碼,又是豁然開朗.
public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true; } if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; }
也就是說是 dispatchTouchEvent 在調用 onTouchEvent 。
思路:
最終效果是豎直方向滑動時listview動起來,水平方向滑動viewpager動起來,顯然我需要對滑動手勢進行判斷,
(1) 在 case MotionEvent.ACTION_DOWN: 分支獲取第一次按下點的坐標 (2) 在 case MotionEvent.ACTION_MOVE: 分支獲取移動結束點的坐標 (3) 通過兩點橫坐標差值與豎坐標的差值可以判斷手勢方向;
這里我的view層次是,activity->viewpager->listview.
所以我希望它的事件流向像下圖一樣
思路差不多就這些.
解決:
直接上代碼吧.
/* 代碼位置:最頂層的VIEW,(viewpager的子view) */
private float xDistance, yDistance, xLast, yLast; /* (non-Javadoc) * @see android.widget.AbsListView#onTouchEvent(android.view.MotionEvent) */ @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: //獲取第一次按下點的坐標 xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; case MotionEvent.ACTION_MOVE: //獲取移動結束點的坐標 final float curX = ev.getX(); final float curY = ev.getY();
//差值 xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; if(xDistance <= yDistance) return super.onTouchEvent(ev); // 消耗 else return false; // 往下傳遞 } return super.onTouchEvent(ev); }
如果你的項目,viewpager之下還有 view ,並且還有手勢處理,那你可以對你想屏蔽的 view 使用 requestDisallowInterceptTouchEvent(true); ,比如說我這的Activity還有手勢處理,我想屏蔽它,於是就在 viewpager 的 onInterceptTouchEvent 里調用了 getParent().requestDisallowInterceptTouchEvent(true); ,代碼如下
@Override public boolean onInterceptTouchEvent(MotionEvent arg0) { getParent().requestDisallowInterceptTouchEvent(true); return super.onInterceptTouchEvent(arg0); }
問題解決,暫時就這些了.