How to save the state of the RecyclerView after turning the screen?

52 Views Asked by At

I ran into such a problem that when I rotate the screen, the data that I added to my list disappears.

I tried to solve this problem through onSaveInstanceState() and onRestoreInstanceState(), or rather through the methods putParcelable() and getParcelable(), but I never figured out how to implement it

(I apologize for the possibly stupid question, I'm just starting to learn android development)

Here is the code of my RecyclerView:

class MainActivity : AppCompatActivity() {
    private var id = 0
    private lateinit var b : ActivityMainBinding
    private val adapter = ItemAdapter()
    private lateinit var item : Item
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        b = ActivityMainBinding.inflate(layoutInflater)
        setContentView(b.root)
        init()

        with(b) {
            addBtn.setOnClickListener{
                var title = edTitle.text.toString()
                var img = R.drawable.img
                var descr = "1111111111111111111111111"
                item = Item(id++, img, title, descr)
                adapter.addItem(item)
            }

            lastBtn.setOnClickListener {
                adapter.setTrueCheck(item)
            }

            resetBtn.setOnClickListener {
                adapter.resetCheck()
            }

            showBtn.setOnClickListener {
                adapter.showChecked(applicationContext)
            }

        }
    }

    private fun init() {
        b.apply {
            rcView.layoutManager = LinearLayoutManager(this@MainActivity)
            rcView.adapter = adapter
        }
    }

}
class ItemAdapter : RecyclerView.Adapter<ItemHolder>() {
    private var itemList : ArrayList<Item> = ArrayList()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return ItemHolder(view)
    }

    override fun getItemCount(): Int {
        return itemList.size
    }

    override fun onBindViewHolder(holder: ItemHolder, position: Int) {
        holder.bind(itemList[position])
        //holder.changeChecked(itemList[position], itemList)
        holder.b.itemIsChck.setOnClickListener {
            itemList[position].isCheck = !itemList[position].isCheck
            onceCheck(itemList[position])
        }
    }

    fun addItem(item : Item) {
        itemList.add(item)
        notifyDataSetChanged()
    }

    fun setTrueCheck(item : Item)  {
        item.isCheck = true
        onceCheck(item)
    }

    fun resetCheck() {
        for (i in itemList.indices) {
            itemList[i].isCheck = false
        }
        notifyDataSetChanged()
    }

    fun onceCheck(item: Item) {
        for (i in itemList.indices) {
            if (itemList[i].id != item.id) itemList[i].isCheck = false
        }
        notifyDataSetChanged()
    }

    fun showChecked(context: Context) {
        var msg = ""
        for (i in itemList.indices) {
            if (itemList[i].isCheck) {
                msg += "${itemList[i].id}"
                break
            }
        }
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
    }

}
class ItemHolder(item : View) : RecyclerView.ViewHolder(item) {
    val b : ItemLayoutBinding = ItemLayoutBinding.bind(item)
    fun bind(item: Item) = with(b) {
        itemImg.setImageResource(item.image)
        itemTitle.text = item.title
        itemDescr.text = item.descr
        itemIsChck.isChecked = item.isCheck
    }
}
class Item(
    val id : Int,
    var image : Int,
    var title : String,
    var descr : String,
    var isCheck : Boolean = false
)
4

There are 4 best solutions below

0
Jordan On BEST ANSWER

Data loss on screen rotation occurs due to the Android activity lifecycle. When the screen is rotated, the following sequence of events happens:

  • onPause: The onPause method of the current activity is called. This indicates that the activity is no longer in the foreground.
  • Configuration Change: The screen rotation triggers a configuration change, which in turn triggers the destruction and recreation of the activity. This means that the onDestroy and onCreate methods of the activity are called again.
  • onCreate: The activity is recreated from scratch. This includes creating new instances of UI elements and variables.
  • Data Loss: Any data that was not explicitly preserved during the configuration change is lost. This includes data stored in instance variables of the activity because they get recreated with default values.

Your idea with onSaveInstanceState etc. isn't a bad one, it would work. However, recommended way to prevent that is using ViewModel, which makes the thing simpler. I suggest reading this article (which also mentions the problem you have): click me.

To better understand the concept of view models I strongly recommend this video as well: MVVM explained. I think it's a pretty good foundation, then you can explore further :)

0
Nguyễn Minh Khoa On

You should learn about StateFlow, SharedFlow, Live Data and Flow in android to manage simple state in your case.

1
Tayfun YILDIRIM On

One of the best ways to prevent data loss when rotation changes is to use a viewmodel. You can prevent the data from being lost due to rotation changes by storing it in the viewmodel.

Because data loss is caused by terminating and recreating the activity. Since it works independently of the activity, it can retain data even when the activity destroyed. You can access the documentation for the viewmodel from this link.

Another way you can use is to save the data to the local memory of the device with datastore and read this data when the activity is recreated. However, with this method, you can only save simple data, it is not a suitable method for saving complex large data. You can access the documentation for datastore from this link.

0
k4bn On

In general, I found a solution to my problem. I don't know how correct it is, but it works. I use ViewModel. I just saved my adapter in the ViewModel. Thanks to everyone who tried to help me!

here is the final code:

    implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")//gradle:app

/////

    class MainActivityViewModel : ViewModel() {
    var adapter = ItemAdapter()
    lateinit var item : Item
    var id = 0
}

/////

class MainActivity : AppCompatActivity() {
    //private var id = 0
    private lateinit var b : ActivityMainBinding
    //private var adapter = ItemAdapter()
    //private lateinit var item : Item
    private lateinit var viewModel: MainActivityViewModel 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        b = ActivityMainBinding.inflate(layoutInflater)
        setContentView(b.root)
        viewModel = ViewModelProvider(this)[MainActivityViewModel::class.java]
        init()

        with(b) {
            addBtn.setOnClickListener{
                var title = edTitle.text.toString()
                var img = R.drawable.img
                var descr = "1111111111111111111111111"
                viewModel.item = Item(viewModel.id++, img, title, descr)
                viewModel.adapter.addItem(viewModel.item)
            }

            lastBtn.setOnClickListener {
                viewModel.adapter.setTrueCheck(viewModel.item)
            }

            resetBtn.setOnClickListener {
                viewModel.adapter.resetCheck()
            }

            showBtn.setOnClickListener {
                viewModel.adapter.showChecked(applicationContext)
            }

        }
    }

    private fun init() {
        b.apply {
            rcView.layoutManager = LinearLayoutManager(this@MainActivity)
            rcView.adapter = viewModel.adapter
        }
    }

}