Have RecyclerView load page 1 automatically - android

I'm using a RecyclerView in my Android project. I have a function, getPosts(int page) that adds new items to the relevant ArrayList. This is called in onLoadMore().
Now, when the activity starts, nothing happens. So I decided to call getPosts(1) manually from onCreate(). The problem with this is now page 1 is being loaded twice. Once by me in onCreate(), and for some reason again in onLoadMore(). Subsequent pages load perfectly.
So is there some way to tell the RecyclerView to start loading? If not, what should I do here?
Edit: As requested, here is a summary of my code:
private ArrayList<Post> postArrayList;
private RecyclerView recycler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_diary);
//...
postArrayList = new ArrayList<>();
recycler = ((RecyclerView) findViewById(R.id.recyclerDiary));
recycler.setHasFixedSize(true);
recycler.setLayoutManager(new LinearLayoutManager(this));
EndlessRecyclerViewScrollListener scrollListener = new EndlessRecyclerViewScrollListener(((LinearLayoutManager) recycler.getLayoutManager())){
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
Log.d("devlog", "Requesting page "+page+" from onLoadMore()");
getPosts(page);
}
};
recycler.setAdapter(new PostAdapter(postArrayList, new RecyclerViewClickHandler()));
recycler.addOnScrollListener(scrollListener);
Log.d("devlog", "Requesting page 1 from onCreate()");
getPosts(1);
}
//...
private void getPosts(int page){
int insertStartPos = postArrayList.size();
Post[] posts;
//get posts from the backend
for (int i = 0; i < posts.length(); i++){
postArrayList.add(posts[i]);
}
DiaryAdapter adap = ((DiaryAdapter) recycler.getAdapter());
if (insertStartPos == 0){
adap.notifyDataSetChanged();
} else {
adap.notifyItemRangeInserted(insertStartPos, bundleSize);
}
}

One way to avoid calling getNewItems(1) twice that means that you should have a global variable on your class, for example, named currentPage that is initialised with value 1 and is incremented every time you call getNewItems().
From now on call
getNewItems(currentPage);
currentPage++;

Related

How to show recyclerview items in time interval?

I want to display recyclerview items after every 10 seconds. For instance, I have 8 items in my arraylist. Initially I want to display 3 items, then after waiting for 10 seconds first three visible items will disappear and next 3 items will show. how to achieve it ?
private void addList(){
ItemAdapter itemAdapter = new ItemAdapter();
itemAdapter.setImage(R.drawable.cachua);
itemAdapter.setText("Tomato");
mList.add(itemAdapter);
itemAdapter = new ItemAdapter();
itemAdapter.setImage(R.drawable.bo);
itemAdapter.setText("butter");
mList.add(itemAdapter);
itemAdapter = new ItemAdapter();
itemAdapter.setImage(R.drawable.cam);
itemAdapter.setText("oranges");
mList.add(itemAdapter);
mAdapter = new ListAdapter(mList, this);
mRecycleview.setAdapter(mAdapter);
mRecycleview.setLayoutManager(new LinearLayoutManager(this));
mAdapter.notifyDataSetChanged();
Log.d("anhtt","mlist : " +mList.size());
mList.clear();
itemAdapter = new ItemAdapter();
itemAdapter.setImage(R.drawable.quaxoai);
itemAdapter.setText("mango");
mList.add(itemAdapter);
itemAdapter = new ItemAdapter();
itemAdapter.setImage(R.drawable.dau);
itemAdapter.setText("strawberry");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mAdapter = new ListAdapter(mList, this);
mRecycleview.setAdapter(mAdapter);
mRecycleview.setLayoutManager(new LinearLayoutManager(this));
mAdapter.notifyDataSetChanged();
Log.d("anhtt","mlist : " +mList.size());
mList.add(itemAdapter);
itemAdapter = new ItemAdapter();
itemAdapter.setImage(R.drawable.tao);
itemAdapter.setText("Apple");
mList.add(itemAdapter);
mList.add(itemAdapter);
itemAdapter = new ItemAdapter();
itemAdapter.setImage(R.drawable.oi);
itemAdapter.setText("guava fruit");
mList.add(itemAdapter);
}
Interesting scenario. I think instead of adding time delays in adapter you should do that stuff in your class where you are passing data to adapter. Try to load first 3 items which you want to show then use handler to make delay of 10 seconds.
Like this :
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do your code to clear list and adding next 3 items and notify your adapter
}
}, 10000);
After this you need to clear your list and add next 3 items in list and notify your adapter that data has been updated. With this approach you can achieve your use case.
You should absolutely avoid using Thread.sleep() on the UI thread to keep it unblocked.
Blocking the UI thread will stop event dispatch (input, drawing events) and to a user the application appears to be frozen. If you end up blocking the UI thread for >5 seconds then the user will see an Application Not Responding dialog which makes for a very poor UX.
For your use-case you could use a Handler to dispatch events to the UI thread in a delayed manner
Here's a rough skeleton that might be resourceful:
public final class ScratchActivity extends AppCompatActivity {
private static final int INTERVAL_IN_MILLIS = 10000;
private static final int STEP = 3;
private Handler mHandler;
// Backing list of models to update the recycler view
private final List<Object> mItems = new ArrayList<>();
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(Looper.getMainLooper());
/*
Set content view for activity etc.
Initialize recycler view, layout manager and adapter
*/
updateItems(0);
}
// Loop periodically to derive a sublist of items to update the recycler view adapter
private void updateItems(int index) {
if (index >= mItems.size()) return;
notifyRecyclerView(mItems.subList(index, Math.min(index + STEP, mItems.size())));
mHandler.postDelayed(() -> updateItems(index + STEP), INTERVAL_IN_MILLIS);
}
private void notifyRecyclerView(List<Object> data) {
// Replace data within the recycler view
}
}

