Java 向上滚动不适用于 Listview 中的 SwipeRefreshLayout

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25270171/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 00:01:34  来源:igfitidea点击:

Scroll up does not work with SwipeRefreshLayout in Listview

javaandroidswiperefreshlayout

提问by user3773337

I want to implement scroll to refresh functionality with a listView. Also there are other view elements in the same layout file which are displayed if the list is empty. Here is my layout file. The problem is that when I scroll down and then try to scroll up, instead of scrolling all the way to the top and then refreshing it just refreshes there and scroll up is not working.

我想使用 listView 实现滚动刷新功能。如果列表为空,则在同一布局文件中还有其他视图元素会显示。这是我的布局文件。问题是当我向下滚动然后尝试向上滚动时,而不是一直滚动到顶部然后刷新它只是在那里刷新并且向上滚动不起作用。

<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/swipe_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="64dp"
        android:paddingLeft="16dp"
        android:paddingRight="16dp" >

        <ImageView
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:src="@drawable/inbox_empty" />

        <TextView
            android:id="@+id/noEventsText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="16dp"
            android:layout_toRightOf="@+id/noEventsIcon" />

        <View
            android:id="@+id/divider"
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:layout_alignParentBottom="true"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:background="@color/dividerOnBlack" />
    </RelativeLayout>

    <ListView
        android:id="@+id/list_items"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:cacheColorHint="@android:color/transparent"
        android:choiceMode="multipleChoice"
        android:descendantFocusability="afterDescendants"
        android:divider="@android:color/transparent"
        android:dividerHeight="0px"
        android:scrollbars="none" />
</LinearLayout>

</android.support.v4.widget.SwipeRefreshLayout>

This is my onScroll method.

这是我的 onScroll 方法。

@Override
public void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount) {
    // If the total item count is zero and the previous isn't, assume the
    // list is invalidated and should be reset back to initial state
    if (totalItemCount < previousTotalItemCount) {
        this.currentPage = this.startingPageIndex;
        this.previousTotalItemCount = totalItemCount;
        if (totalItemCount == 0) { this.loading = true; } 
    }

    // If it's still loading, we check to see if the dataset count has
    // changed, if so we conclude it has finished loading and update the current page
    // number and total item count.
    if (loading && (totalItemCount > previousTotalItemCount)) {
        loading = false;
        previousTotalItemCount = totalItemCount;
        currentPage++;
    }

    // If reverse then the firstVisibleItem is calculated wrong
    if (reverse) {
        firstVisibleItem = totalItemCount - firstVisibleItem;
    }
    // If it isn't currently loading, we check to see if we have breached
    // the visibleThreshold and need to reload more data.
    // If we do need to reload some more data, we execute onLoadMore to fetch the data.
    if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) {
        onLoadMore(currentPage + 1, totalItemCount);
        loading = true;
    }
}

回答by Rishabh

Make your own implementation of SwipeRefreshLayout and override the canChildScrollUp in this way:

自己实现 SwipeRefreshLayout 并以这种方式覆盖 canChildScrollUp:

@Override
public boolean canChildScrollUp() {
if (scrollView != null)
    return scrollView.canScrollVertically(-1);

return false;
}

just replace with any subclass of ScrollView.

只需替换为 ScrollView 的任何子类。

回答by Rushikesh Gomekar

Create a layout like this one.

创建一个这样的布局。

<android.support.v4.widget.SwipeRefreshLayout  
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/guides_no_results"
        android:id="@+id/empty_view"
        android:gravity="center"
        android:padding="16dp"
        android:fontFamily="sans-serif-light"
        android:textSize="20sp"
        android:visibility="gone"/>


    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:drawSelectorOnTop="true"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:id="@+id/guides_list" />

</LinearLayout>

If you use this as-is, everytime you scroll up in that view, the SwipeRefreshLayout fires and updates, making your app unable to scroll up in a list.

如果您按原样使用它,则每次在该视图中向上滚动时,都会触发并更新 SwipeRefreshLayout,从而使您的应用无法在列表中向上滚动。

The trick here is to wire the OnScrollListener from the ListView manually. You just check if the first row being shown matches the first top-most position, and then enable the SwipeRefreshLayout. Otherwise, disable it.

这里的技巧是从 ListView 手动连接 OnScrollListener 。您只需检查显示的第一行是否与第一个最顶部位置匹配,然后启用 SwipeRefreshLayout。否则,禁用它。

guidesList.setOnScrollListener(new AbsListView.OnScrollListener()
{  
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState)
    {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
    {
        int topRowVerticalPosition = (guidesList == null || guidesList.getChildCount() == 0) ? 0 : guidesList.getChildAt(0).getTop();
        swipeContainer.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
    }
});

With this little snippet now it works perfectly.

有了这个小片段,它现在可以完美运行。

Happy coding!

快乐编码!

Source - http://nlopez.io/swiperefreshlayout-with-listview-done-right/

来源 - http://nlopez.io/swiperrefreshlayout-with-listview-done-right/

回答by Paul LeBeau

In order for SwipeRefreshLayout to work, it needs to be the direct parent of your ListView, and the ListView should be the first active child view of the SwipeRefreshLayout.

为了使 SwipeRefreshLayout 工作,它需要是 ListView 的直接父视图,并且 ListView 应该是 SwipeRefreshLayout 的第一个活动子视图。

The documentation for SwipeRefreshLayout says that the ListView should be the only child, but it is okay if it has more than one child as long as the ListView is first. This means, for instance, that SwipeRefreshLayout will work fine if you are using an adapter with a view for "empty". For example:

SwipeRefreshLayout 的文档说 ListView 应该是唯一的孩子,但只要 ListView 是第一个,如果它有多个孩子也没关系。这意味着,例如,如果您使用带有“空”视图的适配器,则 SwipeRefreshLayout 将正常工作。例如:

