天天看点

swiperefreshLayout和webview滚动冲突(X5WebView)

1、问题

swiperefreshLayout,webview(或者X5WebView)嵌套使用,下滚没问题手势在webview,上滚手势被swiperefreshLayout 取走执行刷新动作不是想要的。

需要效果:上滚webview能滚就滚webview。webview滚到顶部了,才执行swiperefreshLayout 刷新

2、概述

swiperefreshLayout 作为谷歌推出的下拉刷新库,并没有内部处理这些手势分配。官网给了个api。 说如果子布局想滚动 重写 canChildScrollUp () 。实际上并不是很好操作。搜了很多blog 都没有用,下面给出有效的解决方式,再进行分析。适用与腾讯的X5WebView

3、解决方式

基于 需要效果,关键是识别到 webview 什么时候滚到顶部再把手势给 swiperefreshLayout 就可以了。只是 webView.getScrollY() 总是 =0 。

解决方式:

//重定义webview 这里继承的是X5WebView
public class X5ObserWebView extends X5WebView {
    private OnScrollChangedCallback mOnScrollChangedCallback;

    public X5ObserWebView(final Context context) {
        super(context);
    }

    public X5ObserWebView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void tbs_onScrollChanged(int l, int t, int oldl, int oldt, View view) {
        this.super_onScrollChanged(l, t, oldl, oldt);
        //X5WebView 父类屏蔽了 onScrollChanged 方法 要用该方法
        if (mOnScrollChangedCallback != null) mOnScrollChangedCallback.onScroll(l, t);
    }

    @Override
    protected void onScrollChanged(final int l, final int t, final int oldl, final int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        //普通webview用这个
        if (mOnScrollChangedCallback != null) mOnScrollChangedCallback.onScroll(l, t);
    }

    public OnScrollChangedCallback getOnScrollChangedCallback() {
        return mOnScrollChangedCallback;
    }

    public void setOnScrollChangedCallback(final OnScrollChangedCallback onScrollChangedCallback) {
        mOnScrollChangedCallback = onScrollChangedCallback;
    }


    public interface OnScrollChangedCallback {
        void onScroll(int l, int t);
    }
}
           

activity 里调用

webView.setOnScrollChangedCallback(new X5ObserWebView.OnScrollChangedCallback() {
            public void onScroll(int l, int t) {
                //Log.d(TAG, "We Scrolled etc..." + l + " t =" + t);
                if (t == ) {//webView在顶部
                    swipeLayout.setEnabled(true);
                } else {//webView不是顶部
                    swipeLayout.setEnabled(false);
                }
            }
        });
           

4、简单原理说明,可忽略

监听webview滚动,setOnScrollChangeListener 这个方法要api = 23 才可以用.

swiperefreshLayout 官方文档里是建议 如果要子布局上滑手势生效是重写swiperefreshLayout

的 boolean canChildScrollUp ()方法。返回true 就是子布局手势,false 就是自己使用。和手势拦截原则是一致了,viewGroup 优先取到手势,决定是不是传给子布局。没有用官方推荐的重写boolean canChildScrollUp () 方式,因为webview 是否滚到顶部识别不出来,也要用重写回调的方式就不适合再用该方式了。不过原理还是很清晰

boolean canChildScrollUp ()

boolean Whether it is possible for the child view of this layout to scroll up. Override this if the child view is a custom view.
           
@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        ensureTarget();

        final int action = MotionEventCompat.getActionMasked(ev);
        int pointerIndex;

        if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
            mReturningToStart = false;
        }

//拦截
        if (!isEnabled() || mReturningToStart || canChildScrollUp()
                || mRefreshing || mNestedScrollInProgress) {
            // Fail fast if we're not in a state where a swipe is possible
            return false;
        }
        ...



    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        int pointerIndex = -;

        if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
            mReturningToStart = false;
        }
//拦截
        if (!isEnabled() || mReturningToStart || canChildScrollUp()
                || mRefreshing || mNestedScrollInProgress) {
            // Fail fast if we're not in a state where a swipe is possible
            return false;
        }
        ...



    @Override
    public void onNestedScroll(final View target, final int dxConsumed, final int dyConsumed,
            final int dxUnconsumed, final int dyUnconsumed) {
        // Dispatch up to the nested parent first
        dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
                mParentOffsetInWindow);

        // This is a bit of a hack. Nested scrolling works from the bottom up, and as we are
        // sometimes between two nested scrolling views, we need a way to be able to know when any
        // nested scrolling parent has stopped handling events. We do that by using the
        // 'offset in window 'functionality to see if we have been moved from the event.
        // This is a decent indication of whether we should take over the event stream or not.
        final int dy = dyUnconsumed + mParentOffsetInWindow[];

       //拦截
        if (dy <  && !canChildScrollUp()) {
            mTotalUnconsumed += Math.abs(dy);
            moveSpinner(mTotalUnconsumed);
        }
    }

           

继续阅读