web-dev-qa-db-fra.com

Ajout d'éléments à Endless Scroll RecyclerView avec ProgressBar en bas

J'ai suivi l'excellente réponse de Vilen sur SO: Mettez une barre de progression indéterminée comme pied de page dans une grille de RecyclerView sur la façon de mettre en œuvre une vue de recyclage sans fin du défilement avec ProgressBar.

Je l'ai mis en œuvre moi-même et cela fonctionne, mais j'aimerais développer l'exemple. Je souhaite ajouter des éléments supplémentaires en haut de la vue de recyclage, comme le fait Facebook lorsque vous ajoutez une nouvelle mise à jour de statut.

Je n'ai pas pu ajouter d'éléments supplémentaires à la liste. Voici le code que j'ai ajouté au 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":

Adding a new item

Lorsque je fais défiler l'écran vers le bas, j'obtiens deux fileurs au lieu d'un:

Scroll down

Lorsque les fileuses ont fini et que les 5 articles suivants sont chargés, le fileur est toujours là:

after spinner

Qu'est-ce que je fais mal?

32
Simon

Le problème est que, lorsque vous ajoutez un nouvel élément interne, EndlessRecyclerOnScrollListener ne le sait pas et les compteurs ne fonctionnent plus. En fait, répondre avec EndlessRecyclerOnScrollListener a quelques limitations et problèmes possibles, par ex. si vous chargez un élément à la fois, cela ne fonctionnera pas. Donc, voici une version améliorée.

  1. Débarrassez-vous de EndlessRecyclerOnScrollListener nous n'en avons plus besoin
  2. Changez votre adaptateur en celui-ci qui contient un listener de défilement

    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. Changer le code dans la classe d'activité

    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é, laissez-moi savoir si cela fonctionne pour vous.

82
Vilen

Je pense que j'ai 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'aurez ajouté, le code fonctionnera. Cependant, vous verrez qu'après le filage, le numéro d'article ne sera pas incrémenté correctement.

increment

En effet, l'élément "nouvellement ajouté" en haut compte comme un élément réel (nous pouvons l'appeler "élément 0"), ce qui entraîne le décalage de 1 comme 21, mais le numéro 21 est devenu l'élément 0. En d’autres termes, il existe 21 postes réels avant le poste 22.

2
Simon