參考的文章
感謝這些作者的分享
http://www.reibang.com/p/24038d957e93
https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout
https://wangyeming.github.io/2017/07/16/use-webview-in-viewpager/
在使用viewpager時(shí)棺滞,如果某一頁存在webview,則會出現(xiàn)webview中輪播圖或者其他滑動控件,無法滑動的問題。這種情況是可以僅通過app端就解決的亮元。
解決問題的思路
1担平、如何控制使用webview處理事件還是viewpager處理事件。
2戈轿、根據(jù)什么來判斷webview處理事件還是viewpager處理事件症副。
問題一:如何控制
所有的滑動沖突問題解決的思路就是兩個(gè):
- 內(nèi)部攔截法
- 外部攔截法
在這個(gè)場景中店雅,在webview外面,可能還包了fragment等眾多viewgroup贞铣,并且最終判斷誰處理事件的依據(jù)在webview中闹啦,所以這里使用內(nèi)部攔截法更方便。
public class ScrollConflictWebView extends WebView {
private boolean 判斷依據(jù) = false;
public ScrollConflictWebView(Context context) {
super(context);
}
public ScrollConflictWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
判斷依據(jù) = false;
//Down時(shí)攔截事件保證在move時(shí)可以拿到事件辕坝。
requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
//Move時(shí)決定是否還要攔截事件
requestDisallowInterceptTouchEvent(!判斷依據(jù));
break;
default:
requestDisallowInterceptTouchEvent(false);
break;
}
return super.onTouchEvent(event);
}
}
可能你在測試時(shí)窍奋,會出現(xiàn)無法攔截的情況,我也遇到了酱畅,因?yàn)槭褂昧薙wipeRefreshLayout琳袄。下文會說到。
問題2:如何判斷
我找到了兩種思路纺酸,第一種
- 通過js交互實(shí)現(xiàn)窖逗,h5告訴客戶端什么情況下是可以滑動的,或者什么位置是可以滑動的餐蔬。
- 通過webview自身的回調(diào):onOverScrolled來判斷碎紊。參考了這個(gè)文章
顯然,第一種方法是非常麻煩的樊诺,涉及到j(luò)s交互仗考,不具備通用性。因?yàn)闇y試了uc和夸克词爬,發(fā)現(xiàn)在他們的瀏覽器中秃嗜,都自動解決了滑動沖突,所以必然是有其他可以判斷的依據(jù)的缸夹,最終找到了第二種方法痪寻。
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
isScrollX = clampedX;
}
這個(gè)方法觸發(fā)的時(shí)機(jī)是webview滑動到邊界時(shí)會觸發(fā)螺句,如果是橫向滑動虽惭,則clamped則為true。這樣的話蛇尚,我們只要在clamped為true的時(shí)候芽唇,把事件交給viewpager來處理就行了。
public class ScrollConflictWebView extends WebView {
private boolean 判斷依據(jù) = false;
public ScrollConflictWebView(Context context) {
super(context);
}
public ScrollConflictWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
判斷依據(jù) = false;
//Down時(shí)攔截事件保證在move時(shí)可以拿到事件。
requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
//Move時(shí)決定是否還要攔截事件
requestDisallowInterceptTouchEvent(!判斷依據(jù));
break;
default:
requestDisallowInterceptTouchEvent(false);
break;
}
return super.onTouchEvent(event);
}
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
判斷依據(jù) = clampedX;
}
}
坑
上面說到了匆笤,如果你Webview的父布局中存在SwipeRefreshLayout研侣,會發(fā)現(xiàn),可能disallow無法傳遞到viewpager炮捧。理論上庶诡,不做任何處理,viewgroup的disallow方法咆课,會挨個(gè)往父布局傳遞末誓。但是為什么會傳遞失敗呢?這就得看下SwipeRefreshLayout的源碼(appcompat版本:1.2.0-alpha03)了:
@Override
public void requestDisallowInterceptTouchEvent(boolean b) {
// if this is a List < L or another view that doesn't support nested
// scrolling, ignore this request so that the vertical scroll event
// isn't stolen
if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
|| (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
// Nope.
} else {
super.requestDisallowInterceptTouchEvent(b);
}
}
從源碼可以看出书蚪,如果SwipeRefreshLayout包裹的布局不支持NestedScroll的話喇澡,就不做任何處理。
其實(shí)這個(gè)是appcompat在1.2.0中才修改的殊校,1.1.0版本是給了方法自己控制的:
一開始的思路是讓SwipeRefreshLayout下面一層View支持nestedScroll晴玖,但是這樣的話,會導(dǎo)致下拉刷新無法觸發(fā)为流。
那就只能手動去修改disallow方法了:
public class AllowSwipeRefreshLayout extends QFSwipeRefreshLayout {
public AllowSwipeRefreshLayout(@NonNull @NotNull Context context) {
super(context, null);
}
public AllowSwipeRefreshLayout(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public void requestDisallowInterceptTouchEvent(boolean b) {
getParent().requestDisallowInterceptTouchEvent(b);
super.requestDisallowInterceptTouchEvent(b);
}
}
這樣修改以后呕屎,disallow就可以正常傳遞給viewpager了。到這里敬察,就完美的解決了webview嵌套在Viewpager中的滑動沖突問題榨惰。
最終實(shí)現(xiàn)效果與uc和夸克一致。