How to implement pagination in RecyclerView which is inside nestedScrollView - android

I have included RecyclerView inside NestedScrollView and set
mRecyclerView.setNestedScrollingEnabled(false);
Since the scrolling of RecyclerView is set to false, I will not get the scroll position of RecyclerView due to this I will not be able to put pagination in RecyclerView.
I have also tried this
mRecylerview.addOnScrollListener(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) {
if(dy > 0) {
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
isLoading = true;
}
}
}
}
});
But here lastVisibleItem always gives the length of the list to be displayed.
After few digging, I found that when we use RecylerView inside NestedScrollView all the view gets created at the very beginning so that's the reason why lastVisibleItem always gives the size of the list.
Or if I am wrong please explain, me how RecyclerView inside NestedScrollView works?.
Is there any workaround so that I can get RecyclerView scroll position so that I can make my pagination work?.
It will be a great help. Thanks in advance

Here scroll is NestedScrollView
scroll.getViewTreeObserver().addOnScrollChangedListener(() -> {
View view = (View) scroll.getChildAt(scroll.getChildCount() - 1);
Log.d("CategoryNeswList", scroll.getChildCount() + " child");
int diff = (view.getBottom() - (scroll.getHeight() + scroll
.getScrollY()));
if (diff == 0) {
// getPlaylistFromServer("more");
Toast.makeText(mContext, "Load More", Toast.LENGTH_SHORT).show();
}
});

Related

Recyclerview indicates that reaches the last item

I have a recycleView that I need to observe when the last item is reached but I have notice the it always indicate that I reached the last item even if I haven't scrolled yet.
My code for setting up the recycler:
newsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
newsRecyclerView.setFocusable(false);
newsRecyclerView.setNestedScrollingEnabled(false);
newsAdapter = new NewsAdapter(getContext(), newsDetails, categoryNumber);
newsRecyclerView.setAdapter(newsAdapter);
layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
My xml code is:
<android.support.v7.widget.RecyclerView
android:id="#+id/news_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/news_top_stories_title_text_view" />
This is my code that I put in my Util, and use anywhere.
Util.setRecyclerViewLastPositionListner(rv, linearLayoutManager , new UtilitiesV2.OnLastPositionReached() {
#Override
public void onReached() {
// last position reached
}
});
Put this in Util.
private boolean userScrolled = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
public interface OnLastPositionReached {
void onReached();
}
public void setRecyclerViewLastPositionListner(RecyclerView rvBooksMockTest, final LinearLayoutManager mLayoutManager, final OnLastPositionReached onLastPositionReached) {
rvBooksMockTest.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
userScrolled = true;
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// Here get the child count, item count and visibleitems
// from layout manager
visibleItemCount = mLayoutManager.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
// Now check if userScrolled is true and also check if
// the item is end then update recycler view and set
// userScrolled to false
if (userScrolled && (visibleItemCount + pastVisiblesItems) == totalItemCount) {
userScrolled = false;
if (onLastPositionReached != null) onLastPositionReached.onReached();
}
}
});
}
Update
According to your requirement, here is NestedScrollView bottom reach listener.
nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
if (nestedScrollView != null) {
if (nestedScrollView.getChildAt(0).getBottom() <= (nestedScrollView.getHeight() + nestedScrollView.getScrollY())) {
//scroll view is at bottom
} else {
//scroll view is not at bottom
}
}
}
});
Thanks to Khemraj who give the tip for solution
because I have a recyclerview inside NestedScrollView and coordinator layout as parent for them
I have solved my problem like this:
public Disposable observeNestedScroll(LoadMoreListener listener) {
return RxNestedScrollView.scrollChangeEvents(nestedScrollView)
.subscribe(
viewScrollChangeEvent -> {
NestedScrollView nestedScrollView =(NestedScrollView) viewScrollChangeEvent.view();
if(nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1) != null) {
if ((viewScrollChangeEvent.scrollY() >= (nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1).getMeasuredHeight() - nestedScrollView.getMeasuredHeight())) &&
viewScrollChangeEvent.scrollY() > viewScrollChangeEvent.oldScrollY()) {
listener.onLoadMore();
}
}
});
}
I've seen to many responses for this question and I stand that all of them don't give accurate behavior as an outcome. However if you follow this approach I'm positive you'll get the best behavior.
rvCategories is your RecyclerView
categoriesList is the list passed to your adapter
binding.rvCategories.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val position = (recyclerView.layoutManager as LinearLayoutManager).findLastCompletelyVisibleItemPosition()
if (position + 1 == categoriesList.size) {
// END OF RECYCLERVIEW IS REACHED
} else {
// END OF RECYCLERVIEW IS NOT REACHED
}
}
})