Refreshing a recyclerview in a fragment

I have 2 activities: RoutineViewer and ExerciseViewer. Both activities contain fragments that are sub-classes of ParentFragment.java.
RoutineViewer's fragment (RoutineViewerFragment) is displaying a recyclerview. The recyclerview's data is from a static ArrayList in ParentFragment.java.
From RoutineViewerFragment, I can start the ExerciseViewer activity, and the fragment inside it (ExerciseViewerFragment) is used to make changes to customRoutineExercise in ParentFragment.java. After making the changes in ExerciseViewerFragment, I can call requireActivity().finish() on ExerciseViewer.java, which will take me back to RoutineViewerFragment. However, my problem is that the data in RoutineViewerFragment is not reflecting the changes I made in ExerciseViewerFragment.
Some solutions I have tried (but didn't work in this case) from looking at other similar problems:
// In RoutineViewerFragment onResume() with if else conditions
mAdapter.notifyItemChanged(// insert relevant position);
// In RoutineViewer.java onResume() with if else
getSupportFragmentManager().beginTransaction.detach(currentfragment).attach(currentfragment).commit()
I'm guessing that the reason why mAdapter.notifyItemChanged(position) isn't working is because I'm not directly passing the static customRoutineExercise in the constructor of mAdapter, but rather a value of the HashMap instead.
As for the .detach().attach(), I've read that it forces the onCreateView() to run again, which theoretically should refresh the recyclerview with the updated data, but it isn't.
I've left the important code segments below. Any help is appreciated, thanks!
ParentFragment.java
protected static int selectedCustomRoutine;
protected static int selectedCustomExercise;
protected static ArrayList<customRoutine> customRoutineInfo = new ArrayList<>();
protected static HashMap <Integer, ArrayList<customExercise>> customRoutineExercise = new HashMap<>();
RoutineViewer.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_routine_viewer);
Intent intent = getIntent();
routineType = intent.getStringExtra("routineType");
routinePosition = intent.getIntExtra("routinePosition", 0);
selectedFragment = new RoutineViewerCustomFragment(routinePosition);
getSupportFragmentManager().beginTransaction().replace(R.id.routineV_fragment_container, selectedFragment).commit();
break;
}
RoutineViewerFragment.java
private int routinePosition;
public RoutineViewerCustomFragment(int routinePosition) {
// constructor
this.routinePosition = routinePosition;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_routine_viewer_custom, container, false);
buildRecyclerView(rootView);
return rootView;
}
// other code
private void buildRecyclerView(View rootView) {
mRecyclerView = rootView.findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getActivity());
mAdapter = new RoutineViewerCustomAdapter(customRoutineExercise.get(routinePosition));
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
}
// Other code
private void goExerciseViewer(int position) {
selectedCustomExercise = //number;
selectedCustomRoutine = //number;
}
ExerciseViewerFragment.java
private void saveChanges() {
customRoutineExercise.get(selectedCustomRoutine).get(selectedCustomExercise).setSetNumber(Integer.parseInt(editText1.getText().toString()));
customRoutineExercise.get(selectedCustomRoutine).get(selectedCustomExercise).setRepNumber(Integer.parseInt(editText2.getText().toString()));
}
Code Smell: The recyclerview's data is from a static ArrayList in ParentFragment.java.
Don't do this.
Have a repository where you store your "recycler view list" (exercises?), and have the recyclerView request (or observe if you use a ViewModel) a list.
On the fragment where you modify this list, have said fragment talk to the same repository either via its own viewModel or a shared one or whatever you use (search for Android ViewModels jetPack if unfamiliar).
This way, when the recyclerView comes back, it will ask for a new list (and will get the updated copy) or if observing, it will be notified that a new list is available.
The list you pass to the adapter is/should be a copy/immutable.
This is not the same but it's an Activity that has a RecyclerView and "asks" directly for the data to a repo. (This example has nothing to do with your problem, but notice how the adapter is dummy, it's just told to display the list).
https://github.com/Gryzor/CheckBoxCounter/blob/master/app/src/main/java/com/androidonlinux/checkboxcounter/MainActivity.kt