<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/swipe_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NearbyJobsActivity$PlaceholderFragment">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@color/list_divider"
        android:dividerHeight="1dp"
        android:listSelector="@drawable/list_row_selector" />

    <TextView
        android:id="@android:id/empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center" />

</android.support.v4.widget.SwipeRefreshLayout>

If you can manage this sort of layout, then SwipeRefreshLayout will work fine and you won't need any of the workarounds listed in other answers.

如果您可以管理这种布局,那么 SwipeRefreshLayout 将可以正常工作,并且您不需要其他答案中列出的任何解决方法。

My own problem was that that I was loading my ListView as a Fragment, so I actually had:

我自己的问题是我将 ListView 作为片段加载,所以我实际上有:

<SwipeRefreshLayout>

    <FrameLayout>           )
         <ListView/>        \ fragment
         <TextView/>        /
    </FrameLayout>          )

</SwipeRefreshLayout>

So the SwipeRefreshLayout was choosing the FrameLayout as it's "target" and its default canChildScrollUp()implementation was always returning false. Once I moved the SwipeRefreshLayout inside the Fragment, everything started working correctly.

所以 SwipeRefreshLayout 选择 FrameLayout 作为它的“目标”,它的默认canChildScrollUp()实现总是返回 false。一旦我将 SwipeRefreshLayout 移动到 Fragment 中,一切都开始正常工作。

<FrameLayout>

    <SwipeRefreshLayout>    )
         <ListView/>        \ fragment
         <TextView/>        /
    </SwipeRefreshLayout>   )

</FrameLayout>

回答by dg428

I had a similar problem where the child of my SwipeRefreshLayoutwas a FrameLayoutwhich had a TextViewand ListViewas children and when I scrolled up on the ListViewit would try to do a refresh.

我也有类似的问题,在我的孩子SwipeRefreshLayoutFrameLayout其中有一个TextViewListView儿童,当我滚动起来的ListView它会尝试做一个刷新。

I fixed it by using a custom FrameLayoutwhich overrides the canScrollVertically()method

我通过使用FrameLayout覆盖该canScrollVertically()方法的自定义来修复它

package com.wi.director.ui.common;

包 com.wi.director.ui.common;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;

/**
 * Created by devansh on 9/22/15.
 */
public class FrameLayoutForSwipeRefresh extends FrameLayout {

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

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

    public FrameLayoutForSwipeRefresh(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public boolean canScrollVertically(int direction) {
        if (super.canScrollVertically(direction)) {
            return true;
        }

        int cc = getChildCount();
        for (int i = 0; i < cc; i++) {
            if (getChildAt(i).canScrollVertically(direction)) {
                return true;
            }
        }

        return false;
    }
}

回答by Kevin Crain

Even simpler just set SwipeRefreshLayoutenabled iffirstVisibleItemor disabled if not:

更简单的只是设置SwipeRefreshLayout启用如果firstVisibleItem或禁用如果不是:

yourList.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override 
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        yourSwipeRefreshLayout.setEnabled(firstVisibleItem == 0);
    }
});

回答by the_dani

I had the same problem and solved it:

我遇到了同样的问题并解决了它:

listView = (ListView) findViewById(R.id.listView);
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
     public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (listView.getChildAt(0) != null) {
        swipeRefreshLayout.setEnabled(listView.getFirstVisiblePosition() == 0 && listView.getChildAt(0).getTop() == 0);
        }
    }
});

回答by Najaf Ali

My Gridview Start Working with Swipefreshlayout

我的 Gridview 开始使用 Swipefreshlayout

gridView.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override

        public void onScrollStateChanged(AbsListView view, int scrollState)

        {

        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (gridView.getChildAt(0) != null) {
                mrefreshlayout.setEnabled(gridView.getFirstVisiblePosition() == 0 && gridView.getChildAt(0).getTop() == 0);
            }

         }


       });

layout in xml

xml中的布局

<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/swiperefreshlayout"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <LinearLayout
        android:background="#ffffff"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <GridView
            android:background="#ffffff"
            android:gravity="center"
            android:verticalSpacing="2dp"
            android:horizontalSpacing="2dp"
            android:numColumns="2"
            android:id="@+id/grid_view"
            android:animateLayoutChanges="true"
            android:overScrollMode="always"
            android:scrollbars="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <TextView
            android:id="@+id/nofieldtv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/adlayout"
            android:textColor="#000000"
            android:gravity="center"
            android:visibility="gone"
            android:textSize="16sp"
            android:layout_below="@+id/headerLayout"
            android:layout_marginTop="2dp">

        </TextView>

    </LinearLayout>


</android.support.v4.widget.SwipeRefreshLayout>

回答by Adrian Grygutis

Basically, we want to scroll something scrollable. We can put all views in ScrollView.

基本上,我们想要滚动可滚动的内容。我们可以将所有视图都放在 ScrollView 中。

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

        </LinearLayout>
    </ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

But scrolling ListView may require some modifications - wrap_content ListView

但是滚动 ListView 可能需要一些修改 - wrap_content ListView

回答by Dwight

For Xamarin I had the same issue (Scroll up does not work with SwipeRefreshLayout in Listview) and implemented it this way.

对于 Xamarin,我遇到了同样的问题(向上滚动不适用于 Listview 中的 SwipeRefreshLayout)并以这种方式实现。

public void OnScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

public void OnScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    }

    public void OnScrollStateChanged(AbsListView view, [GeneratedEnum] ScrollState scrollState)
    {
        if(listView.GetChildAt(0) != null)
        {
            swipeRefreshLayout.Enabled=listView.FirstVisiblePosition==0 && listView.GetChildAt(0).Top==0;
        }
    }