Nested recyclerview endless scroll not working

I have recyclerview which contains horizontal child recycler views and in the end it has one single endless scrolling vertical recycler view child
<---RecyclerView-------->
<Horizonatl recycler view>
<Horizonatl recycler view>
<Vertical recyler view endless scrolling>
<---RecyclerView-------->
I have added wrap_content height to child Vertical recyler view due to which I am not able to add scroll listner to this child recylerview
I have added endless scroll listener to outermost parent recyclerview but scroll is working till page 2 only and then its not working how can I achieve nested infinite scroll. Following is my code for listener applied on parent recyclerview
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = layoutManager.getItemCount();
firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
if (dy > 0) {
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
loading = false;
currentPage++;
}
}
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
if (Utils.haveNetworkConnection(getActivity()) && currentPage < Constants.MAX_PAGES) {
loadMoreData();
loading = true;
}
}
}
}

How to implement RecyclerView Page Scrolling

I have a requirement to scroll Recyclerview in one page increments. Each page containing a grid of 3 by 3 items.
I have used GridLayoutManager to implement the grid, now I am trying to override the onScrolled() method to implement scroll by page. I want each horizontal scroll to display 3 items which represents 1 page.
Here is portion of my code
recyclerView = (RecyclerView) rootView.findViewById(R.id.sub_category_list_recyclerview);
recyclerView.setHasFixedSize(true);
gridLayoutManager = new GridLayoutManager(getContext(), rowCount, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(gridLayoutManager);
adapter = new SubCategoryGridLayoutAdapter(new ArrayList<SubCategoryViewItem>());
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(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) {
int visibleItemCount = gridLayoutManager.getChildCount();
int totalItemCount = gridLayoutManager.getItemCount();
int pastVisiblesItems = gridLayoutManager.findFirstVisibleItemPosition();
int lastitemPos = gridLayoutManager.findLastVisibleItemPosition();
if (dx > 0) {
//Right Scrolling
int moreItems = lastitemPos + 9;
if (totalItemCount > moreItems){
gridLayoutManager.scrollToPositionWithOffset(moreItems, 0);
}else {
gridLayoutManager.scrollToPositionWithOffset(totalItemCount, 0);
}
}
if (dx < 0) {
//Left Scrolling
int lessItems = pastVisiblesItems + 9;
if (lessItems < totalItemCount){
gridLayoutManager.scrollToPositionWithOffset(lessItems, 0);
}else {
gridLayoutManager.scrollToPositionWithOffset(totalItemCount, 0);
};
}
}
});
For future reading of this question, in support library 25.1.0 was added PageSnapHelper (for simulation of ViewPager - only scrolling item by item) or LinearSnapHelper (moves to the item that is closest to the middle of RecycleView).
Usage is simple :
PagerSnapHelper().attachToRecyclerView(rvYourRecycleViewName)

Recyclerview onscrolllistener not working when setNestedScrollingEnabled to false

