Android How to implement endless list with RecyclerView?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26543131/
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
How to implement endless list with RecyclerView?
提问by erdna
I would like to change ListView
to RecyclerView
. I want to use the onScroll
of the OnScrollListener
in RecyclerView
to determine if a user scrolled to the end of the list.
I would like to change ListView
to RecyclerView
. I want to use the onScroll
of the OnScrollListener
in RecyclerView
to determine if a user scrolled to the end of the list.
How do I know if a user scrolls to the end of the list so that I can fetch new data from a REST service?
How do I know if a user scrolls to the end of the list so that I can fetch new data from a REST service?
回答by Abdulaziz Noor
Thanks to @Kushal and this is how I implemented it
Thanks to @Kushal and this is how I implemented it
private boolean loading = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) { //check for scroll down
visibleItemCount = mLayoutManager.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
loading = false;
Log.v("...", "Last Item Wow !");
// Do pagination.. i.e. fetch new data
}
}
}
}
});
Don't forget to add
Don't forget to add
LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
回答by Kushal Sharma
Make these variables.
Make these variables.
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;
Set on Scroll for recycler view.
Set on Scroll for recycler view.
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = mRecyclerView.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
Log.i("Yaeye!", "end called");
// Do something
loading = true;
}
}
});
Note :Make sure you are using
LinearLayoutManager
as layout manager forRecyclerView
.
Note :Make sure you are using
LinearLayoutManager
as layout manager forRecyclerView
.
LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
and for a grid
and for a grid
GridLayoutManager mLayoutManager;
mLayoutManager = new GridLayoutManager(getActivity(), spanCount);
mRecyclerView.setLayoutManager(mLayoutManager);
Have fun with your endless scrolls !! ^.^
Have fun with your endless scrolls !! ^.^
Update :mRecyclerView.
setOnScrollListener()is deprecated just replace withmRecyclerView.addOnScrollListener()
and the warning will be gone! You can read more from this SO question.
Update :mRecyclerView.
setOnScrollListener()is deprecated just replace withmRecyclerView.addOnScrollListener()
and the warning will be gone! You can read more from this SO question.
Since Android now officially support Kotlin, here is an update for the same -
Since Android now officially support Kotlin, here is an update for the same -
Make OnScrollListener
Make OnScrollListener
class OnScrollListener(val layoutManager: LinearLayoutManager, val adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>, val dataList: MutableList<Int>) : RecyclerView.OnScrollListener() {
var previousTotal = 0
var loading = true
val visibleThreshold = 10
var firstVisibleItem = 0
var visibleItemCount = 0
var totalItemCount = 0
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
visibleItemCount = recyclerView.childCount
totalItemCount = layoutManager.itemCount
firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
if (loading) {
if (totalItemCount > previousTotal) {
loading = false
previousTotal = totalItemCount
}
}
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
val initialSize = dataList.size
updateDataList(dataList)
val updatedSize = dataList.size
recyclerView.post { adapter.notifyItemRangeInserted(initialSize, updatedSize) }
loading = true
}
}
}
and add it to your RecyclerView like this
and add it to your RecyclerView like this
recyclerView.addOnScrollListener(OnScrollListener(layoutManager, adapter, dataList))
For a full code example, feel free to refer this Github repo.
For a full code example, feel free to refer this Github repo.
回答by Hai Zhang
For those who only want to get notified when the last item is totally shown, you can use View.canScrollVertically()
.
For those who only want to get notified when the last item is totally shown, you can use View.canScrollVertically()
.
Here is my implementation:
Here is my implementation:
public abstract class OnVerticalScrollListener
extends RecyclerView.OnScrollListener {
@Override
public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (!recyclerView.canScrollVertically(-1)) {
onScrolledToTop();
} else if (!recyclerView.canScrollVertically(1)) {
onScrolledToBottom();
} else if (dy < 0) {
onScrolledUp();
} else if (dy > 0) {
onScrolledDown();
}
}
public void onScrolledUp() {}
public void onScrolledDown() {}
public void onScrolledToTop() {}
public void onScrolledToBottom() {}
}
Note: You can use recyclerView.getLayoutManager().canScrollVertically()
if you want to support API < 14.
Note: You can use recyclerView.getLayoutManager().canScrollVertically()
if you want to support API < 14.
回答by Sabeer Mohammed
Here is another approach. It will work with any layout manager.
Here is another approach. It will work with any layout manager.
- Make Adapter class abstract
- Then create an abstract method in adapter class (eg. load())
- In onBindViewHoldercheck the position if last and call load()
- Override the load() function while creating the adapter object in your activity or fragment.
- In the overided load function implement your loadmore call
- Make Adapter class abstract
- Then create an abstract method in adapter class (eg. load())
- In onBindViewHoldercheck the position if last and call load()
- Override the load() function while creating the adapter object in your activity or fragment.
- In the overided load function implement your loadmore call
For a detail understanding I wrote a blog post and example project get it here http://sab99r.com/blog/recyclerview-endless-load-more/
For a detail understanding I wrote a blog post and example project get it here http://sab99r.com/blog/recyclerview-endless-load-more/
MyAdapter.java
MyAdapter.java
public abstract class MyAdapter extends RecyclerView.Adapter<ViewHolder>{
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
//check for last item
if ((position >= getItemCount() - 1))
load();
}
public abstract void load();
}
MyActivity.java
MyActivity.java
public class MainActivity extends AppCompatActivity {
List<Items> items;
MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
adapter=new MyAdapter(items){
@Override
public void load() {
//implement your load more here
Item lastItem=items.get(items.size()-1);
loadMore();
}
};
}
}
回答by Federico Ponzi
My answer is a modified version of Noor. I passed from a ListView
where i had EndlessScrollListener
(that you can find easily in many answers on SO) to a RecyclerView
so i wanted a EndlessRecyclScrollListener
to easily update my past listener.
My answer is a modified version of Noor. I passed from a ListView
where i had EndlessScrollListener
(that you can find easily in many answers on SO) to a RecyclerView
so i wanted a EndlessRecyclScrollListener
to easily update my past listener.
So here is the code, hope it helps:
So here is the code, hope it helps:
public abstract class EndlessScrollRecyclListener extends RecyclerView.OnScrollListener
{
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;
private int startingPageIndex = 0;
private int currentPage = 0;
@Override
public void onScrolled(RecyclerView mRecyclerView, int dx, int dy)
{
super.onScrolled(mRecyclerView, dx, dy);
LinearLayoutManager mLayoutManager = (LinearLayoutManager) mRecyclerView
.getLayoutManager();
visibleItemCount = mRecyclerView.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
onScroll(firstVisibleItem, visibleItemCount, totalItemCount);
}
public void onScroll(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 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;
}
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount);
}
回答by Minh Nguyen
For me, it's very simple:
For me, it's very simple:
private boolean mLoading = false;
mList.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int totalItem = mLinearLayoutManager.getItemCount();
int lastVisibleItem = mLinearLayoutManager.findLastVisibleItemPosition();
if (!mLoading && lastVisibleItem == totalItem - 1) {
mLoading = true;
// Scrolled to bottom. Do something here.
mLoading = false;
}
}
});
Be careful with asynchronous jobs: mLoading must be changed at the end of the asynchronous jobs. Hope it will be helpful!
Be careful with asynchronous jobs: mLoading must be changed at the end of the asynchronous jobs. Hope it will be helpful!
回答by Yairopro
Most answer are assuming the RecyclerView
uses a LinearLayoutManager
, or GridLayoutManager
, or even StaggeredGridLayoutManager
, or assuming that the scrolling is vertical or horyzontal, but no one has posted a completly generic answer.
Most answer are assuming the RecyclerView
uses a LinearLayoutManager
, or GridLayoutManager
, or even StaggeredGridLayoutManager
, or assuming that the scrolling is vertical or horyzontal, but no one has posted a completly generic answer.
Using the ViewHolder
's adapter is clearly not a good solution. An adapter might have more than 1 RecyclerView
using it. It "adapts" their contents. It should be the RecyclerView(which is the one class which is responsible of what is currently displayed to the user, and not the adapter which is responsible only to provide content to the RecyclerView
) which must notify your systemthat more items are needed (to load).
Using the ViewHolder
's adapter is clearly not a good solution. An adapter might have more than 1 RecyclerView
using it. It "adapts" their contents. It should be the RecyclerView(which is the one class which is responsible of what is currently displayed to the user, and not the adapter which is responsible only to provide content to the RecyclerView
) which must notify your systemthat more items are needed (to load).
Here is my solution, using nothing else than the abstracted classes of the RecyclerView (RecycerView.LayoutManager and RecycerView.Adapter):
Here is my solution, using nothing else than the abstracted classes of the RecyclerView (RecycerView.LayoutManager and RecycerView.Adapter):
/**
* Listener to callback when the last item of the adpater is visible to the user.
* It should then be the time to load more items.
**/
public abstract class LastItemListener extends RecyclerView.OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// init
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (layoutManager.getChildCount() > 0) {
// Calculations..
int indexOfLastItemViewVisible = layoutManager.getChildCount() -1;
View lastItemViewVisible = layoutManager.getChildAt(indexOfLastItemViewVisible);
int adapterPosition = layoutManager.getPosition(lastItemViewVisible);
boolean isLastItemVisible = (adapterPosition == adapter.getItemCount() -1);
// check
if (isLastItemVisible)
onLastItemVisible(); // callback
}
}
/**
* Here you should load more items because user is seeing the last item of the list.
* Advice: you should add a bollean value to the class
* so that the method {@link #onLastItemVisible()} will be triggered only once
* and not every time the user touch the screen ;)
**/
public abstract void onLastItemVisible();
}
// --- Exemple of use ---
myRecyclerView.setOnScrollListener(new LastItemListener() {
public void onLastItemVisible() {
// start to load more items here.
}
}
回答by capt.swag
Although the accepted answer works perfectly, the solution below uses addOnScrollListener since setOnScrollListener is deprecated, and reduces number of variables, and if conditions.
Although the accepted answer works perfectly, the solution below uses addOnScrollListener since setOnScrollListener is deprecated, and reduces number of variables, and if conditions.
final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
feedsRecyclerView.setLayoutManager(layoutManager);
feedsRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
if ((layoutManager.getChildCount() + layoutManager.findFirstVisibleItemPosition()) >= layoutManager.getItemCount()) {
Log.d("TAG", "End of list");
//loadMore();
}
}
}
});
回答by Vladimir Tchernitski
Although there are so many answers to the question, I would like to share our experience of creating the endless list view. We have recently implemented custom Carousel LayoutManager that can work in the cycle by scrolling the list infinitely as well as up to a certain point. Here is a detailed description on GitHub.
Although there are so many answers to the question, I would like to share our experience of creating the endless list view. We have recently implemented custom Carousel LayoutManager that can work in the cycle by scrolling the list infinitely as well as up to a certain point. Here is a detailed description on GitHub.
I suggest you take a look at this article with short but valuable recommendations on creating custom LayoutManagers: http://cases.azoft.com/create-custom-layoutmanager-android/
I suggest you take a look at this article with short but valuable recommendations on creating custom LayoutManagers: http://cases.azoft.com/create-custom-layoutmanager-android/
回答by John T
This is how I do it, simple and short:
This is how I do it, simple and short:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
{
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
if(!recyclerView.canScrollVertically(1) && dy != 0)
{
// Load more results here
}
}
});