Добавление элементов в Endless Scroll RecyclerView с ProgressBar внизу

Я последовал за превосходным ответом Вилена на SO: поместите неопределенный индикатор выполнения в качестве нижнего колонтитула в сетку RecyclerView о том, как реализовать бесконечный просмотр повторного прокрутки с помощью ProgressBar.

Я реализовал это сам, и это работает, но я хотел бы расширить пример. Я хочу добавить дополнительные элементы в верхней части окна реселлера, подобно тому, как это делает Facebook, когда вы добавляете новое обновление статуса.

Я не смог успешно добавить дополнительные элементы в список - вот мой код, который я добавил в код Вилена в его 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 элементов, счетчик все еще там:

после прядильщика

Что я делаю неправильно?

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. Изменить код в классе деятельности

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

Остальное остается без изменений, дайте мне знать, если это работает для вас.

Я думаю, что я понял это.

Я забыл позвонить 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 фактический предмет.

Другие вопросы по тегам