RecyclerView хранить / восстанавливать состояние между действиями

Я перевожу свои ListViews в RecyclerViews. При просмотре списков я использовал общую технику, описанную здесь, чтобы сохранить и восстановить позицию прокрутки между действиями.

Как сделать то же самое с RecyclerViews? RecyclerView.onSaveInstanceState() кажется protected доступ, поэтому не может быть использован напрямую.

9 ответов

Решение

Итак, чтобы ответить на мой собственный вопрос. Насколько я понимаю, поскольку они разъединили код макета и код повторного использования представления (таким образом, имя), компонент, отвечающий за сохранение состояния макета (и его восстановление), теперь является LayoutManager используется в вашем обзоре переработчика.

Таким образом, для сохранения состояния вы используете тот же шаблон, но в менеджере макета, а не в представлении реселлера:

protected void onSaveInstanceState(Bundle state) {
     super.onSaveInstanceState(state);

     // Save list state
     mListState = mLayoutManager.onSaveInstanceState();
     state.putParcelable(LIST_STATE_KEY, mListState);
}

Восстановить состояние в onRestoreInstanceState():

protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);

    // Retrieve list state and list/item positions
    if(state != null)
        mListState = state.getParcelable(LIST_STATE_KEY);
}

Затем обновите LayoutManager (я делаю в onResume()):

@Override
protected void onResume() {
    super.onResume();

    if (mListState != null) {
        mLayoutManager.onRestoreInstanceState(mListState);
    }
}

Я нашел лучшее решение - это решение имеет следующие преимущества:

  1. Состояние RecyclerView сохраняется и восстанавливается при повороте телефона
  2. Состояние RecyclerView сохраняется и восстанавливается при возврате к действию с RecyclerView (которое не было уничтожено во время отображения другого действия - это означает, что onRestoreInstanceState() не называется!!)

КОД

public class ActivityItemList extends AppCompatActivity
{
    private final String KEY_RECYCLER_STATE = "recycler_state";
    private RecyclerView mRecyclerView;
    private static Bundle mBundleRecyclerViewState;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list);//set to whatever layout name you have

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);//set to whatever view id you use
        // don't forget to set your adapter
    }

    @Override
    protected void onPause()
    {
        super.onPause();

        // save RecyclerView state
        mBundleRecyclerViewState = new Bundle();
        Parcelable listState = mRecyclerView.getLayoutManager().onSaveInstanceState();
        mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, listState);
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        // restore RecyclerView state
        if (mBundleRecyclerViewState != null) {
            Parcelable listState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
            mRecyclerView.getLayoutManager().onRestoreInstanceState(listState);
        }
    }
}

Используйте этот код в onPause() а также onResume() сохранить и восстановить положение прокрутки

private Parcelable recyclerViewState;
recyclerViewState = mrecyclerView.getLayoutManager().onSaveInstanceState();//save
mrecyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);//restore

Это мое решение, оно восстанавливает как предметы, так и позицию RecyclerView

1) сохранить состояние представления переработчика в методе onSaveInstanceState

@Override
protected void onSaveInstanceState(Bundle outState) {
    Parcelable listState = myRecyclerView.getLayoutManager().onSaveInstanceState();
    // putting recyclerview position
    outState.putParcelable(SAVED_RECYCLER_VIEW_STATUS_ID, listState);
    // putting recyclerview items
    outState.putParcelableArrayList(SAVED_RECYCLER_VIEW_DATASET_ID,mDataset);
    super.onSaveInstanceState(outState);
}

2) Проверьте Bundle сохраненного экземпляра в методе onCreate

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState==null){
            getRemoteData(); // No saved data, get data from remote
        }else{
            restorePreviousState(); // Restore data found in the Bundle
        }
    }

3) Восстановите данные просмотра переработчика, если экран был повернут

public void restorePreviousState(){
    // getting recyclerview position
    mListState = mSavedInstanceState.getParcelable(SAVED_RECYCLER_VIEW_STATUS_ID);
    // getting recyclerview items
    mDataset = mSavedInstanceState.getParcelableArrayList(SAVED_RECYCLER_VIEW_DATASET_ID);
    // Restoring adapter items
    mAdapter.setItems(mDataset);
    // Restoring recycler view position
    mRvMedia.getLayoutManager().onRestoreInstanceState(mListState);
}

Я знаю, что опаздываю, но все же, если это поможет!

