I have recycler view with datas that when it on the bottom with this code findLastCompletelyVisibleItemPosition() it will load new data. my init data and my load data are the same function just add change the body request after recyclerview scroll to very bottom. My issue is when it load a new datas it will scroll back to top. how do I make it in the same place and not scroll back?
I already tried to impelemnt this in load data function
val recyclerViewState =list_view_history_program.layoutManager?.onSaveInstanceState()
if (loadMore > 0) {
listAdapter.notifyItemRangeChanged(loadMoreCount, listAdapter.itemCount, programNames)
listAdapter.notifyDataSetChanged()
list_view_history_program.layoutManager?.onRestoreInstanceState(recyclerViewState)
list_view_history_program.scrollToPosition(loadMoreCount)
}
but still it scroll back. please help
You already used notifyItemRangeChanged then used notifyDataSetChanged. Remove listAdapter.notifyDataSetChanged()
Use below code after add item in itemList array
NOTE : but you must call adapter the first time then second time use the below code
adapter.notifyItemInserted(itemList.size());
Related
There is a situation where I have to scroll the recyclerview to last position. The problem starts where I am using Paging 3. I don't know what is the proper way to do it every time I submit data to adapter. by the way I have to say that I do not submit data once, but for example every time user submits a new comment to backend I have to get the new list from server and submit it.
This is what I tried. that's how I observe data:
private fun observeComments() {
exploreViewModel.commentsLiveData.observe(viewLifecycleOwner) { comments ->
commentsAdapter.submitData(viewLifecycleOwner.lifecycle, comments)
}
}
And this is how I tried to scroll the recyclreview to last item:
commentsAdapter.addLoadStateListener { loadStates ->
if(loadStates.refresh.endOfPaginationReached){
if(commentsAdapter.itemCount>1)
binding.rvComments.smoothScrollToPosition(commentsAdapter.itemCount-1)
}
}
I also set app:stackFromEnd="true" to recyclerview but this is not useful. whenever I submit a new comment and get the list, recyclerview scrolls to the middle, I thinks it scrolls to the first pages end. but I want it to be scrolled to the end of the list.
Any help or suggestion would be appreciated.
You can implement PagingSource.getRefreshKey() to control the key passed to .load() after invalidation and set initialKey in Pager() to control the key passed to initial call of .load().
Since your list is paginated, scrolling to the end doesn't work because you would need to sequentially load all pages until you get to your desired position, so instead the better strategy is to start loading from where you want the user's scroll position to start, then let paging prepend pages as they scroll back up.
Since I addData, and called the notifyDataSetChanged(), then I use the findLastCompletelyVisibleItemPosition method immediately, the result is not correct.then I use a postDelay, it's result become correct, why can this be? And how can i know whether the data added completely?
Before adding the data to list, save Last visible position to a variable then add data to list and call notifyDataSetChanged(), then scroll the recyclerview to stored position
I use a ListView with a PullToRefresh mechanism using SwipeRefreshLayout. I add the SwipeRefreshLayout dynamically in code to the ListView when I initialize.
SwipeRefreshLayout swipeRefreshLayout = new SwipeRefreshLayout(listView.getContext());
if (listView.getParent() != null) {
final ViewGroup parent = (ViewGroup) listView.getParent();
final int index = parent.indexOfChild(listView);
parent.removeViewAt(index);
parent.addView(swipeRefreshLayout, index);
listView.setVisibility(View.VISIBLE);
}
swipeRefreshLayout.addView(listView);
swipeRefreshLayout.setOnRefreshListener(...) { }
The above code adds the swipeRefreshLayout to the parent (at the position that the listView is currently in), and then adds the listView as the child of the swipeRefreshLayout.
When I pull to refresh, a network call is made - data is fetched, stored in the DB, and loaded using a CursorAdapter.
The problem :
When I pull to refresh, I see a blank screen for a second and the UI updates itself with all the items. But when the blank screen is showing and I touch the screen, it responds to click events and the ListView immediately shows.
I need the ListView to display all the time while data is being fetched (while the PTR view is showing) and update immediately when new data is available.
Is there something I'm doing wrong? I have tried running setRefreshing to true by posting a runnable to the SwipeRefreshLayout and fetching new data. I don't think this has got anything to do with the data fetch in particular.
The adapter is not being changed while the blank screen appears (which means that the ListView should still be visible).
Any ideas?
You're removing the view then re-adding it. The adapter should take care of redrawing the new data. Don't remove the view, instead when you refresh the data and its ready, call notifyDataSetChanged() on your adapter.
I am using endless scroller with recylerview and it is loading products at bottom but always on load more call my page starts from top not from bottom.
Am I doing something wrong? I am using https://gist.github.com/ssinss/e06f12ef66c51252563e as an endless scroller and my code in activity is:
recyclerView.setOnScrollListener(new EndlessRecyclerOnScrollListener(linearLayoutManager) {
#Override
public void onLoadMore(int current_page) {
String load_more_url = url + "&page=" + String.valueOf(current_page);
bottamProgressBar.setVisibility(View.VISIBLE);
productVolleyLoader = new ProductVolleyLoader(getApplicationContext(), load_more_url,productList,
myAdapter,bottamProgressBar, productRecyclerView);
productVolleyLoader.volleyLoader();
}
});
Very Sorry for this silly question. Actually I was setting adapter in volley loader instead of there I have to set adapter in onCreate method and in volley loader I just have to use notifyDataSetChanged();.
After that scrolling is working fine.
At the end of the "onLoad" method, call:
mRecyclerView.smoothScrollToPosition(lastPosition);
Getting the last position from the view with mRecyclerView.getChildCount() or something like that if each item is a child inside the RecyclerView. Save it to a variable on the begining, so you'll have the last position before the load and scroll there on the end of the load.
You are adding the new values in the old list in load more. So every time its a new list.
You will have to programmatically scroll to the last position of the view.
When you are calling for load more, save the position and when list loads, scroll to that position programmatically.
I'm having a bit of trouble preserving the scroll position of a list view when changing it's adapter's data.
What I'm currently doing is to create a custom ArrayAdapter (with an overridden getView method) in the onCreate of a ListFragment, and then assign it to its list:
mListAdapter = new CustomListAdapter(getActivity());
mListAdapter.setNotifyOnChange(false);
setListAdapter(mListAdapter);
Then, when I receive new data from a loader that fetches everything periodically, I do this in its onLoadFinished callback:
mListAdapter.clear();
mListAdapter.addAll(data.items);
mListAdapter.notifyDataSetChanged();
The problem is, calling clear() resets the listview's scroll position. Removing that call preserves the position, but it obviously leaves the old items in the list.
What is the proper way to do this?
As you pointed out yourself, the call to 'clear()' causes the position to be reset to the top.
Fiddling with scroll-position, etc. is a bit of a hack to get this working.
If your CustomListAdapter subclasses from ArrayAdapter, this could be the issue:
The call to clear(), calls 'notifyDataSetChanged()'. You can prevent this:
mListAdapter.setNotifyOnChange(false); // Prevents 'clear()' from clearing/resetting the listview
mListAdapter.clear();
mListAdapter.addAll(data.items);
// note that a call to notifyDataSetChanged() implicitly sets the setNotifyOnChange back to 'true'!
// That's why the call 'setNotifyOnChange(false) should be called first every time (see call before 'clear()').
mListAdapter.notifyDataSetChanged();
I haven't tried this myself, but try it :)
Check out: Maintain/Save/Restore scroll position when returning to a ListView
Use this to save the position in the ListView before you call .clear(), .addAll(), and . notifyDataSetChanged().
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
After updating the ListView adapter, the Listview's items will be changed and then set the new position:
mList.setSelectionFromTop(index, top);
Basically you can save you position and scroll back to it, save the ListView state or the entire application state.
Other helpful links:
Save Position:
How to save and restore ListView position in Android
Save State:
Android ListView y position
Regards,
Please let me know if this helps!
There is one more use-case I came across recently (Android 8.1) - caused by bug in Android code. If I use mouse-wheel to scroll list view - consecutive adapter.notifyDataSetChanged() resets scroll position to zero. Use this workaround until bug gets fixed in Android
listView.onTouchModeChanged(true); // workaround
adapter.notifyDataSetChanged();
More details is here: https://issuetracker.google.com/u/1/issues/130103876
In your Expandable/List Adapter, put this method
public void refresh(List<MyDataClass> dataList) {
mDataList.clear();
mDataList.addAll(events);
notifyDataSetChanged();
}
And from your activity, where you want to update the list, put this code
if (mDataListView.getAdapter() == null) {
MyDataAdapter myDataAdapter = new MyDataAdapter(mContext, dataList);
mDataListView.setAdapter(myDataAdapter);
} else {
((MyDataAdapter)mDataListView.getAdapter()).refresh(dataList);
}
In case of Expandable List View, you will use
mDataListView.getExpandableListAdapter() instead of
mDataListView.getAdapter()