解决 腾讯X5内核 在 ViewPager2 嵌套滚动中横向滑动冲突 ,AppBarLayout中纵向滚动冲突

问题

如果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的相关事件。
可以参考官网的文档。

TBS开发指引

在这里插入图片描述

实现

已经将相关的功能实现了,直接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);
        }
    };
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值