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
Android Endless List
提问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 OnScrollListener
and make changes (like adding items, etc.) to the ListAdapter
at a convenient state in its onScroll
method.
一种解决方案是在其方法中实现一个方便的状态并对其OnScrollListener
进行更改(如添加项目等)。ListAdapter
onScroll
The following ListActivity
shows 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 OnScrollListener
interface, 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
界面的,但我发现它在低端设备上具有更好的滚动性能,因为在滚动操作期间没有执行任何可见/总计数计算。
- Let your
ListFragment
orListActivity
implementOnScrollListener
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
currentPage
is the page of your datasource that should be added to your list, andthreshold
is the number of list items (counted from the end) that should, if visible, trigger the loading process. If you setthreshold
to0
, for instance, the user has to scroll to the very end of the list in order to load more items.(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>
(optional)Finally, remove that loading indicator by calling
listView.removeFooterView(yourFooterView)
if there are no more items or pages.
- 让您
ListFragment
或ListActivity
实施OnScrollListener
将以下方法添加到该类:
@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
是应该(如果可见)触发加载过程的列表项数(从末尾开始计数)。如果设置threshold
到0
,例如,用户滚动到列表的最末端,以装载更多的物品。(可选)如您所见,“加载更多检查”仅在用户停止滚动时调用。为了提高可用性,您可以通过
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>
(可选)最后,
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 GitHub
i found a simple and working solution!
在Ognyan Bankov GitHub
我找到了一个简单而有效的解决方案!
It makes use of the Volley HTTP library
that 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:
给定的代码演示:
- ListView which is populated by HTTP paginated requests.
- Usage of NetworkImageView.
- "Endless" ListView pagination with read-ahead.
- 由 HTTP 分页请求填充的 ListView。
- NetworkImageView 的使用。
- 带有预读功能的“无尽”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/CyberEagle/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/CyberEagle/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/CyberEagle/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 Footer
and 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 getView
method. 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 loadMore
item 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 OnScrollListener
implementation 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 AutoPagerFragment
which makes it very easy to get data from a multi-page source. It can load any page you want into a RecyclerView
on 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 footerView
to give the possibility to the user download more elements clicking the footerView
, I am using a "menu" which is shown above the ListView
and in the bottom of the parent view, this "menu" hides the bottom of the ListView
, so, when the listView
is 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 footerView
is shown in that case, the menu doesn't appear and the user can see the footerView
to 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) {
}
});