Сохранение позиции просмотра ресайклера намного проще, чем то, что сделали другие ответы.

Вот как вы можете это сделать

Сначала создайте переменную-член

      Parcelable state;

Теперь,

      @Override
protected void onPause() {
    super.onPause();
    state = recyclerView.getLayoutManager().onSaveInstanceState();
}

@Override
protected void onResume() {
    super.onResume();
    recyclerView.getLayoutManager().onRestoreInstanceState(state);
}

переопределите методы паузы и возобновления с помощью приведенного выше кода, и все готово!

Вы можете использовать adapter.stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY который вводится в recyclerview:1.2.0-alpha02

https://medium.com/androiddevelopers/restore-recyclerview-scroll-position-a8fbdc9a9334

но у него есть некоторые проблемы, такие как не работа с внутренним RecyclerView, и некоторые другие проблемы, которые вы можете проверить в разделе комментариев среднего сообщения.

Или вы можете использовать ViewModel с участием SavedStateHandleкоторый работает для внутренних RecyclerViews, поворота экрана и смерти процесса.

Создать ViewModel с участием saveStateHandle

val scrollState=
    savedStateHandle.getLiveData<Parcelable?>(KEY_LAYOUT_MANAGER_STATE)

использовать Parcelable scrollState для сохранения и восстановления состояния, как указано в других сообщениях, или путем добавления прослушивателя прокрутки в RecyclerView и

recyclerView.addOnScrollListener(объект: RecyclerView.OnScrollListener() {

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
           // save here
        }
    }

Когда вы используете recyclerView.getLayoutManager().onSaveInstanceState() не забудьте проверить на нулевое значение:

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);

    if (recyclerView != null) {
        outState.putParcelable(SCROLL_POSITION, recyclerView.getLayoutManager().onSaveInstanceState());
    }
}
public class MainActivity extends AppCompatActivity {
Parcelable recyclerViewState;
.......
@Override
    protected void onPause() {
        super.onPause();
        recyclerViewState = MainAnecdotesView.getLayoutManager().onSaveInstanceState();//save
    }
    @Override
    protected void onResume()
    {
        super.onResume();
        if(recyclerViewState!=null)
            MainAnecdotesView.getLayoutManager().onRestoreInstanceState(recyclerViewState);//restore
    }
}

Учитывая, что вы определили RecyclerView (mRecyclerView) и LayoutManager (mLayoutManager) в вашем коде и пока все работает нормально, решение по сохранению позиции (mPosition) вашего RecyclerView выглядит так:

  1. Используемые переменные и константы:

    private final String RECYCLER_POSITION_KEY = "recycler_position";
    private int mPosition = RecyclerView.NO_POSITION;
    private RecyclerView mRecyclerView;
    private LinearLayoutManager mLayoutManager;
    private static Bundle mBundleState;
    
  2. В onPause метод:

    @Override
    protected void onPause()
    {
        super.onPause();
    
        // Save RecyclerView state
        mBundleState = new Bundle();
        mPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
        mBundleState.putInt(RECYCLER_POSITION_KEY, mPosition);
    }
    
  3. В onResume метод:

    @Override
    protected void onResume()
    {
        super.onResume();
    
        // Restore RecyclerView state
        if (mBundleState != null) {
            mPosition = mBundleState.getInt(RECYCLER_POSITION_KEY);
            if (mPosition == RecyclerView.NO_POSITION) mPosition = 0;
            // Scroll the RecyclerView to mPosition
            mRecyclerView.smoothScrollToPosition(mPosition);
        }
    }
    
  4. В onSaveInstanceState метод:

    @Override
    public void onSaveInstanceState(Bundle outState) {
        // Save RecyclerView state
        outState.putInt(RECYCLER_POSITION_KEY,  mLayoutManager.findFirstCompletelyVisibleItemPosition());
    
        super.onSaveInstanceState(outState);
    }
    
  5. В onRestoreInstanceState метод:

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        // Restore RecyclerView state
        if (savedInstanceState.containsKey(RECYCLER_POSITION_KEY)) {
            mPosition = savedInstanceState.getInt(RECYCLER_POSITION_KEY);
            if (mPosition == RecyclerView.NO_POSITION) mPosition = 0;
            // Scroll the RecyclerView to mPosition
            mRecyclerView.smoothScrollToPosition(mPosition);
        }
    
        super.onRestoreInstanceState(savedInstanceState);
    }
    
Другие вопросы по тегам