I followed Vilen's excellent answer on SO: Put an indeterminate progressbar as footer in a RecyclerView grid on how to implement an endless scroll recyclerview with ProgressBar.
I implemented it myself and it works but I would like to extend the example. I want to add extra items at the top of the recyclerview, similar to how Facebook does it when you add a new status update.
I was not able to add extra items onto the list successfully - here is my code that I added onto Vilen's code in his MainActivity:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.add) {
myDataset.add(0, "Newly added");
mRecyclerView.smoothScrollToPosition(0);
mAdapter.notifyItemInserted(0);
}
return super.onOptionsItemSelected(item);
}
When I clicked the "Add" button:
When I scroll down, I get two spinners instead of one:
When the spinners finish and the next 5 items are loaded, the spinner is still there:
What am I doing wrong?
The problem is that when you add new item internal EndlessRecyclerOnScrollListener
doesn't know about it and counters breaking.
As a matter of fact answer with EndlessRecyclerOnScrollListener
has some limitations and possible problems, e.g. if you load 1 item at a time it will not work. So here is an enhanced version.
EndlessRecyclerOnScrollListener
we don't need it anymoreChange your adapter to this which contains scroll listener
public class MyAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
private List<T> mDataset;
// The minimum amount of items to have below your current scroll position before loading more.
private int visibleThreshold = 2;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public MyAdapter(List<T> myDataSet, RecyclerView recyclerView) {
mDataset = myDataSet;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!loading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
}
@Override
public int getItemViewType(int position) {
return mDataset.get(position) != null ? VIEW_ITEM : VIEW_PROG;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext())
.inflate(android.R.layout.simple_list_item_1, parent, false);
vh = new TextViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.progress_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof TextViewHolder) {
((TextViewHolder) holder).mTextView.setText(mDataset.get(position).toString());
} else {
((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
}
}
public void setLoaded() {
loading = false;
}
@Override
public int getItemCount() {
return mDataset.size();
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public interface OnLoadMoreListener {
void onLoadMore();
}
public static class TextViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public TextViewHolder(View v) {
super(v);
mTextView = (TextView) v.findViewById(android.R.id.text1);
}
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public ProgressViewHolder(View v) {
super(v);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
}
}
}
Change code in Activity class
mAdapter = new MyAdapter<String>(myDataset, mRecyclerView);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnLoadMoreListener(new MyAdapter.OnLoadMoreListener() {
@Override
public void onLoadMore() {
//add progress item
myDataset.add(null);
mAdapter.notifyItemInserted(myDataset.size() - 1);
handler.postDelayed(new Runnable() {
@Override
public void run() {
//remove progress item
myDataset.remove(myDataset.size() - 1);
mAdapter.notifyItemRemoved(myDataset.size());
//add items one by one
for (int i = 0; i < 15; i++) {
myDataset.add("Item" + (myDataset.size() + 1));
mAdapter.notifyItemInserted(myDataset.size());
}
mAdapter.setLoaded();
//or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
}
}, 2000);
System.out.println("load");
}
});
The rest remains unchanged, let me know if this works for you.
I think I figured it out.
I forgot to call notifyItemRangeChanged.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.add) {
myDataset.add(0, "Newly added");
mAdapter.notifyItemInserted(0);
mAdapter.notifyItemRangeChanged(1, myDataset.size());
mRecyclerView.smoothScrollToPosition(0);
}
return super.onOptionsItemSelected(item);
}
Once you add it, the code will work, however, you will see that after the spinner finishes spinning, the item number will not increment properly.
This is because the "Newly added" item on top counts as an actual item (we can call it "Item 0"), and this cause the increment to shift by 1 like 21 has been skipped, but actually number 21 has become Item 0. In other words, there are 21 actual items before Item 22.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With