Android 无尽列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1080811/
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-20 02:40:07  来源:igfitidea点击:

Android Endless List

androidlistlistview

提问by Isaac Waller

How can I create a list where when you reach the end of the list I am notified so I can load more items?

如何创建一个列表,当您到达列表末尾时,我会收到通知,以便我可以加载更多项目?

回答by Josef Pfleger

One solution is to implement an OnScrollListenerand make changes (like adding items, etc.) to the ListAdapterat a convenient state in its onScrollmethod.

一种解决方案是在其方法中实现一个方便的状态并对其OnScrollListener进行更改(如添加项目等)。ListAdapteronScroll

The following ListActivityshows a list of integers, starting with 40, adding items when the user scrolls to the end of the list.

下面ListActivity显示了一个整数列表,从 40 开始,当用户滚动到列表末尾时添加项目。

public class Test extends ListActivity implements OnScrollListener {

    Aleph0 adapter = new Aleph0();

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setListAdapter(adapter); 
        getListView().setOnScrollListener(this);
    }

    public void onScroll(AbsListView view,
        int firstVisible, int visibleCount, int totalCount) {

        boolean loadMore = /* maybe add a padding */
            firstVisible + visibleCount >= totalCount;

        if(loadMore) {
            adapter.count += visibleCount; // or any other amount
            adapter.notifyDataSetChanged();
        }
    }

    public void onScrollStateChanged(AbsListView v, int s) { }    

    class Aleph0 extends BaseAdapter {
        int count = 40; /* starting amount */

        public int getCount() { return count; }
        public Object getItem(int pos) { return pos; }
        public long getItemId(int pos) { return pos; }

        public View getView(int pos, View v, ViewGroup p) {
                TextView view = new TextView(Test.this);
                view.setText("entry " + pos);
                return view;
        }
    }
}

You should obviously use separate threads for long running actions (like loading web-data) and might want to indicate progress in the last list item (like the market or gmail apps do).

对于长时间运行的操作(例如加载网络数据),您显然应该使用单独的线程,并且可能希望指示最后一个列表项的进度(例如市场或 gmail 应用程序所做的)。

回答by saschoar

Just wanted to contribute a solution that I used for my app.

只是想贡献一个我用于我的应用程序的解决方案。

It is also based on the OnScrollListenerinterface, but I found it to have a much better scrolling performance on low-end devices, since none of the visible/total count calculations are carried out during the scroll operations.

它也是基于OnScrollListener界面的,但我发现它在低端设备上具有更好的滚动性能,因为在滚动操作期间没有执行任何可见/总计数计算。

  1. Let your ListFragmentor ListActivityimplement OnScrollListener
  2. Add the following methods to that class:

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        //leave this empty
    }
    
    @Override
    public void onScrollStateChanged(AbsListView listView, int scrollState) {
        if (scrollState == SCROLL_STATE_IDLE) {
            if (listView.getLastVisiblePosition() >= listView.getCount() - 1 - threshold) {
                currentPage++;
                //load more list items:
                loadElements(currentPage);
            }
        }
    }
    

    where currentPageis the page of your datasource that should be added to your list, and thresholdis the number of list items (counted from the end) that should, if visible, trigger the loading process. If you set thresholdto 0, for instance, the user has to scroll to the very end of the list in order to load more items.

  3. (optional)As you can see, the "load-more check" is only called when the user stops scrolling. To improve usability, you may inflate and add a loading indicator to the end of the list via listView.addFooterView(yourFooterView). One example for such a footer view:

    <?xml version="1.0" encoding="utf-8"?>
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/footer_layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dp" >
    
        <ProgressBar
            android:id="@+id/progressBar1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_gravity="center_vertical" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/progressBar1"
            android:padding="5dp"
            android:text="@string/loading_text" />
    
    </RelativeLayout>
    
  4. (optional)Finally, remove that loading indicator by calling listView.removeFooterView(yourFooterView)if there are no more items or pages.

  1. 让您ListFragmentListActivity实施OnScrollListener
  2. 将以下方法添加到该类:

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        //leave this empty
    }
    
    @Override
    public void onScrollStateChanged(AbsListView listView, int scrollState) {
        if (scrollState == SCROLL_STATE_IDLE) {
            if (listView.getLastVisiblePosition() >= listView.getCount() - 1 - threshold) {
                currentPage++;
                //load more list items:
                loadElements(currentPage);
            }
        }
    }
    

    哪里currentPage是应该添加到列表中的数据源页面,threshold是应该(如果可见)触发加载过程的列表项数(从末尾开始计数)。如果设置threshold0,例如,用户滚动到列表的最末端,以装载更多的物品。

  3. (可选)如您所见,“加载更多检查”仅在用户停止滚动时调用。为了提高可用性,您可以通过listView.addFooterView(yourFooterView). 这种页脚视图的一个示例:

    <?xml version="1.0" encoding="utf-8"?>
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/footer_layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dp" >
    
        <ProgressBar
            android:id="@+id/progressBar1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_gravity="center_vertical" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/progressBar1"
            android:padding="5dp"
            android:text="@string/loading_text" />
    
    </RelativeLayout>
    
  4. (可选)最后,listView.removeFooterView(yourFooterView)如果没有更多项目或页面,则通过调用来删除该加载指示器。

