android - recyclerview with header
RecyclerView scrolling on insert (2)
Estoy tratando de usar RecyclerView
para crear una aplicación de chat. Estoy usando un LinearLayoutManager
con setReverseLayout(true)
.
Cuando me desplazo hasta la parte inferior (que es el inicio del conjunto de datos = mensaje más nuevo) y se inserta un nuevo mensaje en el conjunto de datos, el elemento aparece en la parte inferior de la lista como se esperaba (la vista se desplaza hacia arriba para hacer espacio) para el nuevo artículo).
El problema que tengo es cuando me he desplazado hacia arriba para ver los mensajes anteriores. Cuando se inserta un nuevo mensaje al principio del conjunto de datos, la vista se desplaza hacia arriba aproximadamente en una altura de mensaje, aunque ese mensaje ni siquiera se haya procesado, ya que está fuera del rango de la ventana gráfica.
¿Cómo puedo mantener el comportamiento de desplazamiento cuando la vista se desplaza hacia la parte inferior, pero deshabilitarla cuando me he desplazado a los mensajes más antiguos?
ACTUALIZACIÓN: También hice una aplicación pequeña, donde se reproduce este problema: https://github.com/ehehhh/RecyclerViewProblem
ACTUALIZACIÓN 2: También comprometí la solución que funcionó en el repositorio que hice.
Código relevante (con suerte ):
compile ''com.android.support:recyclerview-v7:24.2.0''
XML:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="8dp"
android:paddingTop="8dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
Código de inicio de RecyclerView :
layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setScrollContainer(true);
recyclerView.setLayoutAnimation(null);
recyclerView.setItemAnimator(null);
adapter = new ChatAdapter(...);
recyclerView.setAdapter(adapter);
Adaptador :
public class ChatAdapter extends RecyclerView.Adapter<ChatViewHolder> {
private List<MessageWrapper> dataset;
public ChatAdapter(List<MessageWrapper> dataset, ...) {
this.dataset = dataset;
setHasStableIds(true);
}
...
@Override
public long getItemId(int position) {
return dataset.get(position).getId();
}
@Override
public int getItemCount() {
return dataset.size();
}
public void datasetChanged(List<MessageWrapper> dataset) {
this.dataset = dataset;
notifyDataSetChanged();
}
}
Cuando se agrega un nuevo elemento al conjunto de datos, solo llamo al método datasetChanged
en el adaptador.
Tuve un problema similar Estaba creando una aplicación de chat, y mi RecyclerView siempre mostraba filas desde la parte superior, y quería ir a la parte inferior para mostrar el último mensaje insertado. Esto es lo que funcionó para mí, recyclerView.scrollToPosition(messages.size()-1)
agregado en ChildEventListener
:
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private MessageAdapter mMessageAdapter;
private List<Message> messages;
private FirebaseDatabase mFirebaseDatabase;
private DatabaseReference mMessagesDatabaseRef;
....
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFirebaseDatabase = FirebaseDatabase.getInstance();
mMessagesDatabaseRef = mFirebaseDatabase.getReference().child("messages");
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
// Initialize message ListView and its adapter
messages = new ArrayList<>();
mMessageAdapter = new MessageAdapter(messages);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(mMessageAdapter);
.....
mMessagesDatabaseRef.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
messages.add(dataSnapshot.getValue(Message.class));
recyclerView.scrollToPosition(messages.size()-1);
mMessageAdapter.notifyDataSetChanged();
}
......
});
}
}
en la vista de reciclador que utiliza NotifyDataSetChanged es redundante si sabe qué elementos modificados puede usar
notifyItemInserted(position)
En este caso particular lo que funcionó fue
notifyItemInserted(0);
o
notifyItemRangeInserted(positionStart, newItems.size() - 1)
Esto solo rebindirá las vistas en este rango.