【问题标题】:SwipeRefreshLayout + WebView when scroll position is at top当滚动位置位于顶部时,SwipeRefreshLayout + WebView
【发布时间】:2014-07-09 15:56:18
【问题描述】:

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

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

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

【问题讨论】:

  • 我做了一些测试,我可以确认这个问题在一些网页上仍然存在。因此需要修复。

标签: android webview swiperefreshlayout


【解决方案1】:

我已经设法解决它而无需扩展任何东西。看看这个 sn-p (Fragment-specific):

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 的回答。

【讨论】:

  • 穷人单线(带lambda):swipeLayout.getViewTreeObserver().addOnScrollChangedListener(() -> swipeLayout.setEnabled((webView.getScrollY() == 0)));
  • 免费为 Kotlin 提供一个衬里 swipeRefreshLayout.isEnabled = webView.scrollY == 0
  • 使用此解决方案有时会在滚动或翻动时闪烁 webview。
【解决方案2】:

我认为推荐的方法是扩展 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;
    }
}

【讨论】:

  • 真正的问题是为什么SwipeRefreshLayout 在请求不通过滚动子视图时拦截触摸事件事件。删除该“功能”将自动解决所有这些问题。详情见以下回答:stackoverflow.com/questions/25978462/…
  • mWebView.getScrollY 对我来说总是 0。
  • @JohnShelley 检查您的网页布局。 “位置:相对;” CSS 中的属性可能是问题的根源。相对定位会导致在 WebView 中滚动出现问题。
  • @petrnohejl 但是如果我们正在加载其他网站怎么办?我们无法控制其他网站,所以这不是全球解决方案
  • 无法启动活动,android.view.InflateException
【解决方案3】:

只需使用ViewTreeObserver.OnScrollChangedListener 实现您的Fragment 或Activity,然后将侦听器设置为webview.getViewTreeObserver().addOnScrollChangedListener(this);

在onScrollChanged() 方法中这样做

    @Override
    public void onScrollChanged() {
        if (webview.getScrollY() == 0) {
            swipeLayout.setEnabled(true);
        } else {
            swipeLayout.setEnabled(false);
        }
    }

【讨论】:

  • 这应该是公认的答案,谢谢队友
【解决方案4】:

最新版本的支持库具有SwipeRefreshLayout#setOnChildScrollUpCallback 方法,可让您避免子类化SwipeRefreshLayout。

mBinding.swipeRefreshLayout.setOnChildScrollUpCallback((parent, child) -> 
            mBinding.webView.getScrollY() > 10);

【讨论】:

  • 我使用了swipeRefreshLayout.setOnChildScrollUpCallback((p, c) -> webview.getScrollY() > 0);,没有必要为androidx.swiperefreshlayout:swiperefreshlayout:1.1.0扩展SwipeRefreshLayout
【解决方案5】:

您可以通过使用自定义 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);
    }
}

【讨论】:

  • 您打算如何使用嵌套卷轴?有可能吗?
【解决方案6】:

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

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

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

然后onViewCreated:

swipeRefreshLayout.viewTreeObserver.addOnScrollChangedListener(onScrollChangedListener)

然后onStop:

swipeRefreshLayout.viewTreeObserver.removeOnScrollChangedListener(onScrollChangedListener)

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

【讨论】:

  • 您对固定标头的评论确实为我节省了大量调试时间!谢谢。
【解决方案7】:

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

第一步:创建类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);
            }
        }
    });
}

【讨论】:

    【解决方案8】:

    你可以试试这个:

    它适用于 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: 如果网页具有嵌套的滚动内容,这将不起作用。因此,我决定不使用 SwipeRefreshLayout,而是使用简单的 ImageButton 来重新加载网页。

    【讨论】:

      【解决方案9】:

      hiBrianLee 提供的答案几乎对我有用,但正如 John Shelley 所写,getScrollY 在我的情况下总是返回 0(我使用 ListView 作为嵌套滚动子项)。然而,立即起作用的是使用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 或任何其他专用视图类。)

      【讨论】:

        【解决方案10】:

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

         @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;
        

        }

        【讨论】:

          【解决方案11】:
          // 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);
          
                  }
          
              });
          

          【讨论】:

            【解决方案12】:

            解决SwipeRefreshLayout和WebView之间冲突的最可靠方法 是将触摸/滑动手势绑定到屏幕顶部的某个空间。

            以前基于检测webview.scrollY的答案可能不可靠,因为某些页面具有固定的标题位置,因此即使有可滚动列表webview.scrollY也总是返回0

            SOURCE

            1. 创建自定义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. 在视图文件中应用它
                <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>
            

            【讨论】:

              【解决方案13】:

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

              swipeRefreshLayout.setNestedScrollingEnabled(true);
              

              【讨论】:

              • 事情是一旦用户向下滑动太快它会调用 SwipeReferesh 如果用户滚动正常或缓慢它是好的。
              【解决方案14】:

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

              【讨论】:

                【解决方案15】:

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

                webView.setNestedScrollingEnabled(false);
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-08-13
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多