¿Cómo guardar la posición de desplazamiento de RecyclerView usando RecyclerView.State?
Tengo una pregunta sobre RecyclerView.State de Android .
Estoy usando RecyclerView, ¿cómo podría usarlo y vincularlo con RecyclerView.State?
Mi propósito es guardar la posición de desplazamiento de RecyclerView .
Actualizar
Se ha introducido A partir del recyclerview:1.2.0-alpha02
lanzamiento . StateRestorationPolicy
Podría ser un mejor enfoque para el problema dado.
Este tema se ha tratado en el artículo mediano para desarrolladores de Android .
Además, @ rubén-viguera compartió más detalles en la respuesta a continuación. https://stackoverflow.com/a/61609823/892500
Antigua respuesta
Si está utilizando LinearLayoutManager, viene con una API de guardado prediseñada linearLayoutManagerInstance.onSaveInstanceState() y una API de restauración linearLayoutManagerInstance.onRestoreInstanceState(...)
Con eso, puede guardar el paquete devuelto en su estado externo. p.ej,
outState.putParcelable("KeyForLayoutManagerState", linearLayoutManagerInstance.onSaveInstanceState());
y restaurar la posición de restauración con el estado que guardó. p.ej,
Parcelable state = savedInstanceState.getParcelable("KeyForLayoutManagerState");
linearLayoutManagerInstance.onRestoreInstanceState(state);
Para resumir todo, su código final se verá así
private static final String BUNDLE_RECYCLER_LAYOUT = "classname.recycler.layout";
/**
* This is a method for Fragment.
* You can do the same in onCreate or onRestoreInstanceState
*/
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if(savedInstanceState != null)
{
Parcelable savedRecyclerLayoutState = savedInstanceState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
recyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerLayoutState);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(BUNDLE_RECYCLER_LAYOUT, recyclerView.getLayoutManager().onSaveInstanceState());
}
Editar: también puedes usar las mismas API con GridLayoutManager, ya que es una subclase de LinearLayoutManager. Gracias @wegsehen por la sugerencia.
Editar: recuerde, si también está cargando datos en un hilo en segundo plano, necesitará realizar una llamada a onRestoreInstanceState dentro de su método onPostExecute/onLoadFinished para que la posición se restaure tras el cambio de orientación, por ejemplo.
@Override
protected void onPostExecute(ArrayList<Movie> movies) {
mLoadingIndicator.setVisibility(View.INVISIBLE);
if (movies != null) {
showMoviePosterDataView();
mDataAdapter.setMovies(movies);
mRecyclerView.getLayoutManager().onRestoreInstanceState(mSavedRecyclerLayoutState);
} else {
showErrorMessage();
}
}
Almacenar
lastFirstVisiblePosition = ((LinearLayoutManager)rv.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
Restaurar
((LinearLayoutManager) rv.getLayoutManager()).scrollToPosition(lastFirstVisiblePosition);
y si eso no funciona, intenta
((LinearLayoutManager) rv.getLayoutManager()).scrollToPositionWithOffset(lastFirstVisiblePosition,0)
Guardar onPause()
y restauraronResume()
¿Cómo planeas guardar la última posición guardada con RecyclerView.State
?
Siempre puedes confiar en el buen estado de guardado. Extender RecyclerView
y anular onSaveInstanceState() y onRestoreInstanceState()
:
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null && layoutManager instanceof LinearLayoutManager){
mScrollPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
}
SavedState newState = new SavedState(superState);
newState.mScrollPosition = mScrollPosition;
return newState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
if(state != null && state instanceof SavedState){
mScrollPosition = ((SavedState) state).mScrollPosition;
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null){
int count = layoutManager.getItemCount();
if(mScrollPosition != RecyclerView.NO_POSITION && mScrollPosition < count){
layoutManager.scrollToPosition(mScrollPosition);
}
}
}
}
static class SavedState extends android.view.View.BaseSavedState {
public int mScrollPosition;
SavedState(Parcel in) {
super(in);
mScrollPosition = in.readInt();
}
SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mScrollPosition);
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
Quería guardar la posición de desplazamiento de Recycler View al navegar fuera de mi actividad de lista y luego hacer clic en el botón Atrás para navegar hacia atrás. Muchas de las soluciones proporcionadas para este problema eran mucho más complicadas de lo necesario o no funcionaban para mi configuración, así que pensé en compartir mi solución.
Primero guarde el estado de su instancia en onPause como muchos han mostrado. Creo que vale la pena enfatizar aquí que esta versión de onSaveInstanceState es un método de la clase RecyclerView.LayoutManager.
private LinearLayoutManager mLayoutManager;
Parcelable state;
@Override
public void onPause() {
super.onPause();
state = mLayoutManager.onSaveInstanceState();
}
La clave para que esto funcione correctamente es asegurarse de llamar a onRestoreInstanceState después de conectar su adaptador, como algunos han indicado en otros hilos. Sin embargo, la llamada al método real es mucho más simple de lo que muchos han indicado.
private void someMethod() {
mVenueRecyclerView.setAdapter(mVenueAdapter);
mLayoutManager.onRestoreInstanceState(state);
}