Recyclerview StaggeredGridLayoutManager - Find last visible item - android

I am using a Recyclerview with StaggeredGridLayoutManager with Endless Scroll. I want to do a network call when the last item of the list is visible to the user. So here is my code:
public RecyclerViewAdapter(Context context) {
Log.d(Const.DEBUG, "RecyclerViewAdapter Constructor()");
this.context = context;
final StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = staggeredGridLayoutManager.getItemCount();
lastVisibleItems = staggeredGridLayoutManager.findLastVisibleItemPositions(new int[mStaggeredLayoutManager.getSpanCount()]);
Log.d(Const.DEBUG, "LastVisibleItems: " + Arrays.toString(lastVisibleItems));
Log.d(Const.DEBUG, "LastVisibleItems Count: " + lastVisibleItems.length);
if (staggeredGridLayoutManager.getSpanCount() == 1) {
lastVisibleItem = lastVisibleItems[0];
} else if (staggeredGridLayoutManager.getSpanCount() == 2) {
lastVisibleItem = Math.max(lastVisibleItems[0], lastVisibleItems[1]);
} else if (staggeredGridLayoutManager.getSpanCount() == 3) {
lastVisibleItem = Math.max(Math.max(lastVisibleItems[0], lastVisibleItems[1]), lastVisibleItems[2]);
}
if (!isRefreshing && (totalItemCount <= lastVisibleItem + visibleThreshold)) {
Log.d(Const.DEBUG, "isRefreshing: " + isRefreshing);
Log.d(Const.DEBUG, "totalItemCount: " + totalItemCount);
Log.d(Const.DEBUG, "lastVisibileItem: " + lastVisibleItem);
Log.d(Const.DEBUG, "visibileThreshold: " + visibleThreshold);
Log.d(Const.DEBUG, "calling LoadMore()");
if (mIOnLoadMoreListener != null) {
mIOnLoadMoreListener.onLoadMore();
}
isRefreshing = true;
}
}
});
}
I get 9 values from server on first call. So, when i move to the last index[8], the lastVisibleItem will be 8 and visibleThreshold is 1, totalItemCount is 9 and so the next network call should happen. What is actually happening now, is, when the screen loads for the first time, the lastVisibleItem should be 1 or 2, but its returning 8, and as a result, the loadMore() is getting called.
Logcat:
D/xx: Items Count: 9
D/xx: LastVisibleItems: [8]
03-22 15:22:52.772 5957-5957/codsiga.com.xx D/xx: LastVisibleItems Count: 1
03-22 15:22:52.772 5957-5957/codsiga.com.xx D/xx: isRefreshing: false
03-22 15:22:52.772 5957-5957/codsiga.com.xx D/xx: totalItemCount: 9
03-22 15:22:52.772 5957-5957/codsiga.com.xx D/xx: lastVisibileItem: 8
03-22 15:22:52.772 5957-5957/codsiga.com.xx D/xx: visibileThershold: 1
03-22 15:22:52.772 5957-5957/codsiga.com.xx D/xx: calling LoadMore()
03-22 15:22:52.772 5957-5957/codsiga.com.xx D/xx: onLoadMore()
03-22 15:22:52.798 5957-5957/codsiga.com.xx D/xx: getDataFromServer()
What is wrong in the above code? Let me know if you need anything else. The same code worked well before.

Try this.
private int pastVisiblesItems, visibleItemCount, totalItemCount;
private boolean loading = true;
private StaggeredGridLayoutManager layoutManager;
private RecyclerView recyclerView ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
layoutManager=new StaggeredGridLayoutManager(2,1);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(layoutManager);
recycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) //for vertical scrolling
{
} else if (dy < 0) {
}
if (dx > 0)//for horizontal scrolling
{
visibleItemCount = layoutManager.getChildCount();
totalItemCount = layoutManager.getItemCount();
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
if (loading) {
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
loading = false;
loadMore(); //desired function call
}
}
}
}
});
}
Please don't forget to make loading=true after the success of API calling.
Hope it may help..