I want to implement pagination with recyclerView, for this I add addOnScrollListener to the recyclerView but I am having trouble with RecyclerView.OnScrollListener not working when I set rvGridExplore.setNestedScrollingEnabled(false);
But when I remove rvGridExplore.setNestedScrollingEnabled(false); it is working fine,
I don't know how to handle this.
Here is code:
rvGridExplore = (RecyclerView) view.findViewById(R.id.rvGridExplore);
final GridLayoutManager glm = new GridLayoutManager(context,2);
// rvGridExplore.setNestedScrollingEnabled(false);
rvGridExplore.setLayoutManager(glm);
// final int visibleItemCount,totalCount,pastVisibleItems;
rvGridExplore.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
Log.v("scrollll","state changed");
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
int totalCount = glm.getItemCount();
int visibleItemCount = glm.getChildCount();
int pastVisibleItems = glm.findFirstVisibleItemPosition();
if (loading) {
if ((visibleItemCount + pastVisibleItems) >= totalCount) {
Log.v("scroll","scrolled"+pastVisibleItems);
}
}
}
}
});
Do add setOnScrollChangeListner to your NestedScrollView
nestedScrollview.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) {
if(loading)
onClick();
loading=false;
}
}
});
after loading data from server set boolean loading=true.
This question may be old, but to help others who stumbled upon this problem, i would like to share what i did. I had to implement onScroll Listener to recyclerview to load data from server and to make some UI changes. And also needed swipeRefresh Layout for refreshing data.
This was my xml file structure,
-RelativeLayout
-SwipeRefreshLayout
-NestedScrollView
-LinearLayout(Vertical)
-Multiple views required
After this, to detect up and down scrolling i implemented setOnScrollListener to the NestedScrollView.
Normal usage of SwipeRefreshLayout to refresh data.
And to load more data i implemented the logic inside onScrollListener of NestedScrollingView.
if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) {
// Load More Data
}
If you are recyclerView is embedded in any of the NestedScrollView, then you are supposed to attach the onScrollListener to NestedScrollView.
This will work!
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) {
totalItemCount = gridLayoutManager.getItemCount();
lastVisibleItem = gridLayoutManager.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
}
});
}
You said in a comment to your question "
it is under NestedScrollView which is under coordinator layout, if i remove this, Toolbar is not scrolling up". This is a mistake.
I have found that you cannot have it both ways, the CoordinatorLayout behaviour breaks when you have a RecyclerView inside a NestedScrollView to which you've added the behaviour. You need to use one or the other.
When you have a RecyclerView inside a NestedScrollView it will work as long as you set RecyclerView.setNestedScrollingEnabled(false), but as you found out this means that the OnScrollListener is not called.
The only way for all components to work correctly is to remove the NestedScrollView, make sure you do not set nesting scroll to false and work from there. Otherwise the RecyclerView.OnScrollListener events will not fire correctly.
Step 1 : Create EndlessRecyclerOnScrollListener
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();
// use your LayoutManager instead
private LinearLayoutManager llm;
public EndlessRecyclerOnScrollListener(LinearLayoutManager sglm) {
this.llm = llm;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!recyclerView.canScrollVertically(1)) {
onScrolledToEnd();
}
}
public abstract void onScrolledToEnd();
}
Step 2: Apply scroll listener to recycler view.
recyclerview.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager) {
#Override
public void onScrolledToEnd() {
Log.e("Position", "Last item reached");
if (loadMore == true) {
// put your Load more code
// add 10 by 10 to tempList then notify changing in data
}
}
});
remove the nested scroll view use linear or relative layout instead of it as root element then you can write recyclerview.setNestedScrollEnabled(false);

Android - Preventing item duplication on RecyclerView

I have a problem regarding RecyclerView duplicating its items on scroll to top.
My RecyclerView populates its View by taking values from an online database on scroll down to a certain threshold. I am well aware of RecyclerView's behavior on reusing the Views, but I don't want that to happen as it'll get confusing due to having Views with different items inside.
I've searched around SO for the solution. Some said that I have to override getItemId like :
#Override
public long getItemId(int id) {
return id;
}
But they don't elaborate more on that.
Tried using setHasStableIds(true); but it's not working. When I scroll down to populate the RecyclerView, then scroll quickly back up, the first item still shows the last item I scrolled to, or any other random item.
I have this in onBindViewHolder :
if(loading)
{
// Do nothing
}
else {
((ObjectViewHolder) holder).progressBar.setVisibility(View.GONE);
((ObjectViewHolder) holder).postListWrapper.setVisibility(View.VISIBLE);
Uri userImageUri = Uri.parse(mDataset.get(position).author_avatar);
...
// The rest of the code
}
Does it have to do with the error I'm getting? The loading is changed to false when the Fragment containing RecyclerView finished getting value from the database.
Here's the RecyclerView onScrollListener :
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstVisibleItem = manager.findFirstCompletelyVisibleItemPosition();
if(firstVisibleItem < 1 )
{
swipeRefreshLayout.setEnabled(true);
}else
{
swipeRefreshLayout.setEnabled(false);
}
totalItemCount = manager.getItemCount();
lastVisibleItem = manager.findLastVisibleItemPosition();
int visibleThreshold = 2;
if(isLoading == false)
{
if (totalItemCount <= lastVisibleItem + visibleThreshold) {
if(lobiAdapter.getItemCount() > 0)
{
if (lobiAdapter.getItemCount() < 5)
{
setIsLoaded();
}else{
// End has been reached
// Do something
PostListAPI postListAPI = new PostListAPI();
postListAPI.query.user_id = userId;
postListAPI.query.post_count = String.valueOf(counter);
postListAPI.query.flag = "load";
NewsFeedsAPIFunc newsFeedsAPIFunc = new NewsFeedsAPIFunc(BottomLobiFragment.this.getActivity());
newsFeedsAPIFunc.delegate = BottomLobiFragment.this;
setIsLoading();
newsFeedsAPIFunc.execute(postListAPI);
}
}
else {
setIsLoaded();
}
}
}
}
});

Categories

Resources