回答by Dariusz Bacinski

You can detect end of the list with help of onScrollListener, working code is presented below:

您可以在onScrollListener 的帮助下检测列表的末尾,工作代码如下所示:

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    if (view.getAdapter() != null && ((firstVisibleItem + visibleItemCount) >= totalItemCount) && totalItemCount != mPrevTotalItemCount) {
        Log.v(TAG, "onListEnd, extending list");
        mPrevTotalItemCount = totalItemCount;
        mAdapter.addMoreData();
    }
}

Another way to do that (inside adapter) is as following:

另一种方法(内部适配器)如下:

    public View getView(int pos, View v, ViewGroup p) {
            if(pos==getCount()-1){
                addMoreData(); //should be asynctask or thread
            }
            return view;
    }

Be aware that this method will be called many times, so you need to add another condition to block multiple calls of addMoreData().

注意这个方法会被多次调用,所以需要再添加一个条件来阻止多次调用addMoreData().

When you add all elements to the list, please call notifyDataSetChanged()inside yours adapter to update the View (it should be run on UI thread - runOnUiThread)

当您将所有元素添加到列表中时,请在您的适配器内调用notifyDataSetChanged()以更新视图(它应该在 UI 线程上运行 - runOnUiThread

回答by oikonomopo

At Ognyan Bankov GitHubi found a simple and working solution!

Ognyan Bankov GitHub我找到了一个简单而有效的解决方案!

It makes use of the Volley HTTP librarythat makes networking for Android apps easier and most importantly, faster. Volley is available through the open AOSP repository.

它利用Volley HTTP library使 Android 应用程序的网络更容易,最重要的是,更快。Volley 可通过开放的 AOSP 存储库获得。

The given code demonstrates:

给定的代码演示:

  1. ListView which is populated by HTTP paginated requests.
  2. Usage of NetworkImageView.
  3. "Endless" ListView pagination with read-ahead.
  1. 由 HTTP 分页请求填充的 ListView。
  2. NetworkImageView 的使用。
  3. 带有预读功能的“无尽”ListView 分页。

For future consistence i forked Bankov's repo.

为了将来的一致性,我分叉了 Bankov 的 repo

回答by Fernando Camargo

Here is a solution that also makes it easy to show a loading view in the end of the ListView while it's loading.

这是一个解决方案,它也可以在加载时轻松地在 ListView 的末尾显示加载视图。

You can see the classes here:

您可以在此处查看课程:

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java- Helper to make it possible to use the features without extending from SimpleListViewWithLoadingIndicator.

https://github.com/Cyber​​Eagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java- 可以使用不从 SimpleListViewWithLoadingIndicator 扩展的功能。

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java- Listener that starts loading data when the user is about to reach the bottom of the ListView.

https://github.com/Cyber​​Eagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java- 当用户开始加载数据的监听器即将到达ListView的底部。

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java- The EndlessListView. You can use this class directly or extend from it.

https://github.com/Cyber​​Eagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java- EndlessListView。您可以直接使用这个类或从它扩展。

回答by tehcpu

May be a little late but the following solution happened very useful in my case. In a way all you need to do is add to your ListView a Footerand create for it addOnLayoutChangeListener.

可能有点晚了,但以下解决方案对我来说非常有用。在某种程度上,您需要做的就是添加到您的 ListView aFooter并为其创建addOnLayoutChangeListener

http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)