following code is work for me :
//return true if it's last item visited
private boolean isLastItemDisplaying(RecyclerView recyclerView) {
if (recyclerView.getAdapter().getItemCount() != 0) {
//int lastVisibleItemPosition = ((StaggeredGridLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPositions();
int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) Objects.requireNonNull(recyclerView.getLayoutManager())).findLastVisibleItemPositions(null);
// get maximum element within the list
int lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
return lastVisibleItemPosition != RecyclerView.NO_POSITION && lastVisibleItemPosition == recyclerView.getAdapter().getItemCount() - 1;
}
return false;
}
//get last item
public int getLastVisibleItem(int[] lastVisibleItemPositions) {
int maxSize = 0;
for (int i = 0; i < lastVisibleItemPositions.length; i++) {
if (i == 0) {
maxSize = lastVisibleItemPositions[i];
} else if (lastVisibleItemPositions[i] > maxSize) {
maxSize = lastVisibleItemPositions[i];
}
}
return maxSize;
}

private void recyclerViewOnScroll(RecyclerView recyclerView, final StaggeredGridLayoutManager staggeredGridLayoutManager) {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
totalItemCount = staggeredGridLayoutManager.getItemCount();
if (lastPositions == null)
lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
lastPositions = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(lastPositions);
lastVisibleItem = Math.max(lastPositions[0], lastPositions[1]);//findMax(lastPositions);
if (!loading && totalItemCount >= 20 && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
}
});
}

Related

RecyclerView call LoadMore pagination continuously

I'm trying to implement pagination using code below, and it work well with fragments, but when i implemented it in Nested Fragment it load all data (load first page and all next pages continuously). The problem is that my total items is 12 and visible in time is 3 but layoutManager.getChildCount() give me 12 same as total item (layoutManager.getItemCount()) so it start loading next pages continuously.
Here is oNScroll code :
public PaginationScrollListener(LinearLayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0
&& totalItemCount >= getTotalPageCount()) {
loadMoreItems();
}
}
}
When scroll and request next pages :
recyclerView.addOnScrollListener(new PaginationScrollListener(layoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
// mocking network delay for API call
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadNextPage();
}
}, 1000);
}
#Override
public int getTotalPageCount() {
return TOTAL_PAGES;
}
#Override
public boolean isLastPage() {
return isLastPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
change your scroll listener like below:
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(dy>0){ //if is scrolling vertically
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()) {
if (firstVisibleItemPosition + visibleItemCount >= totalItemCount) {
//make your request here
}
}
}});

recyclerview and pagination scrolling issue

I am using recycler view pagination to show my data. I am loading one page api data by default, for next page i am loading data on the scroll basis of the recycler view. But scroll is not getting called as i can see only 1 page. If i put the condition in on scroll for increasing page no. then on scroll method its not stopping & its calling api till last page in background. Meaning on scroll is calling again and again that I don't want. I want to scroll on basis of current visible items.
live_grid.addOnScrollListener(new PaginationScrollListener(gridLayoutManager) {
#Override
protected void loadMoreItems() {
// TODO: 07/11/16 check if totalPage > 1 before triggering next load
Log.d(TAG, "live_grid loadMoreItems.......");
int visibleItemCount = gridLayoutManager.getChildCount();
int totalItemCount = gridLayoutManager.getItemCount();
int firstVisibleItemPosition = gridLayoutManager.findFirstVisibleItemPosition();
Log.d(TAG, "visibleItemCount: "+ visibleItemCount);
Log.d(TAG, "totalItemCount: "+totalItemCount);
Log.d(TAG, "firstVisibleItemPosition: "+firstVisibleItemPosition);
Log.d(TAG, "currentPage: "+currentPage);
Log.d(TAG, "PAGE_SIZE: "+PAGE_SIZE);
if (!isLoading && !isLastPage) {
// if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount && firstVisibleItemPosition >= 0) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount && firstVisibleItemPosition >= 0 && currentPage < (PAGE_SIZE)) {
//&& totalItemCount >= PAGE_SIZE
firstVisibleItemPosition = visibleItemCount;
loadMoreItems1();
}
}
// presenter.setIsLoading(true);
// presenter.setCurrentPage(presenter.getCurrentPage() + 1);
// presenter.loadNextPlaylist();
}
Try this code..
protected int pastVisibleItems, visibleItemCount, totalItemCount; // define class level..
protected void addScrollListener() {
rvData.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) //check for scroll down
{
visibleItemCount = linearLayoutManager.getChildCount();
totalItemCount = linearLayoutManager.getItemCount();
pastVisibleItems = linearLayoutManager.findFirstVisibleItemPosition();
if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
pageNumber++; // increase your page
getMessage(); // call api with new Page.
}
}
}
});
}
this method used after recyclerview define and bind adapter.
GridLayoutManger..
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int totalItrem = mLlManager.getItemCount();
int lastViisble = mLlManager.findLastVisibleItemPosition();
Log.d("item", "total item" + totalItrem);
Log.d("visible", "last " + lastViisble);
if (!isLoading && totalItrem == lastViisble + 1 && total_records != mEventList.size()) {
Log.e("msg", "reached end");
//Toast.makeText(mrecyclerview.getContext(), "reached at end", Toast.LENGTH_SHORT).show();
isLoading = true;
mEventList.add(null);
mCurrentPage++;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
getData(mCurrentPage);
}
}, 2000);
}
}
};

