I've seen several infinite scroll examples for recyclerview, but they are all scrolling down.
what I want is to load my list again and again but regardless of whether the user scrolls from above or below.
Normal List
Scroll Up
Scroll Down
that way show the same ready over and over again with an infinite scroll
Thanks!
it is Pagination :)
you can do it by adding a scroll listener
// Pagination
recyclerView.addOnScrollListener(recyclerViewOnScrollListener);
then
private RecyclerView.OnScrollListener recyclerViewOnScrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#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 >= PAGE_SIZE) {
loadMoreItems();
}
}
}
};
read this article for more things:
medium
Related
I have followed this tutorial on how to achieve pagination with Firestore, however I need to use it with combined queries so I customized my code to look like this:
//combining queries
ArrayList<Task> tasks = new ArrayList<>();
for (String user : following) {
Task t = postRef.whereEqualTo("publisher", user).orderBy("Date", Query.Direction.DESCENDING).limit(1).get();
tasks.add(t);
}
//getting the result from combined queries - works fine
Task<List<QuerySnapshot>> allTasks = Tasks.whenAllSuccess(tasks);
allTasks.addOnSuccessListener(new OnSuccessListener<List<QuerySnapshot>>() {
#Override
public void onSuccess(List<QuerySnapshot> querySnapshots) {
for (QuerySnapshot queryDocumentSnapshots : querySnapshots) {
for (QueryDocumentSnapshot document : queryDocumentSnapshots) {
PostModel post = document.toObject(PostModel.class);
PostsDataHolder.add(post);
}
progressBar.setVisibility(View.GONE);
recyclerView.setVisibility(VISIBLE);
if (queryDocumentSnapshots.size()>0)lastVisible = queryDocumentSnapshots.getDocuments().get(queryDocumentSnapshots.size() - 1);
}
buildRecyclerView();
Toast.makeText(getActivity() , PostsDataHolder.size()+"", Toast.LENGTH_SHORT).show();
RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
isScrolling = true;
}
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstVisibileItem = linearLayoutManager.findFirstVisibleItemPosition();
int visibleItemCount = linearLayoutManager.getChildCount();
int totalItemCount = linearLayoutManager.getItemCount();
if (isScrolling && (firstVisibileItem + visibleItemCount == totalItemCount) && !isLastItemPaged) {
isScrolling = false;
// getting the next queries
ArrayList<Task> nextTasks = new ArrayList<>();
for (String user : following) {
Task t = postRef.whereEqualTo("publisher", user).orderBy("Date").startAfter(lastVisible).limit(1).get();
nextTasks.add(t);
}
//getting the next results - doesn't work i just get the same results as the first query
Task<List<QuerySnapshot>> nextAllTasks = Tasks.whenAllSuccess(tasks);
nextAllTasks.addOnSuccessListener(new OnSuccessListener<List<QuerySnapshot>>() {
#Override
public void onSuccess(List<QuerySnapshot> querySnapshots) {
for (QuerySnapshot nextQueryDocumentSnapshots : querySnapshots)
{
for (QueryDocumentSnapshot document : nextQueryDocumentSnapshots) {
PostModel post = document.toObject(PostModel.class);
PostsDataHolder.add(post);
}
adapter.notifyDataSetChanged();
if (nextQueryDocumentSnapshots.size() < 1) isLastItemPaged = true;
if (nextQueryDocumentSnapshots.size() > 0)
lastVisible = nextQueryDocumentSnapshots.getDocuments().get(nextQueryDocumentSnapshots.size() - 1);
}
}
});
}
}
};
recyclerView.addOnScrollListener(onScrollListener);
}
});
But the posts are just duplicating instead of getting new ones, I have read somewhere in the documentation about continueWith() but I don't seem to understand how it works, is that what I'm missing in my code?
It's not possible to exclude documents from a query. To that point, if you if you are performing multiple queries (for any reason), it's not possible to have them return only unique documents among those queries.
To put it another way: each query will always return all of the matching document, regardless of any other conditions you might like to impose. You will have to remove any duplicate documents from your result sets in the code that performs the queries. There is no alternative or workaround for this.
I have an app which have a recycler with product and anothe layout above with categories
I want to expand and collapse the categories when the recycler scroll like this video
I made a class that listens to scroll:
public abstract class MyRecyclerScroll extends RecyclerView.OnScrollListener {
final float MINIMUM = 200;
int scrollDist = 0;
boolean isVisible = true;
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (isVisible && scrollDist > MINIMUM) {
hide();
scrollDist = 0;
isVisible = false;
}
else if (!isVisible && scrollDist < -MINIMUM) {
show();
scrollDist = 0;
isVisible = true;
}
if ((isVisible && dy > 0) || (!isVisible && dy < 0)) {
scrollDist += dy;
}
}
public abstract void show();
public abstract void hide();
}
Then I used it on recycler.setOnScrollListener like this:
rec.setOnScrollListener(new MyRecyclerScroll() {
#Override
public void show() {
}
#Override
public void hide() {
}
});
I'm trying to develop a recyclerview, that needs to load new elements, as the user scrolls down (a recycler for news feed). The problem is that with any method I try, my adapter sees that the visible items from recycler are same as getItemCount. One method I tried was override following class in adapter:
#Override
public void onViewAttachedToWindow(#NonNull ViewHolder holder) {
super.onViewAttachedToWindow(holder);
if((getItemCount() -1) == holder.getAdapterPosition()){
Log.e("TAGNews", "List Done!");
}
}
or, another mehthod implemented in my fragment:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = manager.getChildCount();
int totalItemCount = manager.getItemCount();
int firstVisibleItem = manager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
Log.e("News!", "end called");
loadMoreNews();
loading = true;
}
}
});
But always, my messages for debug, that must trigger by the end of the recycler, are triggered when recycler opens.
Here's how my recycler looks for the first time it's opened, without any scrolling
I load from 3 to 10 items from my backend and news have similar height.
Thanks in advice!
Please change your second code to this:
int lastCompletelyVisibleItemPosition;
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastCompletelyVisibleItemPosition = ((LinearLayoutManager) binding.recycler.getLayoutManager()).findLastVisibleItemPosition();
if (dy > 5)
if (lastCompletelyVisibleItemPosition == models.size() - 1) {
loadMoreData();
}
}
});
Hello I need to make the infinite scrolling (Pagination) with the recyclerview using the staggeredGridLayoutManager. Pagination is working but the problem is that onLoadMore() function is called so many times while scrolling which causing problems, here is my code:
newSearchAdapter = new NewSearchAdapter(getActivity(), gridData);
StaggeredGridLayoutManager mLayoutManager;
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
rv_NewProfilesGrid.setLayoutManager(mLayoutManager);
rv_NewProfilesGrid.setAdapter(newSearchAdapter);
rv_NewProfilesGrid.addOnScrollListener(new EndlessRecyclerOnScrollListenerStaggeredLayoutmanager(mLayoutManager) {
#Override
public void onLoadMore(int current_page) {
//calling the api
}
});
and here is my scroll listner
I think the problem here is with the getFirstVisibleItems() function because with GridLayoutManger or LinearLayoutManager it returns an integer but with StaggeredLayout it returns an int Array, so I did the following:
public abstract class EndlessRecyclerOnScrollListenerStaggeredLayoutmanager extends RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListenerStaggeredLayoutmanager.class.getSimpleName();
private int scrolledDistance = 0;
private boolean controlsVisible = false;
private boolean loading = true; // True if we are still waiting for the last set of data to load.
private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more.
public static boolean loadOneTime = false;
private int pastVisibleItems, visibleItemCount, totalItemCount,previousTotal;
private int current_page = 1;
private StaggeredGridLayoutManager mStaggeredGridLayoutManager;
public EndlessRecyclerOnScrollListenerStaggeredLayoutmanager(StaggeredGridLayoutManager staggeredGridLayoutManager) {
this.mStaggeredGridLayoutManager = staggeredGridLayoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mStaggeredGridLayoutManager.getItemCount();
int[] firstVisibleItems = null;
firstVisibleItems = mStaggeredGridLayoutManager.findFirstVisibleItemPositions(firstVisibleItems);
if (firstVisibleItems != null && firstVisibleItems.length > 0) {
pastVisibleItems = firstVisibleItems[0];
}
if (loading) {
if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (pastVisibleItems + visibleThreshold)) {
// End has been reached
// Do something
current_page++;
loadOneTime=false;
onLoadMore(current_page);
loading = true;
}
if (scrolledDistance > 1 && controlsVisible) {
controlsVisible = false;
scrolledDistance = 0;
} else if (scrolledDistance < -1 && !controlsVisible) {
controlsVisible = true;
scrolledDistance = 0;
}
if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) {
scrolledDistance += dy;
}
}
public abstract void onLoadMore(int current_page);
}
Try this. I borrowed it from a stack overflow answer and borrowed another answer and combine to of them I finally solve it.
import android.util.Log
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
abstract class PaginationScrollListener constructor() :
RecyclerView.OnScrollListener() {
private lateinit var mLayoutManager: RecyclerView.LayoutManager
constructor(layoutManager: GridLayoutManager) : this() {
this.mLayoutManager = layoutManager
}
constructor(layoutManager: StaggeredGridLayoutManager) : this() {
this.mLayoutManager = layoutManager
}
constructor(layoutManager: LinearLayoutManager) : this() {
this.mLayoutManager = layoutManager
}
/*
Method gets callback when user scroll the search list
*/
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemCount = mLayoutManager.childCount
val totalItemCount = mLayoutManager.itemCount
var firstVisibleItemPosition = 0
when (mLayoutManager) {
is StaggeredGridLayoutManager -> {
val firstVisibleItemPositions =
(mLayoutManager as StaggeredGridLayoutManager).findFirstVisibleItemPositions(null)
// get maximum element within the list
firstVisibleItemPosition = firstVisibleItemPositions[0]
}
is GridLayoutManager -> {
firstVisibleItemPosition =
(mLayoutManager as GridLayoutManager).findFirstVisibleItemPosition()
}
is LinearLayoutManager -> {
firstVisibleItemPosition =
(mLayoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
}
}
if (!isLoading && !isLastPage) {
if (visibleItemCount + firstVisibleItemPosition >= totalItemCount
&& firstVisibleItemPosition >= 0
) {
Log.i(TAG, "Loading more items")
loadMoreItems()
}
}
}
protected abstract fun loadMoreItems()
abstract val isLastPage: Boolean
abstract val isLoading: Boolean
companion object {
private val TAG = PaginationScrollListener::class.java.simpleName
}
}
And use this in your recycler view in this way
rvCategoryProducts.addOnScrollListener(object :
PaginationScrollListener(layoutManager) {
override fun loadMoreItems() {
vm.isLoading = true
vm.currentPage++
GlobalScope.launch {
vm.getCategoryProductByCatId(vm.id, vm.currentPage)
}
}
override val isLastPage: Boolean
get() = vm.isLastPage
override val isLoading: Boolean
get() = vm.isLoading
})
I am using endless Recyclerview which is loading data from Parse.com database.The problem I am facing is that it is displaying two loading images like shown in below screen.I want to display one item at time and then on scrolling show the icon then load the next item.
Code for EndlessScroll is -
public abstract class EndlessScroll extends RecyclerView.OnScrollListener {
public static String TAG = EndlessScroll.class.getSimpleName();
private int previousTotal = 0; // The total number of items in the dataset after the last load
private boolean loading = true; // True if we are still waiting for the last set of data to load.
private int visibleThreshold = 2; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;
Context context;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public EndlessScroll(LinearLayoutManager linearLayoutManager, Context context) {
this.mLinearLayoutManager = linearLayoutManager;
this.context = context;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
current_page++;
onLoadMore(current_page);
loading = true;
}
}
public abstract void onLoadMore(int current_page);
}
I have started working in android of late.
I am not sure why the loading behaviour is problematic.
Your help/suggestion/any pointer is much appreciated.