Ajout d'éléments à RecyclerView de défilement sans fin avec ProgressBar en bas

J'ai suivi L'excellente réponse de Vilen sur SO: Mettre une barre de progression indéterminée comme pied de page dans une grille RecyclerView sur la façon d'implémenter un recyclerview de défilement sans fin avec ProgressBar.

Je l'ai implémenté moi-même et cela fonctionne mais je voudrais étendre l'exemple. Je veux ajouter des éléments supplémentaires en haut de recyclerview, similaire à la façon dont Facebook le fait lorsque vous ajoutez une nouvelle mise à jour de statut.

Je n'ai pas été en mesure d'ajouter des éléments sur la liste avec succès - voici mon code que J'ai ajouté sur le code de Vilen dans son 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);
}

Quand j'ai cliqué sur le bouton "Ajouter":

L'ajout d'un nouvel élément

Quand je fais défiler vers le bas, j'ai deux fileurs au lieu d'un:

Faites défiler vers le bas

Lorsque les spinners se terminent et que les 5 éléments suivants sont chargés, le spinner est toujours là:

après spinner

Qu'est-ce que je fais de mal?

32
demandé sur Community 2015-06-06 13:48:17

2 réponses

Le problème est que lorsque vous ajoutez un nouvel élément interne EndlessRecyclerOnScrollListener ne le sait pas et que les compteurs se cassent. En fait, la réponse avec EndlessRecyclerOnScrollListener a quelques limitations et problèmes possibles, par exemple si vous chargez 1 élément à la fois, cela ne fonctionnera pas. Voici donc une version améliorée.

  1. débarrassez-vous de EndlessRecyclerOnScrollListener nous n'en avons plus besoin
  2. Changez votre adaptateur pour celui-ci qui contient 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);
            }
        }
    }
    
  3. Modifier le code de L'activité Classe

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

Le reste reste inchangé, faites-moi savoir si cela fonctionne pour vous.

81
répondu Vilen 2015-12-29 01:26:51

Je pense que j'ai tout compris.

J'ai oublié d'appeler 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);
}

Une fois que vous l'ajoutez, le code fonctionnera, cependant, vous verrez qu'après que le spinner ait fini de tourner, le numéro d'article ne s'incrémentera pas correctement.

incrément

C'est parce que l'élément "nouvellement ajouté" en haut compte comme un élément réel (nous pouvons l'appeler "Item 0"), et cela provoque le décalage de l'incrément de 1 comme 21 a été ignoré, mais en fait le numéro 21 est devenu L'élément 0. Dans d'autres mots, il ya 21 articles réels avant L'article 22.

2
répondu Simon 2015-06-06 16:36:09