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)
Related
I'm using RecyclerView on my Android App. I want to show firstly bottom and scroll from bottom to top. I can did it but I want to add Scroll Listener and when the scroll position on top I call a function.
chatRecycler = (RecyclerView) findViewById(R.id.chatRecycler);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
chatRecycler.setLayoutManager(layoutManager);
chatRecycler.setItemAnimator(new DefaultItemAnimator());
And I try that but it's not working. This code call the function to bottom:
chatRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (!recyclerView.canScrollVertically(1) && newState==RecyclerView.SCROLL_STATE_IDLE) {
pageId++;
chatDuoPresenter.getChatData(pageId);
}
}
});
Edit:
I'm still searching solution.
Solution:
#Override
public void initScrollListener() {
chatRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount && firstVisibleItemPosition >= 0) {
pageId++;
chatDuoPresenter.getChatData(pageId);
}
}
});
}
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();
}
});
How to implement pagination in recycler listview. I have to show 10 items per page. Can any one tell me how to deal with this?
You can add a scroll listener on recycler view and call the next paginated api on following condition:
recyclerList.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
int pastVisibleItems = mLayoutManager
.findFirstVisibleItemPosition();
int currentPos = pastVisibleItems + visibleItemCount;
if (currentPos >= totalItemCount) {
callNextApi();
}
}
}
});
I have been searching for a while but can't find a solution to detect end of scroll of recycler view with grid layout manager. Using code below actually work, but it is for linear layout manager not for grid layout manager.
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {
final int treeshold = 0;
try {
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
if (((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition() >= yourData.size()
- 1 - treeshold) {
//your load more logic
}
}
} catch (Exception e) {
}
}
The idea is that i want to implement load more function to my application, so i need to detect end of scroll.
Edit : maybe the problem not the grid view itself. I used com.tonicartos.superslim library to get sticky header view. I wonder that it might be the problem
Add a scroll listener to your RecyclerView like below:
int pastVisiblesItems, visibleItemCount, totalItemCount;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = layoutManager.getChildCount();
totalItemCount = layoutManager.getItemCount();
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
//bottom of recyclerview
}
}
});
layoutmanager is your GridLayoutManager
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListener.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 = 5; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;
private int currentPage = 1;
private RecyclerView.LayoutManager mLayoutManager;
private boolean isUseLinearLayoutManager;
private boolean isUseGridLayoutManager;
public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) {
this.mLayoutManager = linearLayoutManager;
isUseLinearLayoutManager = true;
}
public EndlessRecyclerOnScrollListener(GridLayoutManager gridLayoutManager) {
this.mLayoutManager = gridLayoutManager;
isUseGridLayoutManager = true;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
if(isUseLinearLayoutManager && mLayoutManager instanceof LinearLayoutManager){
firstVisibleItem = ((LinearLayoutManager) mLayoutManager).findFirstVisibleItemPosition();
}
if(isUseGridLayoutManager && mLayoutManager instanceof GridLayoutManager){
firstVisibleItem = ((GridLayoutManager) mLayoutManager).findFirstVisibleItemPosition();
}
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
currentPage++;
onLoadMore(currentPage);
loading = true;
}
}
public abstract void onLoadMore(int currentPage);
I just sent an email directly to Mr. Artos about this problem and got a reply:
"It likely won't work properly until the next version is finished. I wrote about it on my blog at http://www.tonicartos.com/2015/12/superslim-milestone-1_23.html?m=1"
I have use Wasabeef for Item animation in Recycler view and it works fine. I also want to add infinite scrolling for recycler, for this I am doing like this
public abstract class InfiniteScroller extends RecyclerView.OnScrollListener {
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 = 5; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public InfiniteScroller(LinearLayoutManager linearLayoutManager) {
this.mLinearLayoutManager = linearLayoutManager;
}
public abstract void onLoadMore(int current_page);
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// Do Nothing
}
#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;
}
}
}
And this also works fine. Real challenge is when I combine item animation with infinte scroll doesn't work even though OnScroll listener is called and also loadMore() is called and it is adding item in the data source but not displaying it in recycler view.
Heres is the code.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
mRecylerView = (RecyclerView)view.findViewById(R.id.my_recycler_view);
mLayoutManager = new LinearLayoutManager(getActivity());
mRecylerView.setLayoutManager(mLayoutManager);
initialize();
mAdapter = new MyAdapter(persons);
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(mAdapter);
ScaleInAnimationAdapter scaleAdapter = new ScaleInAnimationAdapter(alphaAdapter);
scaleAdapter.setDuration(1000);
scaleAdapter.setInterpolator(new OvershootInterpolator());
mRecylerView.setAdapter(scaleAdapter);
mRecylerView.setOnScrollListener(new InfiniteScroller(mLayoutManager) {
#Override
public void onLoadMore(int current_page) {
Log.i("Infinite Scroller", "Current page = " + current_page);
insertNewItem();
}
});
return view;
}
If I comment out animation lines it will scroll infinitely but not otherwise.
Please see the code and let me know what is going wrong.
So i found the problem myself and fixed it :)
For those who are having the same problem i will share my experience.
I was calling mAdpter.notifyItemInserted(position) but it should be scaleAdapter.notifyItemInserted(position).