Admob in RecycleView with pagination

I have RecycleView which showing the number of items with admob it is working good but i want to implement pagination functionality, i have tried to implement but i didn't get success, My code is like below
Home.Java
recycleFeedList.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) //check for scroll down
{
visibleItemCount = mLinearLayoutManager.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
pastVisiblesItems = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
LogUtils.LOGD("LastPage:", lastPage + "");
LogUtils.LOGD("visibleItemCount:", visibleItemCount + "");
LogUtils.LOGD("pastVisiblesItems:", pastVisiblesItems + "");
LogUtils.LOGD("totalItemCount:", totalItemCount + "");
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
loading = false;
getFeedList(2, "", 1, lastPage);
}
}
}
}
});
and my adapter is like below:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
RecyclerViewFeedItem recyclerViewFeedItem = (RecyclerViewFeedItem) viewHolder.itemView;
recyclerViewFeedItem.bind(mContext, items, position, this, REQUEST_CODE);
}
You can do it like this:
int currentPage = 1;
class OnScrollListener extends RecyclerView.OnScrollListener
{
int firstVisibleItem;
int visibleItemCount;
int totalItemCount;
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
if (dy > 0)
{
visibleItemCount = layoutManager.getChildCount();
totalItemCount = layoutManager.getItemCount();
firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
if (loading)
{
if (totalItemCount > previousTotal)
{
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading &&
(totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold))
{
currentPage++;
//here you load next page, e.g.: load(currentPage);
footer.setVisibility(View.VISIBLE);
loading = true;
}
}
}
}
Usage:
recyclerView.addOnScrollListener(new OnScrollListener());

onLoadMoreListener won't stop loading - RecyclerView - Android

