Android滑動(dòng)事件沖突的解決方法
滑動(dòng)是Android中不可缺少的一部分,多個(gè)滑動(dòng)必然會(huì)產(chǎn)生沖突,比如我們最常見(jiàn)的是ScrollView中嵌套了ListView,一般做法是計(jì)算出ListView的總高度,這樣就不用去滑動(dòng)ListView了。又比如一個(gè)ViewPager嵌套Fragment,F(xiàn)ragment里面又有ListView,這原本是有滑動(dòng)沖突的,但是ViewPager內(nèi)部去幫我們解決了這種沖突。那如果我們要自己解決沖突又該怎么辦呢。
下面有兩種方式來(lái)解決:
外部攔截法
外部攔截法是指在有點(diǎn)擊事件時(shí)都要經(jīng)過(guò)父容器,那么在父容器時(shí)如果需要攔截就攔截自己處理,不需要?jiǎng)t傳遞給下一層進(jìn)行處理,下面看個(gè)例子:
首先定義一個(gè)水平滑動(dòng)的HorizontalScrollViewEx,看主要代碼
主要的攔截是需要重寫(xiě)onInterceptTouchEvent
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercepted = false; int x = (int) ev.getX(); int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: //down事件不攔截,否則無(wú)法傳給子元素 intercepted = false; if (!mScroller.isFinished()) { mScroller.abortAnimation(); intercepted = true; } break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; //水平滑動(dòng)則攔截 if (Math.abs(deltaX) > Math.abs(deltaY) + 5) { intercepted = true; } else { intercepted = false; } break; case MotionEvent.ACTION_UP: //不攔截,否則子元素?zé)o法收到 intercepted = false; break; } //因?yàn)楫?dāng)ViewGroup中的子View可能消耗了down事件,在onTouchEvent無(wú)法獲取, // 無(wú)法對(duì)mLastX賦初值,所以在這里賦值一次 mLastX = x; mLastY = y; mLastYIntercept = y; mLastXIntercept = x; return intercepted; }
在down事件不需要攔截,返回false,否則的話子view無(wú)法收到事件,將全部會(huì)由父容器處理,這不是希望的;up事件也要返回false,否則最后子view收不到。
看看move事件,當(dāng)水平滑動(dòng)距離大于豎直距離時(shí),代表水平滑動(dòng),返回true,由父類來(lái)進(jìn)行處理,否則交由子view處理。這里move事件就是主要的攔截條件判斷,如果你遇到的不是水平和豎直的條件這么簡(jiǎn)單,就可以在這里進(jìn)行改變,比如,ScrollView嵌套了ListView,條件就變成,當(dāng)ListView滑動(dòng)到底部或頂部時(shí),返回true,交由父類滑動(dòng)處理,否則自身ListView滑動(dòng)。
在onTouchEvent中主要是做的滑動(dòng)切換的處理
@Override public boolean onTouchEvent(MotionEvent event) { mVelocityTracker.addMovement(event); int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (!mScroller.isFinished()) { mScroller.abortAnimation(); } break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastX; int deltaY = y - mLastY; if (getScrollX() < 0) { scrollTo(0, 0); } scrollBy(-deltaX, 0); break; case MotionEvent.ACTION_UP: int scrollX = getScrollX(); mVelocityTracker.computeCurrentVelocity(1000); float xVelocityTracker = mVelocityTracker.getXVelocity(); if (Math.abs(xVelocityTracker) > 50) {//速度大于50則滑動(dòng)到下一個(gè) mChildIndex = xVelocityTracker > 0 ? mChildIndex - 1 : mChildIndex + 1; } else { mChildIndex = (scrollX + mChildWith / 2) / mChildWith; } mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1)); int dx = mChildIndex * mChildWith - scrollX; smoothScrollBy(dx, 0); mVelocityTracker.clear(); break; } mLastY = y; mLastX = x; return true; }
在這個(gè)嵌套一個(gè)普通的ListView,這樣就可以解決水平和豎直滑動(dòng)沖突的問(wèn)題了。
<com.example.lzy.customview.HorizontalScrollViewEx android:layout_width="match_parent" android:layout_height="200dp"> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_bright" android:text="2" /> <Button android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_green_dark" android:text="3" /> </com.example.lzy.customview.HorizontalScrollViewEx>
其他的部分代碼如果需要可以下載源碼來(lái)看
內(nèi)部攔截法
內(nèi)部攔截法是父容器不攔截任何事件,所有事件都傳遞給子view,如果需要就直接消耗掉,不需要再傳給父容器處理
下面重寫(xiě)一個(gè)ListView,只需要重寫(xiě)一個(gè)dispatchTouchEvent方法就OK
public class ListViewEx extends ListView { private static final String TAG = "lzy"; private int mLastX; private int mLastY; public ListViewEx(Context context) { super(context); } public ListViewEx(Context context, AttributeSet attrs) { super(context, attrs); } public ListViewEx(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { int x = (int) ev.getX(); int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: //子View的所有父ViewGroup都會(huì)跳過(guò)onInterceptTouchEvent的回調(diào) getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastX; int deltaY = y - mLastY; if (Math.abs(deltaX) > Math.abs(deltaY) + 5) {//水平滑動(dòng),使得父類可以執(zhí)行onInterceptTouchEvent getParent().requestDisallowInterceptTouchEvent(false); } break; } mLastX = x; mLastY = y; return super.dispatchTouchEvent(ev); } }
在down事件調(diào)用getParent().requestDisallowInterceptTouchEvent(true),這句代碼的意思是使這個(gè)view的父容器都會(huì)跳過(guò)onInterceptTouchEvent,在move中判斷如果是水平滑動(dòng)就由父容器去處理,父容器只需要把之前的onInterceptTouchEvent改為下面那樣,其他不變。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { int x = (int) ev.getX(); int y = (int) ev.getY(); if (ev.getAction() == MotionEvent.ACTION_DOWN) { mLastX = x; mLastY = y; if (!mScroller.isFinished()) { mScroller.abortAnimation(); return true; } return false; } else { //如果是非down事件,說(shuō)明子View并沒(méi)有攔截父類的onInterceptTouchEvent //說(shuō)明該事件交由父類處理,所以不需要再傳遞給子類,返回true return true; } }
最終實(shí)現(xiàn)效果就是下面那樣,兩個(gè)是用兩種方式實(shí)現(xiàn)的,上面的圓圈是一個(gè)簡(jiǎn)單的自定義View練習(xí)
下載地址:Android滑動(dòng)事件沖突
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android開(kāi)發(fā)之a(chǎn)ctiviti節(jié)點(diǎn)跳轉(zhuǎn)
這篇文章主要介紹了Android開(kāi)發(fā)之a(chǎn)ctiviti節(jié)點(diǎn)跳轉(zhuǎn)的相關(guān)資料,需要的朋友可以參考下2016-04-04Android中如何指定SnackBar在屏幕的位置及小問(wèn)題解決
這篇文章主要給大家介紹了關(guān)于Android中如何指定SnackBar在屏幕的位置,以及一個(gè)小問(wèn)題解決的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03Android實(shí)現(xiàn)原生鎖屏頁(yè)面音樂(lè)控制
這篇文章主要介紹了Android實(shí)現(xiàn)原生鎖屏頁(yè)面音樂(lè)控制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12android基礎(chǔ)總結(jié)篇之一:Activity生命周期
本篇文章主要介紹了android基礎(chǔ)總結(jié)篇之一:Activity生命周期,想要學(xué)習(xí)的可以了解一下。2016-11-11Android入門(mén)之源碼開(kāi)發(fā)基礎(chǔ)教程
這篇文章主要介紹了Android入門(mén)之源碼開(kāi)發(fā)基礎(chǔ)教程,分析了環(huán)境搭建、模擬器使用及編譯文件的相關(guān)技巧與注意事項(xiàng),需要的朋友可以參考下2016-02-02AndroidStudio升級(jí)4.1后啟動(dòng)失敗Plugin問(wèn)題解決
這篇文章主要介紹了AndroidStudio升級(jí)4.1后啟動(dòng)失敗Plugin問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10淺析Android位置權(quán)限以及數(shù)組尋找索引的坑
這篇文章給大家分享了Android位置權(quán)限以及數(shù)組尋找索引的坑的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友可以參考學(xué)習(xí)下。2018-07-07