安卓recyclerview嵌套webview造成的滑动冲突解决以及webview加载完之前白屏的问题解决

此博客作为技术记录用,希望能帮助到需要的人。

安卓recyclerview嵌套webview造成的滑动冲突

最近在做一个智能客服的项目,整个聊天内容是一个recyclerview,里面每一条信息都是一个item,不同类型的回复对应不同的viewholder,其中有一个item的布局中有一个webview,该webview加载的h5页面的实际长度超过了手机屏幕的宽度,所以需要能够左右滑动,但是用户实际滑动过程中不可能完全水平滑动,更多是小角度的斜着滑。

如果你了解view的事件分发机制和滑动的原理,就能预见到斜着滑动会导致这个webview在水平方向滑动会特别生硬且伴随着上下抖动。这是由于view滑动的原理是在一段时间内不断的调用draw方法重新绘制view,每次绘制之间的时间间隔很短,这样就实现了平滑的滑动效果(原理类似动画的制作)。

至于view的事件分发可以看看郭神的博客
Android事件分发机制完全解析,带你从源码的角度彻底理解(上).
此处我是使用了一个相对布局作为一个webviewcontainer,通过设置其背景图片来设置聊天信息的背景图(类似qq)。


此问题的解决办法是:重写这个相对布局,并重写它的ontouchevent方法,在方法中计算出用户滑动的方向和水平方向的夹角,如果超过60°,就判定为用户想上下滑动,就调用requestDisallowInterceptTouchEvent方法来干预其父控件recyclerview的intercptouchevent方法,让它拦截这个事件并处理;如果小于60°,就认为用户想左右滑动,即不让recyclerview拦截该事件。代码如下:


public class AiRelativeLayout extends RelativeLayout {
        public float oldY;
        public float oldX;
        public float newY;
        public float newX;
        public AiRelativeLayout (Context context){
            super(context);
        }
        public AiRelativeLayout (Context context, AttributeSet attributeSet){
            super(context,attributeSet);
        }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                newX = ev.getX();
                newY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                //手指滑动同时判断滑动方向,一旦滑动方向大于+-60便调用
                getParent().getParent().requestDisallowInterceptTouchEvent(true);
                //将滑动事件交给RecyclerView来处理
                oldX = newX;
                oldY = newY;
                newX = ev.getX();
                newY = ev.getY();
                float moveX = Math.abs(oldX - newX);
                float moveY = Math.abs(oldY - newY);
                //moveX * 1.73 < moveY  ,判断左右滑动范围为+-60度
                if(moveX*1.73<moveY){
                    getParent().getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;

        }
        return super.onInterceptTouchEvent(ev);
    }


}

代码中这个requestDisallowInterceptTouchEvent方法的参数是一个布尔型变量,传true表示禁止该view对事件的拦截,传false表示不禁止。此处我的这个重写的相对布局的父控件是一个线性布局,也就是这个item的根布局,其父布局就是recyclerview,所以调用了两次getParent()。此处如果你的webview没有重写过且以后都不会重写onInterceptTouchEvent方法的话,你可以直接重写这个webview而不是这个相对布局,相对应的,需要调用三次getParent()来控制recyclerview是否拦截事件。
这段代码很好理解,就是计算用户每次滑动的角度,若超过60°就把事件分发给recyclerview处理,否则分发给webview,最终效果就是小于60°的斜着滑动只会让webview左右滑动,大于60°就是只会上下滑动。

webview加载完之前白屏的问题解决

如果是页面效果比较复杂的h5页面,页面完全加载是需要时间的,在加载完成之前会在webview的位置显示白色背景,这个背景不是背景图也不是背景色,而是webview自带的显示效果,无法通过设置隐藏。如果你对这个问题很在意的话这里我提供一个解决办法:这个办法有一个前提,那就是你还没对webviewclient进行自定义
webview加载页面都需要调用loadurl方法,在此方法后加上以下代码

mWebviewContainer.setVisibility(View.INVISIBLE);
mWebView.loadUrl(url);
mWebView.setWebViewClient(new WebViewClient(){
                @Override
                public void onPageFinished(WebView view, String url) {
                	mWebviewContainer.setVisibility(View.VISIBLE);
                    super.onPageFinished(view, url);
                }
            });

这里的mWebviewContainer就是我前文中重写的相对布局,如果你不需要也可以重写webview,那么相应的这里也应该是对webview设置Visibility。需要在给webview加载链接之前就把它设置GONE,并在webviewclient中重写onpagefinished方法,该方法的调用即表示h5页面加载完毕,在此方法中再设置其可见,这样就解决掉了白屏的问题。
如果你希望在加载完成之前让其完全不显示(不留空间)的话把开始设置的INVISIBLE换成GONE即可。
以上,如有错误欢迎指正。

你可能感兴趣的:(安卓recyclerview嵌套webview造成的滑动冲突解决以及webview加载完之前白屏的问题解决)