I'm trying to use SwipeRefreshLayout with WebView.

I'm facing the problem where in the middle of page, when user scrolls down, unwanted refresh kicks in.

How do I make the refresh event only happen when webview's scroll position is at the top. (ie, he's looking at the top portion of the page)?

11 Answers 11

I've managed to solve it without having to extend anything. Have a look at this snippet (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();
}

For a broader context, have a look at my answer to Android - SwipeRefreshLayout with empty textview.

  • The only working proper answer – TeodorKolev May 20 '16 at 6:59
  • 1
    worked like charm...!!! – Yogesh Patel Sep 23 '16 at 4:32
  • 2
    One-liner of the poor man (with lambda): swipeLayout.getViewTreeObserver().addOnScrollChangedListener(() -> swipeLayout.setEnabled((webView.getScrollY() == 0))); – Ek1noX Nov 25 '16 at 15:12
  • 1
    Doesn't work on iframes – Daniel F Apr 2 at 21:58
  • Free one liner for Kotlin swipeRefreshLayout.isEnabled = webView.scrollY == 0 – Xenolion 34 mins ago

I think the recommended way of doing this is to extend the SwipeRefreshLayout and override canChildScrollUp() - https://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html#canChildScrollUp()

This is how it's done in the Google IO 2014 Schedule app - https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/MultiSwipeRefreshLayout.java#L76

The CustomSwipeRefreshLayout will probably look like:

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();
    }
}

And in your Activity that has the 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;
    }
}
  • The real issue is why does SwipeRefreshLayout intercepts touch events event when requested not to by scrolling child views. Removing that "feature" would solve all these issues automatically. See the following answer for details: stackoverflow.com/questions/25978462/… – corsair992 Feb 6 '15 at 0:17
  • 2
    mWebView.getScrollY is always 0 for me. – John Shelley Oct 29 '15 at 20:04
  • 1
    @JohnShelley Check your web layout. "position: relative;" property in your CSS might be the source of your problem. Relative positioning causes troubles with scrolling in the WebView. – petrnohejl Feb 16 '16 at 23:32
  • @petrnohejl but what if we are loading some othe website? we can't have control over other websites, so this is not a global solution – Lalit Poptani Dec 1 '16 at 11:52
  • Unable to start activity, android.view.InflateException – Pablo Cegarra Jun 30 '17 at 7:29

Just implement your Fragment or Activity with ViewTreeObserver.OnScrollChangedListener then set Listener like webview.getViewTreeObserver().addOnScrollChangedListener(this);

In onScrollChanged() method do like this

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

You can solve this by extending SwipeRefreshLayout with a custom SwipeRefreshLayout that takes in the WebView and use the WebView to check the scrollY position.

Note: This will work for scrolling the whole page, but it may not work with nested scrolls. Also, if you have horizontal scrolling you may also want to add the logic found in this answer: http://stack.kinokrad-co.coma/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);
    }
}
  • How would you purpose it work with nested scrolls? Is it possible? – John Shelley Oct 29 '15 at 20:24

I was facing Similar issue but none of the answers given here works for me. So, I got a solution from this answer.

Step 1 : Create Class 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);
}}

Step 2 : In xml file use webview like this-

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

Step 3 : In your MainActivity use 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);
            }
        }
    });
}

The answer provided by hiBrianLee almost worked for me, but as John Shelley wrote, getScrollY always returned 0 in my case (I used a ListView as the nested scrolling child). What worked straight away, however, was to use canScrollVertically instead (which is a View method, so it's available both for ListViews and WebViews, and also for any other kind of scrolling child).

My layout class looks as follows:

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();
    }
}

(As mentioned, canScrollVertically is a View method, so the ListView can be safely replaced by a general View or any other specialized view class.)

Most recent versions of support library have SwipeRefreshLayout#setOnChildScrollUpCallback method which allows you to avoid subclassing SwipeRefreshLayout.

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

I am answering the same question but using Kotlin. By enabling swipeRefreshLayout when the scrollY of webView gets 0:

Make this variable in your fragment or activity:

private val onScrollChangedListener = object : ViewTreeObserver.OnScrollChangedListener {
    override fun onScrollChanged() {
        swipeRefreshLayout.isEnabled = webView.scrollY == 0
    }
}

Then onViewCreated:

swipeRefreshLayout.viewTreeObserver.addOnScrollChangedListener(onScrollChangedListener)

Then onStop:

swipeRefreshLayout.viewTreeObserver.removeOnScrollChangedListener(onScrollChangedListener)

From here everything will work well.

Use SwipeRefreshLayout normally as you do with other views but enable nested scrolling.

swipeRefreshLayout.setNestedScrollingEnabled(true);
  • Thing is once user swipe down too fast it will invoke SwipeReferesh if the user scrolls normally or slowly it is good. – Tej Oct 24 '16 at 14:31

i find a solution for it. Just use the CoordinatorLayout as the root layout and add layout_behavior="@string/appbar_scrolling_view_behavior" to your SwipeRefreshLayout.It's perfect for me.

I found disabling nested scrolling on the webview (not the SwipeRefreshLayout itself) sorted this problem for me. It won't be possible to do this in all situations, but was in mine.

webView.setNestedScrollingEnabled(false);

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.