Implement endless recyclerview -android?

so i'm trying to implement an endless recyclerview. I read other tutorials and samples on stack and none of them are straightforward. I'm looking for a universal answer that can be applied for a lot of people's needs including my own.
Currently, i have a recyclerview + adapter working that gets data from my backend and loads it. Now, when i reach the end I want to call my api methods to get data again and load it into my Recycler view.
Here is my Recycler view initialization.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.activity_9b_newsfeed , container, false);
bindActivity();
return rootView;
}
private void bindActivity()
{
progressBar = (ProgressBar) rootView.findViewById(R.id.newsfeed_PB);
recyclerView = (RecyclerView) rootView.findViewById(R.id.newsfeed_RV);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity().getBaseContext());
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setNestedScrollingEnabled(false);
endlessScrollListener = new EndlessScrollListener()//no clue how the hell to use it.
{
#Override
public boolean onLoadMore(int page, int totalItemsCount)
{
//when i reach my last post, this doesn't even get called..so don't really know how to use this.
return false;
}
};
recyclerView.addOnScrollListener(endlessScrollListener);
GetPostsAsyncTask getPostsAsyncTask = new GetPostsAsyncTask();
getPostsAsyncTask.execute();
}
This is my GetPostAsyncTask class, which grabs my post data, encapsulates it into Post Objects, then shoves into my recyclerview, which displays the posts as a newsfeed like feature.
private class GetPostsAsyncTask extends AsyncTask<Void,Void,Void>
{
#Override
protected Void doInBackground(Void... params)
{
if(doesPostListExist)
{
getNewsFeed = new GetNewsFeed(getActivity(),true,Global_Class.getInstance().getValue().user.getUsername());
getNewsFeed.getMyNewsFeed();
}
else
{
getNewsFeed = new GetNewsFeed(getActivity(),false,Global_Class.getInstance().getValue().user.getUsername());
getNewsFeed.getMyNewsFeed();
}
return null;
}
#Override
public void onPostExecute(Void var)
{
progressBar.setVisibility(View.GONE);
Calendar c = Calendar.getInstance();
farTime = c.getTimeInMillis();
Log.d(MY_NEWSFEED,"my far time is " + String.valueOf(farTime));
if(getNewsFeed.isGetNewsFeedSuccess())
{
complexRecyclerViewAdapter = new ComplexRecyclerViewAdapter(getNewsFeed.getNewsFeedPost());//change getNewsFeed.getNewsFeedPost() -> postArrayList
recyclerView.setAdapter(complexRecyclerViewAdapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setNestedScrollingEnabled(false);
}
}
}
This is the endlessScrollListener Class, which i scrapped up somewhere, I have no idea how to use it, but here it is.
public abstract class EndlessScrollListener extends RecyclerView.OnScrollListener implements AbsListView.OnScrollListener {
// The minimum number 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;
public EndlessScrollListener() {
}
public EndlessScrollListener(int visibleThreshold) {
this.visibleThreshold = visibleThreshold;
}
public EndlessScrollListener(int visibleThreshold, int startPage) {
this.visibleThreshold = visibleThreshold;
this.startingPageIndex = startPage;
this.currentPage = startPage;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
// 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;
currentPage++;
}
// 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.
if (!loading && (firstVisibleItem + visibleItemCount + visibleThreshold) >= totalItemCount ) {
loading = onLoadMore(currentPage + 1, totalItemCount);
}
}
// Defines the process for actually loading more data based on page
// Returns true if more data is being loaded; returns false if there is no more data to load.
public abstract boolean onLoadMore(int page, int totalItemsCount);
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// Don't take any action on changed
}
}
My adapter class is gigantic as hell, (to display all the UI), It may be confusing to read all that code. But assume you have a normal recyclerview adapter that handles an arraylist of strings as input, please write that down in your answer if there is anything I myself must put in my adapter in order for endless scrolling to work.
Overall, im looking for a complete easy and functional solution for this universal problem.
thanks!
Your method seems a little overly complicated. Using a scroll listener at all is unneeded. I would take another approach at it.
The main idea behind an infinite scroller is that you hold a subset of the items in memory, because fetching all of them is too expensive (in time, memory, etc). Instead you fetch and display a subset, then when it looks like you need them soon, you fetch the rest. Lets say that there's 1000 items to display. You may only want to display the first 100 at the beginning. So when you think you want to show the next 100 soon you start the fetch. Like this:
bindViewHolder(VH holder, int position) {
if(position > getCount() - ITEM_FETCH_THRESHOLD && !fetching) {
fetching = true;
performFetch();
}
}
performFetch should start an asynchronous fetch. When the fetch finishes, it should call notifyDataSetChanged(). ITEM_FETCH_THRESHOLD is a constant that you can play with until you get right (the right value should be large enough that you can finish a fetch before it ends, but as small as possible to avoid overfetching).
That's all you need to do an infinite scroll. There's some UI tweaks you can add in case the user hits the bottom before the fetch completes, but those are trivial to add.

