38

我正在尝试将 SwipeRefreshLayout 与 WebView 一起使用。

我面临的问题是在页面中间,当用户向下滚动时,会出现不需要的刷新。

如何使刷新事件仅在 webview 的滚动位置位于顶部时发生。(即,他正在查看页面的顶部)?

4

15 回答 15

36

我已经设法解决它而无需扩展任何东西。看看这个片段(片段特定):

private ViewTreeObserver.OnScrollChangedListener mOnScrollChangedListener;

@Override
public void onStart() {
    super.onStart();

    swipeLayout.getViewTreeObserver().addOnScrollChangedListener(mOnScrollChangedListener =
            new ViewTreeObserver.OnScrollChangedListener() {
                @Override
                public void onScrollChanged() {
                    if (mWebView.getScrollY() == 0)
                        swipeLayout.setEnabled(true);
                    else
                        swipeLayout.setEnabled(false);

                }
            });
}

@Override
public void onStop() {
    swipeLayout.getViewTreeObserver().removeOnScrollChangedListener(mOnScrollChangedListener);
    super.onStop();
}

对于更广泛的上下文,请查看我对Android - SwipeRefreshLayout with empty textview的回答。

于 2015-01-13T07:05:02.540 回答
25

我认为这样做的推荐方法是扩展SwipeRefreshLayout和覆盖canChildScrollUp()- https://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html#canChildScrollUp()

这就是它在 Google IO 2014 Schedule 应用程序中的完成方式 - https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget /MultiSwipeRefreshLayout.java#L76

CustomSwipeRefreshLayout可能看起来像:

public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {

    private CanChildScrollUpCallback mCanChildScrollUpCallback;

    public interface CanChildScrollUpCallback {
        boolean canSwipeRefreshChildScrollUp();
    }

    public void setCanChildScrollUpCallback(CanChildScrollUpCallback canChildScrollUpCallback) {
        mCanChildScrollUpCallback = canChildScrollUpCallback;
    }

    @Override
    public boolean canChildScrollUp() {
        if (mCanChildScrollUpCallback != null) {
            return mCanChildScrollUpCallback.canSwipeRefreshChildScrollUp();
        }
        return super.canChildScrollUp();
    }
}

在你的Activity那有WebView

public class FooActivity
        implements CustomSwipeRefreshLayout.CanChildScrollUpCallback {

    private CustomSwipeRefreshLayout mRefreshLayout;
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // after initialization
        mRefreshLayout.setCanChildScrollUpCallback(this);
    }

    @Override
    public boolean canSwipeRefreshChildScrollUp() {
        return mWebview.getScrollY() > 0;
    }
}
于 2015-02-05T22:59:50.520 回答
12

只需实现您的Fragmentor ActivityViewTreeObserver.OnScrollChangedListener然后设置 Listener 就像webview.getViewTreeObserver().addOnScrollChangedListener(this);

onScrollChanged()方法中这样做

    @Override
    public void onScrollChanged() {
        if (webview.getScrollY() == 0) {
            swipeLayout.setEnabled(true);
        } else {
            swipeLayout.setEnabled(false);
        }
    }
于 2016-01-04T10:03:57.813 回答
5

支持库的最新版本具有SwipeRefreshLayout#setOnChildScrollUpCallback允许您避免子类化的方法SwipeRefreshLayout

mBinding.swipeRefreshLayout.setOnChildScrollUpCallback((parent, child) -> 
            mBinding.webView.getScrollY() > 10);
于 2018-09-13T08:13:35.850 回答
2

您可以通过使用自定义 SwipeRefreshLayout 扩展 SwipeRefreshLayout 来解决此问题,该自定义 SwipeRefreshLayout 接收 WebView 并使用 WebView 检查 scrollY 位置。

注意:这将适用于滚动整个页面,但它可能不适用于嵌套滚动。此外,如果您有水平滚动,您可能还想添加在此答案中找到的逻辑:https ://stackoverflow.com/a/24453194

public class CustomSwipeToRefresh extends SwipeRefreshLayout {

    private final int yScrollBuffer = 5;
    private WebView mWebView;

    public CustomSwipeToRefresh(Context context, WebView webView) {
        super(context);

        mWebView = webView;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (mWebView.getScrollY() > yScrollBuffer)
            return false;

        return super.onInterceptTouchEvent(event);
    }
}
于 2014-07-23T18:01:25.927 回答
2

我正在回答同样的问题,但使用的是 Kotlin。通过在获取swipeRefreshLayout的 scrollY 时启用:webView0

在您的片段或活动中创建此变量:

private val onScrollChangedListener = ViewTreeObserver.OnScrollChangedListener{ swipeRefreshLayout.isEnabled = webView.scrollY == 0 }

然后onViewCreated

swipeRefreshLayout.viewTreeObserver.addOnScrollChangedListener(onScrollChangedListener)

然后onStop

swipeRefreshLayout.viewTreeObserver.removeOnScrollChangedListener(onScrollChangedListener)

从这里一切都会很好。但是,如果您正在使用的网页有一个不滚动的固定标题,则scrollY始终等于0. 并且此解决方案与此页面中的其他解决方案一起可能无法正常工作。

于 2018-09-28T15:06:15.083 回答
1

你可以试试这个:

它适用于 API >= 23 Android Marshmallow

webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
        @Override
        public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
            swipeRefreshLayout.setEnabled(scrollY == 0);
        }
    });

编辑 1: 如果网页有嵌套的滚动内容,这不起作用。因此,我决定使用简单的 ImageButton 来重新加载网页,而不是使用 SwipeRefreshLayout。

于 2021-03-11T08:42:21.757 回答
1

我遇到了类似的问题,但这里给出的答案都不适合我。所以,我从这个答案中得到了解决方案。