http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)

For example:

例如:

ListView listView1 = (ListView) v.findViewById(R.id.dialogsList); // Your listView
View loadMoreView = getActivity().getLayoutInflater().inflate(R.layout.list_load_more, null); // Getting your layout of FooterView, which will always be at the bottom of your listview. E.g. you may place on it the ProgressBar or leave it empty-layout.
listView1.addFooterView(loadMoreView); // Adding your View to your listview 

...

loadMoreView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
         Log.d("Hey!", "Your list has reached bottom");
    }
});

This event fires once when a footer becomes visible and works like a charm.

当页脚变得可见并且像魅力一样工作时,此事件会触发一次。

回答by Shubham Chaudhary

Best solution so far that I have seen is in FastAdapterlibrary for recycler views. It has a EndlessRecyclerOnScrollListener.

到目前为止,我看到的最佳解决方案是在FastAdapter库中用于回收器视图。它有一个EndlessRecyclerOnScrollListener.

Here is an example usage: EndlessScrollListActivity

这是一个示例用法:EndlessScrollListActivity

Once I used it for endless scrolling list I have realised that the setup is a very robust. I'd definitely recommend it.

一旦我将它用于无休止的滚动列表,我就意识到该设置非常强大。我肯定会推荐它。

回答by gbenroscience

I know its an old question and the Android world has mostly moved on to RecyclerViews, but for anyone interested, you may find thislibrary very interesting.

我知道这是一个老问题,Android 世界主要转向 RecyclerViews,但对于任何感兴趣的人,您可能会发现这个库非常有趣。

It uses the BaseAdapter used with the ListView to detect when the list has been scrolled to the last item or when it is being scrolled away from the last item.

它使用与 ListView 一起使用的 BaseAdapter 来检测列表何时滚动到最后一个项目或何时从最后一个项目滚动离开。

It comes with an example project(barely 100 lines of Activity code) that can be used to quickly understand how it works.

它附带一个示例项目(仅 100 行 Activity 代码),可用于快速了解其工作原理。

Simple usage:

简单用法:

class Boy{

private String name;
private double height;
private int age;
//Other code

}

An adapter to hold Boy objects would look like:

用于保存 Boy 对象的适配器如下所示:


public class BoysAdapter extends EndlessAdapter<Boy>{




        ViewHolder holder = null;


        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent
                    .getContext());

            holder = new ViewHolder();

            convertView = inflater.inflate(
                    R.layout.list_cell, parent, false);


            holder.nameView = convertView.findViewById(R.id.cell);

            // minimize the default image.
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Boy boy = getItem(position);

        try {
            holder.nameView.setText(boy.getName());

            ///Other data rendering codes.

        } catch (Exception e) {
            e.printStackTrace();
        }

        return super.getView(position,convertView,parent);

}

Notice how the BoysAdapter's getView method returns a call to the EndlessAdapter superclass's getViewmethod. This is 100% essential.

请注意 BoysAdapter 的 getView 方法如何返回对 EndlessAdapter 超类getView方法的调用。这是 100% 必不可少的。

Now to create the adapter, do:

现在要创建适配器,请执行以下操作:

   adapter = new ModelAdapter() {
            @Override
            public void onScrollToBottom(int bottomIndex, boolean moreItemsCouldBeAvailable) {

                if (moreItemsCouldBeAvailable) { 
                    makeYourServerCallForMoreItems();
                } else {
                    if (loadMore.getVisibility() != View.VISIBLE) {
                        loadMore.setVisibility(View.VISIBLE);
                    }
                }
            }

            @Override
            public void onScrollAwayFromBottom(int currentIndex) { 
                loadMore.setVisibility(View.GONE);
            }

            @Override
            public void onFinishedLoading(boolean moreItemsReceived) { 
                if (!moreItemsReceived) {
                    loadMore.setVisibility(View.VISIBLE);
                }
            }
        };

The loadMoreitem is a button or other ui element that may be clicked to fetch more data from the url. When placed as described in the code, the adapter knows exactly when to show that button and when to disable it. Just create the button in your xml and place it as shown in the adapter code above.

loadMore项目是一个按钮或其他 ui 元素,可以单击以从 url 获取更多数据。当按照代码中的描述放置时,适配器确切地知道何时显示该按钮以及何时禁用它。只需在您的 xml 中创建按钮并将其放置在上面的适配器代码中。

Enjoy.

享受。

回答by ProtossShuttle

The key of this problem is to detect the load-more event, start an async request for data and then update the list. Also an adapter with loading indicator and other decorators is needed. In fact, the problem is very complicated in some corner cases. Just a OnScrollListenerimplementation is not enough, because sometimes the items do not fill the screen.

这个问题的关键是检测load-more事件,发起数据的异步请求,然后更新列表。还需要一个带有加载指示器和其他装饰器的适配器。事实上,在某些极端情况下,问题是非常复杂的。仅仅OnScrollListener实现是不够的,因为有时项目不会填满屏幕。

I have written a personal package which support endless list for RecyclerView, and also provide a async loader implementation AutoPagerFragmentwhich makes it very easy to get data from a multi-page source. It can load any page you want into a RecyclerViewon a custom event, not only the next page.

我已经编写了一个支持无限列表的个人包RecyclerView,并且还提供了一个异步加载器实现AutoPagerFragment,这使得从多页源获取数据变得非常容易。它可以将您想要的任何页面加载到RecyclerView自定义事件中,而不仅仅是下一页。

Here is the address: https://github.com/SphiaTower/AutoPagerRecyclerManager

这里是地址:https: //github.com/SphiaTower/AutoPagerRecyclerManager

回答by pabloverd

I've been working in another solution very similar to that, but, I am using a footerViewto give the possibility to the user download more elements clicking the footerView, I am using a "menu" which is shown above the ListViewand in the bottom of the parent view, this "menu" hides the bottom of the ListView, so, when the listViewis scrolling the menu disappear and when scroll state is idle, the menu appear again, but when the user scrolls to the end of the listView, I "ask" to know if the footerViewis shown in that case, the menu doesn't appear and the user can see the footerViewto load more content. Here the code:

我一直在研究另一个与此非常相似的解决方案,但是,我使用 afooterView为用户提供点击 下载更多元素的可能性footerView,我使用的是显示在 上方ListView和底部的“菜单”父视图,这个“菜单”隐藏了 的底部ListView,因此,当listView滚动菜单消失并且滚动状态空闲时,菜单再次出现,但是当用户滚动到 的末尾时listView,我“要求”知道footerView在这种情况下是否显示 ,菜单不会出现,用户可以看到footerView加载更多内容。这里的代码:

Regards.

问候。

listView.setOnScrollListener(new OnScrollListener() {

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub
        if(scrollState == SCROLL_STATE_IDLE) {
            if(footerView.isShown()) {
                bottomView.setVisibility(LinearLayout.INVISIBLE);
            } else {
                bottomView.setVisibility(LinearLayout.VISIBLE);
            } else {
                bottomView.setVisibility(LinearLayout.INVISIBLE);
            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {

    }
});