使用底部的 ProgressBar 将项目添加到 Endless Scroll RecyclerView
Adding items to Endless Scroll RecyclerView with ProgressBar at bottom
我遵循了 Vilen 在 SO 上的出色回答:Put an indeterminate progressbar as footer in a RecyclerView grid 关于如何使用 ProgressBar 实现无限滚动回收视图。
我自己实现了它并且它有效,但我想扩展这个例子。我想在 recyclerview 的顶部添加额外的项目,类似于 Facebook 在您添加新的状态更新时的做法。
我无法成功将额外的项目添加到列表中 - 这是我在他的 MainActivity 中添加到 Vilen 代码中的代码:
@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);
}
当我点击 "Add" 按钮时:
当我向下滚动时,我得到两个微调器而不是一个:
当旋转器完成并加载下 5 个项目时,旋转器仍然存在:
我做错了什么?
我想我明白了。
我忘了调用 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);
}
一旦你添加它,代码就会工作,但是,你会看到在微调器完成旋转后,项目编号不会正确增加。
这是因为顶部的 "Newly added" 项目算作实际项目(我们可以称它为 "Item 0"),这导致增量移动 1,就像 21 被跳过一样,但是实际上21号变成了Item 0,也就是说在Item 22之前实际有21条。
问题是,当您添加新项目时,内部 EndlessRecyclerOnScrollListener
不知道它并反对破坏。
事实上,用 EndlessRecyclerOnScrollListener
回答有一些限制和可能的问题,例如如果您一次加载 1 个项目,它将不起作用。所以这里是加强版。
- 去掉
EndlessRecyclerOnScrollListener
我们不再需要它了
将您的适配器更改为包含滚动侦听器的适配器
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);
}
}
}
更改 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");
}
});
其余保持不变,如果这对您有用,请告诉我。
我遵循了 Vilen 在 SO 上的出色回答:Put an indeterminate progressbar as footer in a RecyclerView grid 关于如何使用 ProgressBar 实现无限滚动回收视图。
我自己实现了它并且它有效,但我想扩展这个例子。我想在 recyclerview 的顶部添加额外的项目,类似于 Facebook 在您添加新的状态更新时的做法。
我无法成功将额外的项目添加到列表中 - 这是我在他的 MainActivity 中添加到 Vilen 代码中的代码:
@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);
}
当我点击 "Add" 按钮时:
当我向下滚动时,我得到两个微调器而不是一个:
当旋转器完成并加载下 5 个项目时,旋转器仍然存在:
我做错了什么?
我想我明白了。
我忘了调用 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);
}
一旦你添加它,代码就会工作,但是,你会看到在微调器完成旋转后,项目编号不会正确增加。
这是因为顶部的 "Newly added" 项目算作实际项目(我们可以称它为 "Item 0"),这导致增量移动 1,就像 21 被跳过一样,但是实际上21号变成了Item 0,也就是说在Item 22之前实际有21条。
问题是,当您添加新项目时,内部 EndlessRecyclerOnScrollListener
不知道它并反对破坏。
事实上,用 EndlessRecyclerOnScrollListener
回答有一些限制和可能的问题,例如如果您一次加载 1 个项目,它将不起作用。所以这里是加强版。
- 去掉
EndlessRecyclerOnScrollListener
我们不再需要它了 将您的适配器更改为包含滚动侦听器的适配器
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); } } }
更改 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"); } });
其余保持不变,如果这对您有用,请告诉我。