我正在尝试将 SwipeRefreshLayout 与 WebView 一起使用。
我面临的问题是在页面中间,当用户向下滚动时,会出现不需要的刷新。
如何使刷新事件仅在 webview 的滚动位置位于顶部时发生。(即,他正在查看页面的顶部)?
我正在尝试将 SwipeRefreshLayout 与 WebView 一起使用。
我面临的问题是在页面中间,当用户向下滚动时,会出现不需要的刷新。
如何使刷新事件仅在 webview 的滚动位置位于顶部时发生。(即,他正在查看页面的顶部)?
我已经设法解决它而无需扩展任何东西。看看这个片段(片段特定):
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的回答。
我认为这样做的推荐方法是扩展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;
}
}
只需实现您的Fragment
or Activity
, ViewTreeObserver.OnScrollChangedListener
然后设置 Listener 就像webview.getViewTreeObserver().addOnScrollChangedListener(this);
在onScrollChanged()
方法中这样做
@Override
public void onScrollChanged() {
if (webview.getScrollY() == 0) {
swipeLayout.setEnabled(true);
} else {
swipeLayout.setEnabled(false);
}
}
支持库的最新版本具有SwipeRefreshLayout#setOnChildScrollUpCallback
允许您避免子类化的方法SwipeRefreshLayout
。
mBinding.swipeRefreshLayout.setOnChildScrollUpCallback((parent, child) ->
mBinding.webView.getScrollY() > 10);
您可以通过使用自定义 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);
}
}
我正在回答同样的问题,但使用的是 Kotlin。通过在获取swipeRefreshLayout
的 scrollY 时启用:webView
0
在您的片段或活动中创建此变量:
private val onScrollChangedListener = ViewTreeObserver.OnScrollChangedListener{ swipeRefreshLayout.isEnabled = webView.scrollY == 0 }
然后onViewCreated
:
swipeRefreshLayout.viewTreeObserver.addOnScrollChangedListener(onScrollChangedListener)
然后onStop
:
swipeRefreshLayout.viewTreeObserver.removeOnScrollChangedListener(onScrollChangedListener)
从这里一切都会很好。但是,如果您正在使用的网页有一个不滚动的固定标题,则scrollY
始终等于0
. 并且此解决方案与此页面中的其他解决方案一起可能无法正常工作。
你可以试试这个:
它适用于 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。
我遇到了类似的问题,但这里给出的答案都不适合我。所以,我从这个答案中得到了解决方案。
第 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);
}
}
});
}
hiBrianLee 提供的答案几乎对我有用,但正如 John Shelley 所写,getScrollY
在我的情况下总是返回 0(我使用 aListView
作为嵌套滚动子项)。然而,立即起作用的是使用canScrollVertically
代替(这是一种View
方法,因此它可用于ListView
s 和WebView
s,也可用于任何其他类型的滚动子级)。
我的布局类如下所示:
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
或任何其他专用视图类。)
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
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)
}
}
<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>
以上没有对我有用。这是我的方法
@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;
}
// 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);
}
});
我找到了解决方案。只需使用 CoordinatorLayout 作为根布局并将 layout_behavior="@string/appbar_scrolling_view_behavior" 添加到您的 SwipeRefreshLayout。这对我来说是完美的。
像处理其他视图一样正常使用 SwipeRefreshLayout,但启用嵌套滚动。
swipeRefreshLayout.setNestedScrollingEnabled(true);
我发现禁用webview上的嵌套滚动(而不是 SwipeRefreshLayout 本身)为我解决了这个问题。不可能在所有情况下都这样做,但在我的情况下。
webView.setNestedScrollingEnabled(false);