- Saving Scroll Positions of Nested RecyclerViews
- Storing the carousel states
- Retrieving carousel states
- Restore RecyclerView scroll position
- Restoring the scroll position
- Как сохранить позицию прокрутки RecyclerView в Android?
- 8 ответов
- Как сохранить позицию прокрутки RecyclerView с помощью RecyclerView.State?
- Обновить
- Старый ответ
Saving Scroll Positions of Nested RecyclerViews
May 3, 2019 · 2 min read
One of the most annoying things when using RecyclerViews is that by nature they don’t store item states upon recycling. Thus, each item resets to its original state from creation as soon as you scroll away from the view. And the issue with nested RecyclerView carousels is that each carousel loses the index that was scrolled to originally. Makes you wonder, is there a trend being practiced in over-the-top apps like Netflix, Prime Video, Disney+, or even Google Play Store? The answer is yes.
Storing the carousel states
The idea behind t his solution is that we will save the current scroll index of each horizontal carousel in some data structure before it is recycled. This way when the views return to foreground we can retrieve the saved indices and set the scroll positions for the carousels.
Few things to consider:
- All changes should be done in the outer adapter.
- We should store a LinearLayoutManager in the view holder and instantiate it upon creating its inner RecyclerView.
- Fun fact: you can get the parent context in an adapter without passing it to the constructor inside onCreateViewHolder with mContext = viewGroup.getContext()
- Create a SparseIntArray or map to hold the scroll position of each carousel.
Now override the onViewRecycled to store the position of the first visible item in that carousel after the view holder recycles.
Retrieving carousel states
Now that the states are stored we can retrieve them dynamically in onBindViewHolder per position.
We have now just created an adapter that maintains the states of a carousel when it is recycled through the list! Although this solution works best when SnapHelper is used for the carousel, since the positions closest to the first visible item is used as a reference point. This technique can be used to store and retrieve data and complex views with similar list patterns.
Источник
Restore RecyclerView scroll position
You might have had the problem where a RecyclerView loses the scroll position when your Activity / Fragment is re-created. This usually happens because the Adapter data is loaded asynchronously and data hasn’t loaded by the time RecyclerView needs to layout so it fails to restore the scroll position.
Starting with 1.2.0-alpha02 , RecyclerView offers a new API to let the Adapter block layout restoration until it is ready. Read on to learn how to use this new API and how it works.
Restoring the scroll position
There are several ways to ensure a correct scroll position that you might have adopted. The best one is making sure that you always set the data on the Adapter before the first layout pass by caching the data you want to display in memory, in a ViewModel or in a repository. If this approach wasn’t possible, other solutions were either more complicated, like avoiding setting the Adapter on the RecyclerView , which can bring issues with items like headers, or misusing LayoutManager.onRestoreInstanceState API.
The recyclerview:1.2.0-alpha02 solution is a new Adapter method which allows you to set a state restoration policy (via the StateRestorationPolicy enum). This has 3 options:
- ALLOW — the default state, that restores the RecyclerView state immediately, in the next layout pass
- PREVENT_WHEN_EMPTY — restores the RecyclerView state only when the adapter is not empty ( adapter.getItemCount() > 0 ). If your data is loaded async, the RecyclerView waits until data is loaded and only then the state is restored. If you have default items, like headers or load progress indicators as part of your Adapter , then you should use the PREVENT option, unless the default items are added using ConcatAdapter (find out more here). ConcatAdapter waits for all of its adapters to be ready and only then it restores the state.
- PREVENT — all state restoration is deferred until you set ALLOW or PREVENT_WHEN_EMPTY .
Set the state restoration policy on the adapter like this:
That’s it! A short and sweet post to get you up to date with RecyclerView ’s lazy state restoration feature. Start using it 🏁👩💻👨💻!
Источник
Как сохранить позицию прокрутки RecyclerView в Android?
У меня есть представление Recycler, которое находится внутри SwipeRefreshLayout. Кроме того, у вас есть возможность открывать каждый элемент в другом действии. После возврата в Recycler мне нужно прокрутить до выбранного элемента или до предыдущего Y. Как это сделать?
Да, я погуглил, нашел в StackOverFlow статьи о сохранении экземпляра диспетчера компоновки, например этот: RecyclerView сохраняет / восстанавливает состояние между действиями. Но мне это не помогает.
ОБНОВЛЕНИЕ
Сейчас у меня есть такая решающая проблема, но, конечно, тоже не работает.
Да, я пробовал scrollTo (int, int) — не работает.
Теперь я попробовал просто прокрутить, например, до Y = 100, но прокрутки нет вообще.
8 ответов
Сохраните текущее состояние позиции повторного просмотра @onPause:
Восстановить позицию прокрутки @onResume:
Или другим способом может быть @onPause:
По какой-то причине существует множество вводящих в заблуждение советов / предложений о том, как сохранять и восстанавливать положение прокрутки в your_scrolling_container при изменении ориентации.
Взять текущую позицию прокрутки и сохранить ее в onSaveInstanceState Activity. Расширение определенного прокручиваемого View, чтобы сделать то же самое. Предотвращение разрушения Activity при вращении. И да, они работают нормально, но .
Но на самом деле все намного проще, потому что Android уже делает это за вас!
Если вы внимательно посмотрите на источники RecyclerView / ListView / ScrollView / NestedScrollView, вы увидите, что каждый из них сохраняет свою позицию прокрутки в onSaveInstanceState. И во время первого прохода макета они пытаются прокрутить до этой позиции в методе onLayout.
Чтобы убедиться, что все работает нормально, вам нужно сделать всего 2 вещи:
Установите идентификатор для прокручиваемого представления, что, вероятно, уже сделано. В противном случае Android не сможет автоматически сохранять состояние просмотра.
Предоставьте данные перед первым проходом макета, чтобы иметь те же границы прокрутки, которые были перед вращением. На этом этапе у разработчиков обычно возникают проблемы.
Самый простой и совместимый с переходом способ, который я нашел:
Используйте свой ресайклер для просмотра linearlayoutmanager для получения позиции прокрутки
И при восстановлении используйте следующий код
Надеюсь, это поможет тебе
Вы можете использовать scrollToPosition или smoothScrollToPosition для перехода к любой позиции элемента в RecyclerView .
Если вы хотите прокрутить до позиции элемента в адаптере , вам придется использовать адаптер scrollToPosition или smoothScrollToPosition .
Чтобы сохранить позицию в настройках, добавьте это в свой onStop ()
Затем восстановите положение, подобное этому
Этот последний код должен быть добавлен внутри события адаптера (не уверен, что событие ведьмы, но в моем случае было onEvent () — com.google.firebase.firestore.EventListener)
Начиная с версии 1.2.0-alpha02 библиотеки androidx recyclerView, она теперь управляется автоматически. Просто добавьте его с помощью:
Перечисление StateRestorationPolicy имеет 3 варианта:
- РАЗРЕШИТЬ — состояние по умолчанию, которое немедленно восстанавливает состояние RecyclerView на следующем проходе макета
- PREVENT_WHEN_EMPTY — восстанавливает состояние RecyclerView только тогда, когда адаптер не пустой (adapter.getItemCount () & gt; 0). Если ваши данные загружаются асинхронно, RecyclerView ждет, пока данные будут загружены, и только после этого состояние восстанавливается. Если у вас есть элементы по умолчанию, такие как заголовки или индикаторы выполнения загрузки, как часть вашего адаптера, вам следует использовать параметр PREVENT, если элементы по умолчанию не добавлены с помощью MergeAdapter. MergeAdapter ожидает готовности всех своих адаптеров и только после этого восстанавливает состояние.
- PREVENT — все восстановление состояния откладывается до тех пор, пока вы не установите ALLOW или PREVENT_WHEN_EMPTY.
Обратите внимание, что на момент этого ответа библиотека recyclerView все еще находится в альфа03, но альфа-фаза не подходит для производственных целей .
Многие из этих ответов, кажется, слишком усложняют его.
LayoutManager поддерживает onRestoreInstanceState из коробки, поэтому нет необходимости сохранять позиции прокрутки и т. Д. Встроенный метод уже сохраняет идеальные позиции пикселей.
Пример кода фрагмента (нулевая проверка и т. д. удалена для ясности):
Тогда просто позвони
Как только ваши данные будут повторно прикреплены к вашему RecyclerView
Источник
Как сохранить позицию прокрутки RecyclerView с помощью RecyclerView.State?
У меня вопрос об Android RecyclerView.State .
Я использую RecyclerView, как я могу использовать и связать его с RecyclerView.State?
Моя цель — сохранить позицию прокрутки RecyclerView .
Как вы планируете сохранять последнюю сохраненную позицию с помощью RecyclerView.State ?
Вы всегда можете рассчитывать на хорошее состояние сохранения. Расширить RecyclerView и переопределить onSaveInstanceState () и onRestoreInstanceState() :
Обновить
Введено начиная с recyclerview:1.2.0-alpha02 релиза StateRestorationPolicy . Это мог бы быть лучший подход к данной проблеме.
Кроме того, @ rubén-viguera поделился более подробной информацией в ответе ниже. https://stackoverflow.com/a/61609823/892500
Старый ответ
Если вы используете LinearLayoutManager, он поставляется с предварительно созданным api сохранения linearLayoutManagerInstance.onSaveInstanceState () и восстановлением api linearLayoutManagerInstance.onRestoreInstanceState (. )
При этом вы можете сохранить возвращенную посылку в своем другом штате. например,
, и восстановить положение с сохраненным состоянием. например,
Подводя итог, ваш окончательный код будет выглядеть примерно так:
Изменить: вы также можете использовать тот же API с GridLayoutManager, так как это подкласс LinearLayoutManager. Спасибо @wegsehen за предложение.
Изменить: помните, что если вы также загружаете данные в фоновом потоке, вам понадобится вызов onRestoreInstanceState в вашем методе onPostExecute / onLoadFinished для восстановления позиции при изменении ориентации, например
и если это не сработает, попробуйте
Положить в магазин onPause() и восстановить в onResume()
Я хотел сохранить позицию прокрутки Recycler View, когда уходил от своей активности в списке, а затем нажимал кнопку «Назад», чтобы вернуться назад. Многие решения этой проблемы были либо намного сложнее, чем требовалось, либо не работали для моей конфигурации, поэтому я решил поделиться своим решением.
Сначала сохраните состояние вашего экземпляра в onPause, как многие показали. Думаю, здесь стоит подчеркнуть, что эта версия onSaveInstanceState является методом из класса RecyclerView.LayoutManager.
Ключ к правильной работе — это убедиться, что вы вызываете onRestoreInstanceState после присоединения адаптера, как некоторые указывали в других потоках. Однако фактический вызов метода намного проще, чем многие указывали.
Я устанавливаю переменные в onCreate (), сохраняю позицию прокрутки в onPause () и устанавливаю позицию прокрутки в onResume ()
Вам больше не нужно сохранять и восстанавливать состояние самостоятельно. Если вы установите уникальный идентификатор в xml и recyclerView.setSaveEnabled (true) (true по умолчанию), система автоматически сделает это. Подробнее об этом: http://trickyandroid.com/saving-android-view-state-correctly/
Все менеджеры компоновки, входящие в состав библиотеки поддержки, уже знают, как сохранять и восстанавливать положение прокрутки.
Положение прокрутки восстанавливается при первом проходе макета, что происходит при соблюдении следующих условий:
- менеджер по расположению прикреплен к RecyclerView
- адаптер прилагается к RecyclerView
Обычно вы устанавливаете диспетчер компоновки в XML, поэтому все, что вам нужно сделать сейчас, это
- Загрузите адаптер с данными
- Затем прикрепите адаптер к RecyclerView
Вы можете сделать это в любое время, это не ограничено Fragment.onCreateView или Activity.onCreate .
Пример: убедитесь, что адаптер подключен каждый раз при обновлении данных.
Сохраненная позиция прокрутки рассчитывается на основе следующих данных:
- Положение адаптера первого элемента на экране
- Пиксельное смещение верха элемента от верха списка (для вертикального расположения)
Поэтому вы можете ожидать небольшого несоответствия, например, если ваши элементы имеют разные размеры в портретной и альбомной ориентации.
RecyclerView / ScrollView / Что нужно иметь android:id для своего государства , чтобы спастись.
Вот мой подход к их сохранению и поддержанию состояния recyclerview во фрагменте. Сначала добавьте изменения конфигурации в родительскую активность, как показано ниже.
Затем в своем фрагменте объявите этот метод экземпляра
Добавьте ниже код в свой метод @onPause
Добавьте метод onConfigartionChanged, как показано ниже.
Этот подход решит вашу проблему. если у вас другой менеджер компоновки, просто замените верхний менеджер компоновки.
Вот как я восстанавливаю позицию RecyclerView с помощью GridLayoutManager после вращения, когда вам нужно перезагрузить данные из Интернета с помощью AsyncTaskLoader.
Сделайте глобальную переменную Parcelable и GridLayoutManager и статическую финальную строку:
Сохранение состояния gridLayoutManager в onSaveInstance ()
Восстановить в onRestoreInstanceState
Затем, когда загрузчик извлекает данные из Интернета, вы восстанавливаете позицию recyclerview в onLoadFinished ()
.. конечно, вам нужно создать экземпляр gridLayoutManager внутри onCreate. Ура
У меня было такое же требование, но решения здесь не совсем помогли мне перейти линию из-за источника данных для recyclerView.
Я извлекал состояние LinearLayoutManager RecyclerViews в onPause ()
Parcelable state сохраняется onSaveInstanceState(Bundle outState) и снова извлекается onCreate(Bundle savedInstanceState) , когда savedInstanceState != null .
Однако адаптер recyclerView заполняется и обновляется объектом ViewModel LiveData, возвращаемым вызовом DAO в базу данных Room.
В конце концов я обнаружил, что восстановление состояния экземпляра непосредственно после установки данных в адаптере будет сохранять состояние прокрутки при поворотах.
Я подозреваю, что это связано либо с тем, что LinearLayoutManager состояние, которое я восстановил, было перезаписано, когда данные были возвращены вызовом базы данных, либо восстановленное состояние было бессмысленным для «пустого» LinearLayoutManager.
Если данные адаптера доступны напрямую (т. Е. Не связаны с вызовом базы данных), то восстановление состояния экземпляра в LinearLayoutManager может быть выполнено после того, как адаптер установлен в recyclerView.
Различие между двумя сценариями удерживало меня целую вечность.
На уровне 28 Android API я просто проверяю, что настроил свой метод LinearLayoutManager and RecyclerView.Adapter in my Fragment#onCreateView , и все работает Just Worked ™ ️. Мне не нужно было ничего делать onSaveInstanceState или onRestoreInstanceState работать.
Ответ Евгения Печанека объясняет, почему это работает.
Начиная с версии 1.2.0-alpha02 библиотеки androidx recyclerView, она теперь управляется автоматически. Просто добавьте его с помощью:
Перечисление StateRestorationPolicy имеет 3 варианта:
- РАЗРЕШИТЬ — состояние по умолчанию, которое восстанавливает состояние RecyclerView немедленно, на следующем проходе макета
- PREVENT_WHEN_EMPTY — восстанавливает состояние RecyclerView только тогда, когда адаптер не пустой (adapter.getItemCount ()> 0). Если ваши данные загружаются асинхронно, RecyclerView ждет, пока данные будут загружены, и только после этого состояние восстанавливается. Если у вас есть элементы по умолчанию, такие как заголовки или индикаторы выполнения загрузки, как часть вашего адаптера, вы должны использовать опцию PREVENT, если только элементы по умолчанию не добавлены с помощью MergeAdapter . MergeAdapter ожидает готовности всех своих адаптеров и только после этого восстанавливает состояние.
- PREVENT — все восстановление состояния откладывается до тех пор, пока вы не установите ALLOW или PREVENT_WHEN_EMPTY.
Обратите внимание, что на момент этого ответа библиотека recyclerView все еще находится в alpha03, но альфа-фаза не подходит для производственных целей .
Для меня проблема заключалась в том, что я каждый раз настраивал новый менеджер компоновки при смене адаптера, теряя позицию прокрутки очереди в recyclerView.
Я просто хотел бы поделиться недавней проблемой, с которой я столкнулся с RecyclerView. Я надеюсь, что любой, кто сталкивается с такой же проблемой, выиграет.
Требования к моему проекту: Итак, у меня есть RecyclerView, в котором перечислены некоторые интерактивные элементы в моем основном действии (Activity-A). При щелчке по элементу отображается новое действие с подробностями об элементе (действие-B).
Я реализовал в файле манифеста то, что Activity-A является родительским для Activity-B, таким образом, у меня есть кнопка возврата или возврата на панель действий Activity-B.
Проблема: каждый раз, когда я нажимал кнопку «Назад» или «Домой» на панели действий Activity-B, Activity-A переходит в полный жизненный цикл действия, начиная с onCreate ().
Несмотря на то, что я реализовал onSaveInstanceState (), сохраняющий список адаптера RecyclerView, когда Activity-A запускает его жизненный цикл, saveInstanceState всегда имеет значение null в методе onCreate ().
Продолжая копаться в Интернете, я столкнулся с той же проблемой, но человек заметил, что кнопка «Назад» или «Домой» под устройством Anroid (кнопка «Назад / Домой по умолчанию») Activity-A не входит в жизненный цикл активности.
- Я удалил тег для родительской активности в манифесте для Activity-B
Я включил кнопку «Домой» или «Назад» на Activity-B
В методе onCreate () добавьте эту строку supportActionBar? .SetDisplayHomeAsUpEnabled (true)
В методе overide fun onOptionsItemSelected () я проверил item.ItemId, по которому щелкают элемент, на основе идентификатора. Идентификатор кнопки возврата:
android.R.id.home
Затем реализуйте вызов функции finish () внутри android.R.id.home
Это завершит Activity-B и принесет Acitivy-A без прохождения всего жизненного цикла.
Для моих требований это пока лучшее решение.
Источник