RecyclerView duplicates every item

I have a problem dealing with recyclerview, when i return back to the activity after opening another activity, it duplicates all the items i displayed. Is there any way to fix this?
Like on list view, it just automatically clears the list and then display it again. can you show me how? thanks
OnCreate
int x = 0;
for(String Name : property_name){
JSONObject jsonObject = arrayResponse.getJSONObject(x);
property_id[x] = jsonObject.getString("property_id");
property_name[x] = jsonObject.getString("property_name");
type[x] = jsonObject.getString("property_type");
price[x] = jsonObject.getString("price");
address[x] = jsonObject.getString("address");
ListingNearby listingNearby = new ListingNearby(property_id[x], property_name[x], type[x], price[x], Math.round(distance[x]));
x++;
list.add(listingNearby);
}
recyclerView = (RecyclerView) findViewById(R.id.ln_recycler_view);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
adapter = new ListingNearbyAdapter(list);
recyclerView.setAdapter(adapter);
I wonder what to put in onResume so that the listview clears the list and then display it again.
Just try to list.clear() before you begin your loop. I guess your list is kind of static variable.
override these two method in your adapter:
#Override
public int getItemCount() {
return reviewModels.size();
}
#Override
public long getItemId(int position) {
return position;
}
it worked for me

Android: Implementing progressbar and "loading..." for Endless List like Android Market

