Agregar elementos a Endless Scroll RecyclerView con ProgressBar en la parte inferior

Seguí la excelente respuesta de Vilen en SO: coloque una barra de progreso indeterminada como pie de página en una cuadrícula de RecyclerView sobre cómo implementar una vista de reciclador de rollos sin fin con ProgressBar.

Lo implementé yo mismo y funciona, pero me gustaría extender el ejemplo. Quiero agregar elementos adicionales en la parte superior de la vista de reciclador, similar a cómo lo hace Facebook cuando agrega una nueva actualización de estado.

No pude agregar elementos adicionales a la lista con éxito; aquí está mi código que agregué al código de Vilen en su actividad principal:

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

Cuando hice clic en el botón “Agregar”:

Agregar un nuevo elemento

Cuando me desplazo hacia abajo, obtengo dos spinners en lugar de uno:

Desplazarse hacia abajo

Cuando los hilanderos terminan y los 5 elementos siguientes se cargan, la ruleta todavía está allí:

después de spinner

¿Qué estoy haciendo mal?

El problema es que cuando agrega un nuevo elemento interno, EndlessRecyclerOnScrollListener no lo conoce y los contadores se rompen. Como cuestión de hecho, responder con EndlessRecyclerOnScrollListener tiene algunas limitaciones y posibles problemas, por ejemplo, si carga 1 elemento a la vez, no funcionará. Entonces aquí hay una versión mejorada.

  1. Deshágase de EndlessRecyclerOnScrollListener ya no lo necesitamos
  2. Cambie su adaptador a este que contiene oyente de desplazamiento

     public class MyAdapter extends RecyclerView.Adapter { private final int VIEW_ITEM = 1; private final int VIEW_PROG = 0; private List 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 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. Cambiar el código en la clase de actividad

     mAdapter = new MyAdapter(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"); } }); 

El rest no cambia, avíseme si esto funciona para usted.

Creo que lo descubrí.

Olvidé llamar a 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); } 

Una vez que lo agregue, el código funcionará; sin embargo, verá que una vez que el girador termina de girar, el número del artículo no boostá correctamente.

incremento

Esto se debe a que el elemento “Nuevamente agregado” en la parte superior cuenta como un elemento real (podemos llamarlo “Artículo 0”), y esta causa porque el incremento para cambiar por 1 como 21 se ha omitido, pero en realidad el número 21 se ha convertido en Artículo 0 En otras palabras, hay 21 elementos reales antes del Artículo 22.