问题
如果WebView在ViewPager2中。如果网页中有banner之类的可以横向滚动的控件,会和ViewPager2的横向滚动发生冲突。需要处理。
原理
在处理横向滑动冲突的时候。
原理很简单。
拦截webview的点击事件
在dispatchTouchEvent的时候调用view.getParent().requestDisallowInterceptTouchEvent(true);不允许父类拦截事件。
而在webview滚动到边缘的时候会触发以下事件。在以下事件中可以运行父类拦截。
@Override
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY, View view)
X5内核
X5的webview是一个FrameLayout。需要通过特殊的方式来拦截其内部的WebView的相关事件。
可以参考官网的文档。
实现
已经将相关的功能实现了,直接copy代码就行了
TRSNestedScrollWebView
package com.trs.nmip.common.util.web;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.core.view.NestedScrollingChild;
import com.tencent.smtt.sdk.WebView;
/**
* <pre>
* Created by zhuguohui on 2019/10/25.
* 这个类的作用是把WebView的纵向滚动事件专递给上传
* 便于AppBarLayout等实现动画。
* </pre>
*/
public class TRSNestedScrollWebView extends WebView implements NestedScrollingChild {
public static final String TAG = "NestedScrollWebView";
private boolean haveClick = false;
private X5WebViewScrollUtil scrollUtil;
public boolean isUserClicked() {
return haveClick;
}
public TRSNestedScrollWebView(Context context) {
super(context);
this.init();
}
public TRSNestedScrollWebView(Context context, AttributeSet attrs) {
super(context, attrs);
this.init();
}
public TRSNestedScrollWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.init();
}
private void init() {
//解决腾讯X5WebView和ViewPager存在的横向滚动冲突的问题
scrollUtil = new X5WebViewScrollUtil(this);
setNestedScrollingEnabled(true);
}
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
haveClick = true;
}
return super.dispatchTouchEvent(event);
}
public void setNestedScrollingEnabled(boolean enabled) {
this.scrollUtil.setNestedScrollingEnabled(enabled);
}
public boolean isNestedScrollingEnabled() {
return this.scrollUtil.isNestedScrollingEnabled();
}
public boolean startNestedScroll(int axes) {
return this.scrollUtil.startNestedScroll(axes);
}
public void stopNestedScroll() {
this.scrollUtil.stopNestedScroll();
}
public boolean hasNestedScrollingParent() {
return this.scrollUtil.hasNestedScrollingParent();
}
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
return this.scrollUtil.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
}
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return this.scrollUtil.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return this.scrollUtil.dispatchNestedFling(velocityX, velocityY, consumed);
}
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return this.scrollUtil.dispatchNestedPreFling(velocityX, velocityY);
}
}
X5WebViewScrollUtil
package com.trs.nmip.common.util.web;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Build;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import androidx.core.view.MotionEventCompat;
import androidx.core.view.NestedScrollingChild;
import androidx.core.view.NestedScrollingChildHelper;
import androidx.core.view.ViewCompat;
import com.tencent.smtt.export.external.extension.interfaces.IX5WebViewClientExtension;
import com.tencent.smtt.export.external.extension.proxy.ProxyWebViewClientExtension;
import com.tencent.smtt.export.external.interfaces.JsResult;
import com.tencent.smtt.export.external.interfaces.WebResourceRequest;
import com.tencent.smtt.export.external.interfaces.WebResourceResponse;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewCallbackClient;
/**
* <pre>
* Created by zhuguohui
* Date: 2021/12/23
* Time: 10:57
* Desc:解决WebView和腾讯X5内核存在的横向滚动冲突的工具类
* </pre>
*/
public class X5WebViewScrollUtil implements NestedScrollingChild {
private final int touchSlop;
CallbackClient mCallbackClient;
WebView webView;
private NestedScrollingChildHelper mChildHelper;
public X5WebViewScrollUtil(WebView webView) {
this.webView = webView;
mCallbackClient = new CallbackClient();
webView.setWebViewCallbackClient(mCallbackClient);
if (webView.getX5WebViewExtension() != null) {
webView.getX5WebViewExtension().setWebViewClientExtension(mWebViewClientExtension);
}
touchSlop = ViewConfiguration.get(webView.getContext()).getScaledTouchSlop();
this.mChildHelper = new NestedScrollingChildHelper(webView);
}
public void setNestedScrollingEnabled(boolean enabled) {
this.mChildHelper.setNestedScrollingEnabled(enabled);
}
public boolean isNestedScrollingEnabled() {
return this.mChildHelper.isNestedScrollingEnabled();
}
public boolean startNestedScroll(int axes) {
return this.mChildHelper.startNestedScroll(axes);
}
public void stopNestedScroll() {
this.mChildHelper.stopNestedScroll();
}
public boolean hasNestedScrollingParent() {
return this.mChildHelper.hasNestedScrollingParent();
}
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
return this.mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
}
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return this.mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return this.mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return this.mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
class CallbackClient implements WebViewCallbackClient {
private int mLastMotionY;
private int mLastMotionX;
private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = new int[2];
private int mNestedYOffset;
boolean overScrollX = false;
public void invalidate() {
}
@Override
public boolean onTouchEvent(MotionEvent event, View view) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
webView.requestDisallowInterceptTouchEvent(true);
overScrollX = false;
}
boolean result = false;
MotionEvent trackedEvent = MotionEvent.obtain(event);
int action = MotionEventCompat.getActionMasked(event);
if (action == 0) {
this.mNestedYOffset = 0;
}
int y = (int) event.getY();
int x = (int) event.getX();
event.offsetLocation(0.0F, (float) this.mNestedYOffset);
switch (action) {
case MotionEvent.ACTION_DOWN:
this.mLastMotionY = y;
this.mLastMotionX = x;
result = webView.super_onTouchEvent(event);
break;
case MotionEvent.ACTION_MOVE:
int deltaY = this.mLastMotionY-y;
int deltaX = this.mLastMotionX-x;
if(Math.abs(deltaX)>touchSlop||Math.abs(deltaY)>touchSlop) {
if(Math.abs(deltaY)>Math.abs(deltaX)){
//开始纵向滚动
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
}
if (dispatchNestedPreScroll(deltaX, deltaY, this.mScrollConsumed, this.mScrollOffset)) {
deltaY -= this.mScrollConsumed[1];
trackedEvent.offsetLocation(0.0F, (float) this.mScrollOffset[1]);
this.mNestedYOffset += this.mScrollOffset[1];
} else {
boolean intercept = !overScrollX;
webView.requestDisallowInterceptTouchEvent(intercept);
}
int oldY = webView.getScrollY();
int oldX = webView.getScrollX();
this.mLastMotionY = y - this.mScrollOffset[1];
this.mLastMotionX = x - this.mScrollOffset[0];
if (deltaY < 0) {
int newScrollY = Math.max(0, oldY + deltaY);
deltaY -= newScrollY - oldY;
if (dispatchNestedScroll(0, newScrollY - deltaY, 0, deltaY, this.mScrollOffset)) {
this.mLastMotionY -= this.mScrollOffset[1];
trackedEvent.offsetLocation(0.0F, (float) this.mScrollOffset[1]);
this.mNestedYOffset += this.mScrollOffset[1];
}
}
trackedEvent.recycle();
result = webView.super_onTouchEvent(event);
}
case MotionEvent.ACTION_UP:
stopNestedScroll();
result = webView.super_onTouchEvent(event);
break;
}
return result;
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent, View view) {
return webView.super_overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY,
isTouchEvent);
}
@Override
public void computeScroll(View view) {
webView.super_computeScroll();
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY, View view) {
webView.super_onOverScrolled(scrollX, scrollY, clampedX, clampedY);
this.overScrollX = clampedX;
}
@Override
public void onScrollChanged(int l, int t, int oldl, int oldt, View view) {
webView.super_onScrollChanged(l, t, oldl, oldt);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev, View view) {
return webView.super_dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev, View view) {
return webView.super_onInterceptTouchEvent(ev);
}
}
;
private IX5WebViewClientExtension mWebViewClientExtension = new ProxyWebViewClientExtension() {
public void invalidate() {
}
public void onReceivedViewSource(String data) {
}
;
@Override
public boolean onTouchEvent(MotionEvent event, View view) {
return mCallbackClient.onTouchEvent(event, view);
}
// 1
public boolean onInterceptTouchEvent(MotionEvent ev, View view) {
return mCallbackClient.onInterceptTouchEvent(ev, view);
}
// 3
public boolean dispatchTouchEvent(MotionEvent ev, View view) {
return mCallbackClient.dispatchTouchEvent(ev, view);
}
// 4
public boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent, View view) {
return mCallbackClient.overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent, view);
}
// 5
public void onScrollChanged(int l, int t, int oldl, int oldt, View view) {
mCallbackClient.onScrollChanged(l, t, oldl, oldt, view);
}
// 6
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY, View view) {
mCallbackClient.onOverScrolled(scrollX, scrollY, clampedX, clampedY, view);
}
// 7
public void computeScroll(View view) {
mCallbackClient.computeScroll(view);
}
public boolean notifyAutoAudioPlay(String url, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(webView.getContext());
builder.setTitle("提示");
builder.setMessage("音乐自动播放提示!");
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
builder.setNeutralButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
result.cancel();
}
});
builder.show();
return true;
}
;
@Override
public void onResponseReceived(WebResourceRequest request, WebResourceResponse response, int errorCode) {
super.onResponseReceived(request, response, errorCode);
}
};
}