androidxmlandroid-preferencesmaterial-components-android

How to use material 3 Switch in PreferenceFragmentCompat programmatically?


SwitchPreferenceCompat still retains the old Switch style, I know to be able to style it Material 3 in the following way:

theme.xml

<style name="Theme.Material3.Preference" parent="Theme.Material3.DayNight.NoActionBar">
    <item name="preferenceTheme">@style/MaterialPreferenceThemeOverlay</item>
</style>

<style name="MaterialPreferenceThemeOverlay" parent="PreferenceThemeOverlay">
    <item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat</item>
</style>

<style name="Preference.SwitchPreferenceCompat" parent="Preference.SwitchPreferenceCompat.Material">
    <item name="android:widgetLayout">@layout/preference_widget_material_switch</item>
</style>

preference_widget_material_switch.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.materialswitch.MaterialSwitch
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/switchWidget"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:focusable="false"
    android:clickable="false"
    android:background="@null"/>

setting.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <SwitchPreferenceCompat
        android:defaultValue="true"
        android:key="pref_key"
        android:summary="Preference Summary"
        android:title="Preference Title" />
</PreferenceScreen>

enter image description here
But for some reason I have to build PreferenceFragment programmatically like this:

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
    val context = preferenceManager.context
    val screen = preferenceManager.createPreferenceScreen(context)
    val notificationPreference = SwitchPreferenceCompat(context).apply {
        key = "pref_key"
        title = "Preference Title"
        summary="Preference Summary"
    }
    screen.addPreference(notificationPreference)
    preferenceScreen = screen
}

At this time, even if I have the ThemeOverlay above, the style of the switch is still old. enter image description here
So, how to use material 3 Switch in PreferenceFragmentCompat programmatically?

I'm guessing it might be possible to set the theme of SwitchPreferenceCompat via one of its constructors, but I don't know about Views, so can you guys help me?

SwitchPreferenceCompat(@NonNull Context context)  

SwitchPreferenceCompat(
    @NonNull Context context,
    @Nullable AttributeSet attrs
)  

SwitchPreferenceCompat(
    @NonNull Context context,
    @Nullable AttributeSet attrs,
    int defStyleAttr
)  

SwitchPreferenceCompat(
    @NonNull Context context,
    @Nullable AttributeSet attrs,
    int defStyleAttr,
    int defStyleRes
)

Solution

  • Before adding your SwitchPreference to your preferences screen you can use setWidgetLayout() where you pass the resId of your preference_widget_material_switch.xml layout.

    Or if it doesn't work (like in my project) you can create a custom View that extends SwitchPreferenceCompat and it's in its init block that you set the layout resource id for the switch widget to use.

    class MaterialSwitchPreference(context: Context): SwitchPreferenceCompat(context) {
    
        init {
            widgetLayoutResource = R.layout.preference_widget_material_switch
        }
    
    }
    

    Then just use this MaterialSwitchPreference instead of the usual SwitchPrefernce.

    Note: I'm a beginner and I don't know if extending a View for such a little thing is a good practice, but it works fine for me...