I have a user feed where user's posts will be displayed if the current user is following them, at the moment I have the loadMoreListener working fine in regards to it loading the next batch of images when they get to the bottom of the RecyclerView. But, when I reach the last item (the feeds only shows 50 posts) in the List it'll attempt to load more and cause an OutOfMemoryException. I've restricted the size of the list in the adapter, but at the minute I can't seem to work out when the user has hit the very bottom to stop the progress from displaying and to stop the OnLoadMoreListener from triggering. This is what I have tried so far:
Adapter Constructor:
public RecyclerViewAdapterAllFeeds(Context context,
final ArrayList<AllFeedsDataModel> previousPostsList, final boolean profile, RecyclerView recyclerView, final ProgressBar progressBar) {
this.context = context;
this.previousPostsList = previousPostsList;
this.profile = profile;
if(recyclerView.getLayoutManager() instanceof LinearLayoutManager){
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager)recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
progressBar.setVisibility(View.VISIBLE);
if (getItemCount() > LOADED_POSTS - 2) {
totalItemCount = getItemCount();
// total_posts = getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (lastVisibleItem <= TOTAL_POSTS) {
if (!loading && totalItemCount <= (lastVisibleItem)) {
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
}
}
});
}
}
onLoadMoreListener
adapter = new RecyclerViewAdapterAllFeeds(getActivity(), latestUpdatesList, false, recyclerView, progressBar);
recyclerView.setAdapter(adapter);// set adapter on recyclerview
adapter.setOnLoadMoreListener(new RecyclerViewAdapterAllFeeds.OnLoadMoreListener() {
#Override
public void onLoadMore() {
refreshCount++;
populateRecyclerView(true, refreshCount);
adapter.update(updatesList);
adapter.setLoaded();
System.out.println("load");
}
});
adapter.notifyDataSetChanged();// Notify the adapter
progressBar.setIndeterminate(false);
progressBar.setVisibility(View.INVISIBLE);
My question is how can I stop it loading if the total posts is less than that of the restrictions or to know when the final item is visible?
First create EndlessRecyclerViewScrollListener.java:
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
// The current offset index of data you have loaded
private int currentPage = 0;
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;
RecyclerView.LayoutManager mLayoutManager;
public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
}
public EndlessRecyclerViewScrollListener(GridLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
}
public EndlessRecyclerViewScrollListener(StaggeredGridLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
}
public int getLastVisibleItem(int[] lastVisibleItemPositions) {
int maxSize = 0;
for (int i = 0; i < lastVisibleItemPositions.length; i++) {
if (i == 0) {
maxSize = lastVisibleItemPositions[i];
} else if (lastVisibleItemPositions[i] > maxSize) {
maxSize = lastVisibleItemPositions[i];
}
}
return maxSize;
}
// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
#Override
public void onScrolled(RecyclerView view, int dx, int dy) {
int lastVisibleItemPosition = 0;
int totalItemCount = mLayoutManager.getItemCount();
if (mLayoutManager instanceof StaggeredGridLayoutManager) {
int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
// get maximum element within the list
lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
} else if (mLayoutManager instanceof LinearLayoutManager) {
lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
} else if (mLayoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();
}
// 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;
}
// 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.
// threshold should reflect how many total columns there are too
if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
currentPage++;
onLoadMore(currentPage, totalItemCount);
loading = true;
}
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount);
}
And then implement in this way:
recyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener(recyclerView.getLayoutManager()) {
#Override
public void onLoadMore(int page, int totalItemsCount) {
//TODO Add logic for load more posts or show message if posts is ended.
}
});
Check out the code which I use for endless scroll:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// if (BuildConfig.DEBUG)
// Log.d(TAG, "onScrolled: [x, y]=[" + dx + ", " + dy + "], count=[" + recyclerView.getLayoutManager().getChildCount() + ", " + recyclerView.getLayoutManager().getItemCount() + ", " + ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition() + "]");
if (hasMoreRequest) {
if (dy > 0) { //check for scroll down
checkForMoreLoad();
}
}
}
});
private void checkForMoreLoad() {
final Handler handler = new Handler();
Runnable checkForMoreData = new Runnable() {
#Override
public void run() {
if (null != recyclerView) {
int visibleItemCount = recyclerView.getLayoutManager().getChildCount();
int totalItemCount = recyclerView.getLayoutManager().getItemCount();
int pastVisibleItems = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
onScrollToLoad(visibleItemCount, pastVisibleItems, totalItemCount);
}
}
};
handler.postDelayed(checkForMoreData, 100);
}
private void onScrollToLoad(int visibleItemCount, int pastVisibleItems, int totalItemCount) {
if ((visibleItemCount + pastVisibleItems) + 4 >= totalItemCount && hasMoreRequest) {
if (BuildConfig.DEBUG)
Log.d(TAG, "onScroll lastInScreen - so load more");
startNetworkCall(page + 1);
}
}
And after data from API, if there is more data after that I will again call checkForMoreLoad().
The line which has (visibleItemCount + pastVisibleItems) + 4 in onScrollToLoad() has 4 in it. It is for starting API before hand of reaching end of the scroll.

Grid layout with recycler view android

I found this code and adapted it to my project
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = mLayoutManager.getChildCount();
//totalItemCount = mLayoutManager.getItemCount();
int[] firstVisibleItems = null;
firstVisibleItems = mLayoutManager
.findFirstVisibleItemPositions(firstVisibleItems);
if (firstVisibleItems != null && firstVisibleItems.length > 0) {
pastVisibleItems = firstVisibleItems[0];
}
if (!loading && visibleItemCount <= (pastVisibleItems + visibleThreshold)) {
// End has been reached
// Do something
Log.d(TAG, "onLoadMoreListener ");
getTagResults(mQuery, mMinId, mMaxId);
// if (onLoadMoreListener != null) {
// onLoadMoreListener.onLoadMore();
// }
loading = true;
}
}
but this doesnt work it works for linear layout though. I have found lots of code for all the other layouts but i cant find how to load more results when end is reached.
Use following code
RecyclerView.LayoutManager layoutManager;
layoutManager = new GridLayoutManager(getActivity(), 2);
recyclerView.setLayoutManager(layoutManager);

Categories

Resources