问题
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)?
回答1:
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.
回答2:
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;
}
}
回答3:
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);
}
}
回答4:
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: 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);
}
}
回答5:
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);
回答6:
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);
}
}
});
}
回答7:
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 = ViewTreeObserver.OnScrollChangedListener{ swipeRefreshLayout.isEnabled = webView.scrollY == 0 }
Then onViewCreated:
swipeRefreshLayout.viewTreeObserver.addOnScrollChangedListener(onScrollChangedListener)
Then onStop:
swipeRefreshLayout.viewTreeObserver.removeOnScrollChangedListener(onScrollChangedListener)
From here everything will work well. But if the webpage your are using has a fixed header that does not scroll scrollY will always be equal to 0. And this solution together with other solution in this page may not work.
回答8:
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.)
回答9:
Use SwipeRefreshLayout normally as you do with other views but enable nested scrolling.
swipeRefreshLayout.setNestedScrollingEnabled(true);
回答10:
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.
回答11:
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);
来源:https://stackoverflow.com/questions/24658428/swiperefreshlayout-webview-when-scroll-position-is-at-top