Как сохранить положение прокрутки RecyclerView в Android?

У меня есть вид Recycler, который лежит внутри SwipeRefreshLayout. Кроме того, есть возможность открыть каждый элемент в другой деятельности. После возвращения в Recycler мне нужно перейти к выбранному элементу или к предыдущему Y. Как это сделать?

Да, я погуглил, нашел в StackOverFlow статьи о сохранении экземпляра менеджера компоновки, как этот: RecyclerView состояние сохранения / восстановления между действиями. Но это не помогает мне.

ОБНОВИТЬ

Сейчас у меня проблема такого рода, но, конечно, она тоже не работает.

private int scrollPosition;

...//onViewCreated - it is fragment
recyclerView.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(llm);
data = new ArrayList<>();
adapter.setData(getActivity(), data);
recyclerView.setAdapter(adapter);
...

@Override
public void onResume() {
    super.onResume();
    recyclerView.setScrollY(scrollPosition);
}

@Override
public void onPause() {
    super.onPause();
    scrollPosition = recyclerView.getScrollY();
}

Да, я пробовал scrollTo(int, int) - не работает.

Теперь я попробовал просто прокрутить, например, до Y = 100, но он не прокручивается вообще.

9 ответов

Решение

Сохранить текущее состояние позиции просмотра корзины @onPause:

    positionIndex= llManager.findFirstVisibleItemPosition();
    View startView = rv.getChildAt(0);
    topView = (startView == null) ? 0 : (startView.getTop() - rv.getPaddingTop());

Восстановите позицию прокрутки @onResume:

    if (positionIndex!= -1) {
        llManager.scrollToPositionWithOffset(positionIndex, topView);
    }

или другой способ может быть @onPause:

long currentVisiblePosition = 0;
currentVisiblePosition = ((LinearLayoutManager)rv.getLayoutManager()).findFirstCompletelyVisibleItemPosition();

восстановить @onResume:

((LinearLayoutManager) rv.getLayoutManager()).scrollToPosition(currentVisiblePosition);
currentVisiblePosition = 0;

Многие из этих ответов кажутся слишком сложными.

LayoutManager поддерживает onRestoreInstanceState не требует сохранения позиций прокрутки и т. д. Встроенный метод уже сохраняет идеальные позиции пикселей.

пример кода фрагмента (проверка на ноль и т. д. для ясности удалена):

private Parcelable listState;
private RecyclerView list;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    listState=savedInstanceState.getParcelable("ListState");

}

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

    outState.putParcelable("ListState", list.getLayoutManager().onSaveInstanceState());

}

тогда просто позвони

list.getLayoutManager().onRestoreInstanceState(listState);

как только ваши данные были присоединены к вашему RecyclerView

Начиная с версии 1.2.0-alpha02 библиотеки androidx recyclerView, она теперь управляется автоматически. Просто добавьте его с помощью:

implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02"

И используйте:

adapter.stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY

Перечисление StateRestorationPolicy имеет 3 варианта:

  • РАЗРЕШИТЬ - состояние по умолчанию, которое немедленно восстанавливает состояние RecyclerView на следующем проходе макета
  • PREVENT_WHEN_EMPTY - восстанавливает состояние RecyclerView только тогда, когда адаптер не пустой (adapter.getItemCount() > 0). Если ваши данные загружаются асинхронно, RecyclerView ждет, пока данные будут загружены, и только после этого состояние восстанавливается. Если у вас есть элементы по умолчанию, такие как заголовки или индикаторы выполнения загрузки, как часть вашего адаптера, вы должны использовать параметр PREVENT, если элементы по умолчанию не добавлены с помощью MergeAdapter. MergeAdapter ожидает готовности всех своих адаптеров и только после этого восстанавливает состояние.
  • PREVENT - все восстановление состояния откладывается до тех пор, пока вы не установите ALLOW или PREVENT_WHEN_EMPTY.

Обратите внимание, что на момент этого ответа библиотека recyclerView все еще находится в alpha03, но альфа-фаза не подходит для производственных целей.

Для получения позиции прокрутки используйте свой просмотрщик linearlayoutmanager.

int position = 0;
if (linearLayoutManager != null) {
   scrollPosition = inearLayoutManager.findFirstVisibleItemPosition();
}

и при восстановлении используйте следующий код

if (linearLayoutManager != null) {
  cardRecyclerView.scrollToPosition(mScrollPosition);
}

Надеюсь, это поможет вам

в методе фрагмента onSaveInstanceState() вы можете сохранить положение прокрутки RecycleView

      @Override
  public void onSaveInstanceState(Bundle outState) {
   super.onSaveInstanceState(outState);
   LinearLayoutManager layoutManager = (LinearLayoutManager) 
   recyclerView.getLayoutManager();
   outState.putInt("scrolled_position", 
   layoutManager.findFirstCompletelyVisibleItemPosition());
}

затем вы можете получить сохраненную позицию прокрутки в методе onViewStateRestored()

      @Override
  public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
  super.onViewStateRestored(savedInstanceState);
  if (savedInstanceState != null) {
    int scrollPosition = savedInstanceState.getInt("scrolled_position");
    recyclerView.scrollToPosition(scrollPosition);
  }
}

Чтобы сохранить позицию в настройках, добавьте это в свой onStop()

 int currentVisiblePosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
 getPreferences(MODE_PRIVATE).edit().putInt("listPosition", currentVisiblePosition).apply();

затем восстановить положение, как это

 if (getItemCount() == 0) {
     int savedListPosition = getPreferences(MODE_PRIVATE).getInt("listPosition", 0);
     recyclerView.getLayoutManager().scrollToPosition(savedListPosition); }

последний код должен быть добавлен в событие Адаптера (не уверен, что это событие ведьмы, но в моем случае это onEvent() - com.google.firebase.firestore.EventListener)

Самый простой и совместимый с переходом способ, который я нашел:

   @Override
public void onPause() {
    super.onPause();
    recyclerView.setLayoutFrozen(true);
}

@Override
public void onResume() {
    super.onResume();
    recyclerView.setLayoutFrozen(false);
}

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

Возьмите текущую позицию прокрутки и сохраните ее в onSaveInstanceState Activity. Расширение определенного прокручиваемого представления, чтобы сделать то же самое там. Предотвращение разрушения Activity при вращении. И да, они работают нормально, но…

Но на самом деле все гораздо проще, потому что Android уже делает это за вас!

Если вы внимательно посмотрите на источники RecyclerView/ListView/ScrollView/NestedScrollView, вы увидите, что каждый из них сохраняет свою позицию прокрутки в onSaveInstanceState. И во время первого прохода макета они пытаются прокрутить до этой позиции в методе onLayout.

Есть только 2 вещи, которые нужно сделать, чтобы убедиться, что все будет работать нормально:

  1. Установите идентификатор для вашего прокручиваемого представления, что, вероятно, уже сделано. В противном случае Android не сможет автоматически сохранить состояние просмотра.

  2. Предоставьте данные перед первым проходом макета, чтобы иметь те же границы прокрутки, которые были до вращения. На этом этапе у разработчиков обычно возникают некоторые проблемы.

Ты можешь использовать scrollToPosition или же smoothScrollToPosition для прокрутки до любой позиции элемента в RecyclerView.

Если вы хотите прокрутить до позиции элемента в адаптере, то вам придется использовать адаптеры scrollToPosition или же smoothScrollToPosition,

Другие вопросы по тегам