Как можно у RecyclerView сохранить состояние скролла при пересоздании Activity?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Сохранение состояния скролла RecyclerView
Для сохранения состояния скролла RecyclerView при пересоздании Activity необходимо управлять состоянием макета (layout state), которое включает позицию скролла, состояние элементов и другие параметры. Вот основные подходы:
1. Сохранение состояния в Bundle
Самый распространенный метод - использование onSaveInstanceState() и restoreInstanceState().
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private val KEY_RECYCLER_STATE = "recycler_state"
private var recyclerViewState: Parcelable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = MyAdapter()
// Восстановление состояния после пересоздания
savedInstanceState?.let {
recyclerViewState = it.getParcelable(KEY_RECYCLER_STATE)
}
}
override fun onResume() {
super.onResume()
// Восстанавливаем состояние после onResume
recyclerViewState?.let {
recyclerView.layoutManager?.onRestoreInstanceState(it)
recyclerViewState = null
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// Сохраняем состояние RecyclerView
recyclerView.layoutManager?.let {
outState.putParcelable(KEY_RECYCLER_STATE, it.onSaveInstanceState())
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
// Альтернативный способ восстановления состояния
recyclerViewState = savedInstanceState.getParcelable(KEY_RECYCLER_STATE)
}
}
2. Использование ViewModel
ViewModel сохраняется при изменении конфигурации, что позволяет сохранять состояние:
class MyViewModel : ViewModel() {
var recyclerViewState: Parcelable? = null
}
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = MyAdapter()
// Восстановление из ViewModel
viewModel.recyclerViewState?.let {
recyclerView.layoutManager?.onRestoreInstanceState(it)
}
}
override fun onPause() {
super.onPause()
// Сохранение в ViewModel
viewModel.recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState()
}
}
3. Автоматическое сохранение с RecyclerView
Начиная с Support Library 24+, RecyclerView может автоматически сохранять состояние:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:saveEnabled="true"
app:layoutManager="linear" />
Но для полного контроля лучше использовать явное сохранение.
4. Сохранение состояния вложенных RecyclerView
Для сложных случаев с вложенными RecyclerView или горизонтальными списками:
class NestedRecyclerViewState {
companion object {
private val statesMap = mutableMapOf<String, Parcelable>()
fun saveState(recyclerViewId: Int, state: Parcelable) {
statesMap[recyclerViewId.toString()] = state
}
fun restoreState(recyclerViewId: Int): Parcelable? {
return statesMap[recyclerViewId.toString()]
}
}
}
// Использование в адаптере вложенного RecyclerView
inner class NestedAdapter : RecyclerView.Adapter<NestedViewHolder>() {
override fun onBindViewHolder(holder: NestedViewHolder, position: Int) {
holder.nestedRecyclerView.layoutManager?.let { lm ->
val savedState = NestedRecyclerViewState.restoreState(holder.itemView.id)
savedState?.let { lm.onRestoreInstanceState(it) }
}
}
}
5. Решение проблем с конфигурацией
При изменении конфигурации (поворот экрана) важно:
- Сохранять позицию скролла точно
- Сохранять состояние элементов (чекбоксы, прогресс-бары)
- Учитывать перезагрузку данных из сети/БД
// Дополнительное сохранение позиции
private var scrollPosition = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
recyclerView.layoutManager?.let { lm ->
if (lm is LinearLayoutManager) {
scrollPosition = lm.findFirstVisibleItemPosition()
outState.putInt("scroll_position", scrollPosition)
}
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
scrollPosition = savedInstanceState.getInt("scroll_position", 0)
recyclerView.scrollToPosition(scrollPosition)
}
Ключевые моменты:
- LayoutManager.onSaveInstanceState() - основной метод сохранения состояния
- ViewModel предпочтительнее для сложных сценариев
- При работе с асинхронной загрузкой данных сохраняйте состояние после полной загрузки
- Для динамически изменяемых списков учитывайте изменение размера данных
- Тестируйте поведение при быстрых поворотах экрана
Лучшие практики:
- Всегда очищайте ссылки на состояние после восстановления
- Используйте уникальные ключи для Bundle
- При работе с Fragment сохраняйте состояние в onSaveInstanceState() Fragment
- Для реализации бесконечного скролла сохраняйте загруженные данные и позицию
Правильное сохранение состояния RecyclerView улучшает пользовательский опыт, делая навигацию плавной и предсказуемой при пересоздании Activity.