【问题标题】:Adding items to Endless Scroll RecyclerView with ProgressBar at bottom将项目添加到 Endless Scroll RecyclerView,底部有 ProgressBar
【发布时间】:2015-08-21 07:28:43
【问题描述】:

我关注了 Vilen 在 SO:Put an indeterminate progressbar as footer in a RecyclerView grid 上的出色回答,了解如何使用 ProgressBar 实现无限滚动回收视图。

我自己实现了它,它可以工作,但我想扩展这个例子。我想在 recyclerview 顶部添加额外的项目,类似于 Facebook 在添加新状态更新时的做法。

我无法成功地将额外的项目添加到列表中 - 这是我在 Vilen 的 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);
}

当我点击“添加”按钮时:

当我向下滚动时,我得到两个微调器而不是一个:

当微调器完成并加载接下来的 5 个项目时,微调器仍然存在:

我做错了什么?

【问题讨论】:

    标签: android android-recyclerview infinite-scroll


    【解决方案1】:

    我想我明白了。

    我忘记调用 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);
    }
    

    添加后,代码将起作用,但是,您会看到微调器完成旋转后,项目编号不会正确增加。

    这是因为顶部的“新添加”项算作实际项(我们可以称其为“项 0”),这会导致增量移位 1 就像 21 已被跳过,但实际上数字 21 已变为第 0 项。也就是说,第 22 项之前有 21 个实际项。

    【讨论】:

      【解决方案2】:

      问题是当您添加新项目时,内部EndlessRecyclerOnScrollListener 不知道它并且计数器会损坏。 事实上,EndlessRecyclerOnScrollListener 的回答有一些限制和可能的问题,例如如果您一次加载 1 个项目,它将不起作用。所以这里是一个增强版。

      1. 摆脱EndlessRecyclerOnScrollListener,我们不再需要它了
      2. 将您的适配器更改为包含滚动侦听器的适配器

        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);
                }
            }
        }
        
      3. 在 Activity 类中更改代码

        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");
            }
        });
        

      其余部分保持不变,让我知道这是否适合您。

      【讨论】:

      • 谢谢维伦。虽然我的答案有效,但我注意到如果我在列表中添加超过 1 个项目,微调器会出现两次,并且不会像您的答案那样顺利。我已接受您的答案作为正确答案。 +1。
      • Vilen,我已经为我的 recyclerview 实现了无限滚动,但是一旦它到达我的 recyclerview 中的所有项目的末尾,我注意到 OnScrollListener 实际上被调用了两次 1。它在什么时候被调用我向下滚动以激活它 2。当我到达列表末尾时,当我将任何内容添加到我的 recyclerview 中时,它会再次被调用,因为 totalItemCount = (lastVisibleItem + visibleThreshold)。我能做些什么来对抗它吗?我认为 loadMore 适配器的逻辑 (!loading && totalItemCount
      • 如果您想让我为此再写一个关于 SO 的问题,请告诉我,我很乐意这样做。
      • @VilenMelkumyan 我尝试了相同的代码,但未向我触发 onscrolled 事件。对这个问题有什么建议吗?
      • 很棒的答案。要使其与swipeRefreshLayout 一起使用,我只需在OnScrollListener().onScrolled() 中添加swipeRefreshLayout.setEnabled(layoutManager.findFirstCompletelyVisibleItemPosition() == 0);。在删除OnScrollListeners 时,我只是调用了swipeRefreshLayout.setEnabled(true)
      猜你喜欢
      • 2016-03-25
      • 1970-01-01
      • 1970-01-01
      • 2016-03-29
      • 1970-01-01
      • 1970-01-01
      • 2016-12-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多