¿Cómo guardar la posición de desplazamiento de RecyclerView usando RecyclerView.State?

Resuelto 陈文涛 asked hace 54 años • 20 respuestas

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 .

陈文涛 avatar Jan 01 '70 08:01 陈文涛
Aceptado

Actualizar

Se ha introducido A partir del recyclerview:1.2.0-alpha02lanzamiento . StateRestorationPolicyPodrí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();
        }
    }
Gökhan Barış Aker avatar Mar 20 '2015 12:03 Gökhan Barış Aker

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()

Gillis Haasnoot avatar Jan 14 '2015 22:01 Gillis Haasnoot

¿Cómo planeas guardar la última posición guardada con RecyclerView.State?

Siempre puedes confiar en el buen estado de guardado. Extender RecyclerViewy 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];
            }
        };
    }
Nikola Despotoski avatar Jan 14 '2015 23:01 Nikola Despotoski

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);
}
Braden Holt avatar May 11 '2017 02:05 Braden Holt