第 1 步:创建类CustomWebView

public class CustomWebView extends WebView{

private OnScrollChangedCallback mOnScrollChangedCallback;

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

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

public CustomWebView(final Context context, final AttributeSet attrs, final int defStyle)
{
    super(context, attrs, defStyle);
}

@Override
protected void onScrollChanged(final int l, final int t, final int oldl, final int oldt)
{
    super.onScrollChanged(l, t, oldl, oldt);
    if(mOnScrollChangedCallback != null) mOnScrollChangedCallback.onScroll(l, t);
}

public OnScrollChangedCallback getOnScrollChangedCallback()
{
    return mOnScrollChangedCallback;
}

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

/**
 * Impliment in the activity/fragment/view that you want to listen to the webview
 */
public static interface OnScrollChangedCallback
{
    public void onScroll(int horizontal, int vertical);
}}

第2步:在xml文件中webview像这样使用-

 <com.yourpackagename.CustomWebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

第 3 步:在您的MainActivity使用中setOnScrollChangedCallback-

  mWebView.setOnScrollChangedCallback(new CustomWebView.OnScrollChangedCallback(){
        public void onScroll(int horizontal, int vertical){
            System.out.println("=="+horizontal+"---"+vertical);
   //this is to check webview scroll behaviour
            if (vertical<50){
                swipeRefreshLayout.setEnabled(true);
            }else{
                swipeRefreshLayout.setEnabled(false);
            }
        }
    });
}
于 2017-01-23T11:08:50.390 回答
0

hiBrianLee 提供的答案几乎对我有用,但正如 John Shelley 所写,getScrollY在我的情况下总是返回 0(我使用 aListView作为嵌套滚动子项)。然而,立即起作用的是使用canScrollVertically代替(这是一种View方法,因此它可用于ListViews 和WebViews,也可用于任何其他类型的滚动子级)。

我的布局类如下所示:

public class NestedScrollSwipeLayout extends SwipeRefreshLayout {

    private ListView scrollChild;

    public NestedScrollSwipeLayout(Context context) {
        super(context);
    }

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

    public void setScrollChild(ListView child) {
        this.scrollChild = child;
    }

    @Override
    public boolean canChildScrollUp() {
        return (scrollChild != null && scrollChild.canScrollVertically(-1)) ||
            super.canChildScrollUp();
    }
}

(如前所述,canScrollVertically是一种View方法,因此ListView可以安全地替换为通用View或任何其他专用视图类。)

于 2015-11-26T11:50:36.413 回答
0

The most reliable approach to solve conflict between SwipeRefreshLayout and WebView is to bound touch/swipe gesture to certain space at the top of the screen.

Previous answers base on detecting webview.scrollY may be not reliable, because some pages has fixed header position, therefore even if there is scrollable list webview.scrollY will alwyas returns 0

SOURCE

  1. Create custom SwipeRefreshLayout
class MySwipeRefreshLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : SwipeRefreshLayout(context, attrs) {

    private var mDeclined = false
    private var mPrevY: Float = 0f

    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                mPrevY = event.y

                mDeclined = false
                if (mPrevY > resources.getDimension(R.dimen.disable_swipe_after)){
                    mDeclined = true
                }
            }
            MotionEvent.ACTION_MOVE -> {
                if (mDeclined) {
                    return false
                }
            }
        }
        return super.onInterceptTouchEvent(event)
    }
}
  1. Apply it in the view file
    <yourpackage.MyLimitedSwipeRefreshLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:windowSoftInputMode="adjustResize"
            >

            <WebView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                />
        </FrameLayout>
    </yourpackage.MySwipeRefreshLayout>
于 2020-12-04T14:31:06.583 回答
0

以上没有对我有用。这是我的方法

 @Override
 public boolean dispatchTouchEvent(MotionEvent event)
 {
super.dispatchTouchEvent(event);
int x = (int)event.getX();
int y = (int)event.getY();
 if(y>800){
     swipeRefreshLayout.setEnabled(false);
 }else {
     swipeRefreshLayout.setEnabled(true);
 }
 Log.e("yy",""+y);

switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
    case MotionEvent.ACTION_MOVE:
    case MotionEvent.ACTION_UP:
}

return false;

}

于 2020-04-02T09:35:06.177 回答
0
// swipe - active only when WebView Y==0        
swipeView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                if (webView.getScrollY() == 0) {
                    Log.d(TAG, "onScrollChanged - y=0 - make swipe refresh active");
                    swipeView.setEnabled(true);
                }else {
                    swipeView.setEnabled(false);
                    Log.d(TAG, "onScrollChanged - y is not null - swipe refresh not active");
                }
            }
        });

// swipe - after refresh do work (see above for condition)
    swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

        @Override
        public void onRefresh() {

            Log.d(TAG, "Swipe - refresh");

            // do work ... your work method here
            getSchedule();

            // hide spinner of swiperefreshlayout
            swipeView.setRefreshing(false);

        }

    });
于 2020-06-20T13:48:09.000 回答
-1

我找到了解决方案。只需使用 CoordinatorLayout 作为根布局并将 layout_behavior="@string/appbar_scrolling_view_behavior" 添加到您的 SwipeRefreshLayout。这对我来说是完美的。

于 2017-06-12T08:26:53.967 回答
-1

像处理其他视图一样正常使用 SwipeRefreshLayout,但启用嵌套滚动。

swipeRefreshLayout.setNestedScrollingEnabled(true);
于 2016-10-21T19:37:37.370 回答
-1

我发现禁用webview上的嵌套滚动(而不是 SwipeRefreshLayout 本身)为我解决了这个问题。不可能在所有情况下都这样做,但在我的情况下。

webView.setNestedScrollingEnabled(false);
于 2017-09-06T16:36:48.983 回答