Taking inspiration from Android Market, i have implemented a Endless List which loads more data from the server when we reach the end of the List.
Now, i need to implement the progressbar & "Loading.." text as shown
Sample code to take inspiration from would be great.
Here is a solution that also makes it easy to show a loading view in the end of the ListView while it's loading.
You can see the classes here:
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java
- Helper to make it possible to use the features without extending from SimpleListViewWithLoadingIndicator.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java
- Listener that starts loading data when the user is about to reach the bottom of the ListView.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java
- The EndlessListView. You can use this class directly or extend from it.
Add an onScrollListener to the ListView. When the user scrolls, check if the ListView is nearing its end. If yes, then fetch more data. As an example :
public abstract class LazyLoader implements AbsListView.OnScrollListener {
private static final int DEFAULT_THRESHOLD = 10 ;
private boolean loading = true ;
private int previousTotal = 0 ;
private int threshold = DEFAULT_THRESHOLD ;
public LazyLoader() {}
public LazyLoader(int threshold) {
this.threshold = threshold;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if(loading) {
if(totalItemCount > previousTotal) {
// the loading has finished
loading = false ;
previousTotal = totalItemCount ;
}
}
// check if the List needs more data
if(!loading && ((firstVisibleItem + visibleItemCount ) >= (totalItemCount - threshold))) {
loading = true ;
// List needs more data. Go fetch !!
loadMore(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
}
// Called when the user is nearing the end of the ListView
// and the ListView is ready to add more items.
public abstract void loadMore(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount);
}
Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
ListView listView = (ListView) findViewById(R.id.listView);
listView.setOnScrollListener(new LazyLoader() {
#Override
public void loadMore(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// Fetch your data here !!!
}
});
}
}
You can find the complete implementation at this link
The other answers here refer to outdated, unmaintained solutions. This article, however, seems to be kept up-to-date:
https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews-and-RecyclerView
There's too much there to put it all in a SO answer, but here's some important bits as of the time I'm writing this answer:
Implementing endless pagination for RecyclerView requires the following steps:
Copy over the EndlessRecyclerViewScrollListener.java into your application.
Call addOnScrollListener(...) on a RecyclerView to enable endless pagination. Pass in an instance of EndlessRecyclerViewScrollListener and implement the onLoadMore which fires whenever a new page needs to be loaded to fill up the list.
Inside the aforementioned onLoadMore method, load additional items into the adapter either by sending out a network request or by loading from another source.
To start handling the scroll events for steps 2 and 3, we need to use the addOnScrollListener() method in our Activity or Fragment and pass in the instance of the EndlessRecyclerViewScrollListener with the layout manager as shown below:
public class MainActivity extends Activity {
// Store a member variable for the listener
private EndlessRecyclerViewScrollListener scrollListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Configure the RecyclerView
RecyclerView rvItems = (RecyclerView) findViewById(R.id.rvContacts);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
rvItems.setLayoutManager(linearLayoutManager);
// Retain an instance so that you can call `resetState()` for fresh searches
scrollListener = new EndlessRecyclerViewScrollListener(linearLayoutManager) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
// Triggered only when new data needs to be appended to the list
// Add whatever code is needed to append new items to the bottom of the list
loadNextDataFromApi(page);
}
};
// Adds the scroll listener to RecyclerView
rvItems.addOnScrollListener(scrollListener);
}
// Append the next page of data into the adapter
// This method probably sends out a network request and appends new data items to your adapter.
public void loadNextDataFromApi(int offset) {
// Send an API request to retrieve appropriate paginated data
// --> Send the request including an offset value (i.e `page`) as a query parameter.
// --> Deserialize and construct new model objects from the API response
// --> Append the new data objects to the existing set of items inside the array of items
// --> Notify the adapter of the new items made with `notifyItemRangeInserted()`
}
}

Categories

Resources