How to retain selection in multi choice dialog in Android on rotation?

179 Views Asked by At

I have a dialog to select more than one days of a week as follows:

class DialogSettingsEnabledDays : DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

        return activity.let {

            val selectedDaysValue = BooleanArray(7) { _ -> false }
            val selectedDaysIndex = ArrayList<Int>()

            val daysToIndexMap = mutableMapOf<String, Int>()
            val indexToDaysMap = mutableMapOf<Int, String>()
            val daysArray = resources.getStringArray(R.array.days_medium)
            for (i in 0..6) {
                daysToIndexMap[daysArray[i]] = i
                indexToDaysMap[i] = daysArray[i]
            }

            val prefs = it!!.getSharedPreferences(getString(R.string.shared_prefs_settings), Context.MODE_PRIVATE)
            val selectedDaysString = prefs.getString("enabled_days", getString(R.string.default_enabled_days))
            val selectedDays = selectedDaysString!!.split(", ")

            for (day in selectedDays) {
                selectedDaysValue[daysToIndexMap.getValue(day)] = true
                selectedDaysIndex.add(daysToIndexMap.getValue(day))
            }

            val enabledDaysBuilder = AlertDialog.Builder(it)
            enabledDaysBuilder
                .setTitle(R.string.settings_enabled_days)
                .setMultiChoiceItems(R.array.days_long, selectedDaysValue) { _, which, isChecked ->
                    if (isChecked)
                        selectedDaysIndex.add(which)
                    else if (selectedDaysIndex.contains(which))
                        selectedDaysIndex.remove(Integer.valueOf(which))
                }
                .setPositiveButton(R.string.dialog_ok) { _, _ ->
                    if (selectedDaysIndex.isEmpty()) {
                        Toast.makeText(it, "Select atleast one day !!", Toast.LENGTH_SHORT).show()
                    } else {
                        selectedDaysIndex.sort()
                        val selectedDaysList = mutableListOf<String>()
                        for (i in selectedDaysIndex) {
                            selectedDaysList.add(indexToDaysMap.getValue(i))
                        }
                        val editor = prefs.edit()
                        editor
                            .putString("enabled_days", selectedDaysList.joinToString())
                            .apply()
                        val enabledDays = it.findViewById<LinearLayout>(R.id.settings_enabled_days)
                        enabledDays.findViewById<TextView>(R.id.secondary_text).text = selectedDaysList.joinToString()
                    }
                }
                .setNegativeButton(R.string.dialog_cancel) { _, _ -> /* do nothing */ }

            enabledDaysBuilder.create()
        }
    }
}

And I am calling this dialog in this way from my activity:

findViewById<LinearLayout>(R.id.settings_enabled_days)
    .setOnClickListener {
        DialogSettingsEnabledDays().show(this.supportFragmentManager, null)
}

My problem is that my selection of days resets to default on rotation. By default I mean the selection stored in SharedPreferences, that is selectedDaysValue in .setMultiChoiceItems. Suppose, these are the selected days when the dialog pops up:

Mon, Tue, Wed, Thu, Fri

Now, I change the selection as:

Mon, Tue

But, when I rotate the phone, the selection sets back to default:

Mon, Tue, Wed, Thu, Fri

How can I retain my selection on orientation change? Because in some apps I have seen, the Dialog selection remains same on rotation.

2

There are 2 best solutions below

0
Akshdeep Singh On BEST ANSWER

Well, this is the final solution I have made by making changes in the DialogFragment code only:

Changing the scope of data to be stored:

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity.let {
        val selectedDaysValue = BooleanArray(7) { _ -> false }
        val selectedDaysIndex = ArrayList<Int>()

to:

private var selectedDaysValue = BooleanArray(7) { _ -> false }
private var selectedDaysIndex = ArrayList<Int>()

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity.let {

Storing the data:

override fun onSaveInstanceState(outState: Bundle) {
    outState.putBooleanArray("selected_days_value", this.selectedDaysValue)
    outState.putIntegerArrayList("selected_days_index", this.selectedDaysIndex)
}

And where I read the data as:

val prefs = it!!.getSharedPreferences(getString(R.string.shared_prefs_settings), Context.MODE_PRIVATE)
val selectedDaysString = prefs.getString("enabled_days", getString(R.string.default_enabled_days))
val selectedDays = selectedDaysString!!.split(", ")

for (day in selectedDays) {
    selectedDaysValue[daysToIndexMap.getValue(day)] = true
    selectedDaysIndex.add(daysToIndexMap.getValue(day))
}

to read from saved state as:

val prefs = activity!!.getSharedPreferences(getString(R.string.shared_prefs_settings), Context.MODE_PRIVATE)
if (savedInstanceState == null) {
    val selectedDaysString = prefs.getString("enabled_days", getString(R.string.default_enabled_days))
    val selectedDays = selectedDaysString!!.split(", ")

    for (day in selectedDays) {
        selectedDaysValue[daysToIndexMap.getValue(day)] = true
        selectedDaysIndex.add(daysToIndexMap.getValue(day))
    }
} else {
    with(savedInstanceState) {
        selectedDaysValue = getBooleanArray("selected_days_value")!!
        selectedDaysIndex = getIntegerArrayList("selected_days_index")!!
    }
}
10
SSB On

Android system will automatically restore your fragment state, while the state changed fragment is not actually getting destroyed but only its view is recreated, so what ever value is inside your fragment variables will be kept as it is, all you have to do is reassign that variables value to your view, here's the reference link https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en