androidxmlkotlinandroid-preferencesswitchpreference

Icon Change Not Working for SwitchPreference


I'm trying to change the icon for my SwitchPreference depending on its state. If the SwitchPreference is on, I want the icon set to be @drawable/ic_notifications_active, but if it's off I want the icon set to be @drawable/ic_notifications_off.

This is what I have in my PreferenceScreen xml file:

<SwitchPreference
    android:icon="@drawable/ic_notifications_active"
    android:key="notifications_switch_preference"
    android:defaultValue="true"
    app:title="Receive Notifications" />

And this is what it looks like in my design tab:

Android Design Tab of SwitchPreference

In my SettingsActivity, I have this set of code to detect changes to the SwitchPreference:

notificationsPreference?.onPreferenceChangeListener =
    Preference.OnPreferenceChangeListener { preference, newValue ->
        val switched: Boolean = (preference as SwitchPreference)
            .isChecked
        if (switched) {
            if (notificationsPreference != null) {
                notificationsPreference.icon = resources.getDrawable(R.drawable.ic_notifications_active)
            }
        } else {
            if (notificationsPreference != null) {
                notificationsPreference.icon = resources.getDrawable(R.drawable.ic_notifications_off)
            }
        }
        true
    }

The problem now is when I run my app and click on the SwitchPreference toggle for the first time, it changes the icon's color to white, but not the actual icon. When I click again, it then changes icons but it is still white and no longer it's default grey. The wrong icons are now showing for the wrong states.

This is what it looks like for both on and off states:

SwitchPreference On SwitchPreference Off

How do I make it so that when the user clicks on the toggle, it changes to the correct icon and does not change color. I also want it to work on the first try, not on the second.


Solution

  • The wrong icon is displayed because of the next line:

    val switched: Boolean = (preference as SwitchPreference).isChecked
    

    preference object that you get in a callback has old value while newValue stores the actual new value. You have to use newValue object to verify whether the switch was enabled or disabled.

    Here is an example of a full fragment that switches the icon as expected:

    class SettingsFragment : PreferenceFragmentCompat() {
            override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
                setPreferencesFromResource(R.xml.root_preferences, rootKey)
    
                val switchPreference: SwitchPreferenceCompat = findPreference("sync")!!
                switchPreference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { preference, newValue ->
                    val isChecked = newValue as? Boolean ?: false
                    if (isChecked) {
                        switchPreference.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_baseline_sync_24)
                    } else {
                        switchPreference.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_baseline_sync_disabled_24)
                    }
                    true
                }
            }
        }
    

    Link to the gif

    example test