How to use material 3 Switch in PreferenceFragmentCompat programmatically?
Asked Answered
H

2

7

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
)
Hootenanny answered 18/11, 2022 at 9:17 Comment(0)
P
1

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...

Paracelsus answered 29/12, 2022 at 21:14 Comment(2)
Call setWidgetLayout() on an instance of SwitchPreferenceCompat working fine for me.Hootenanny
@Hootenanny Ok, the problem in my project was maybe cause I use PreferenceDataStore... So I've edited the answer, please review it, and maybe set it to accepted answer?Paracelsus
M
4

If you don't want to add a new class for the Material 3 switches, you can override the used layout of the usual SwitchPreferenceCompat by following 2 simple steps:

https://medium.com/@cyb3rko/simple-implementation-of-material-3-switches-in-preferences-4b83ea3202d1


  1. Add the new switch layout to your project:
<?xml version="1.0" encoding="utf-8"?>

<!-- Derived from https://github.com/androidx/androidx/blob/005e9694795cee9a42375d80b0d813af9e700ac1/preference/preference/res/layout/preference_widget_switch_compat.xml -->
<!-- Thanks to https://mcmap.net/q/1249800/-new-android-12-materialswitch-and-androidx-preference -->
<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" />
  1. Override the switch theming in your app theming:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    ...
    
    <style name="Preference.SwitchPreferenceCompat" parent="@style/Preference.SwitchPreferenceCompat.Material"
           tools:ignore="ResourceCycle">
        <item name="widgetLayout">@layout/preference_switch</item>
    </style>
    
    ...
</resources>

Now you can just use the usual SwitchPreferenceCompat in your preference screen like before:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <SwitchPreferenceCompat
        app:title="Material 3 switch"
        app:summary="This is the new Material 3 switch" />

</PreferenceScreen>
Merits answered 17/5, 2023 at 9:20 Comment(0)
P
1

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...

Paracelsus answered 29/12, 2022 at 21:14 Comment(2)
Call setWidgetLayout() on an instance of SwitchPreferenceCompat working fine for me.Hootenanny
@Hootenanny Ok, the problem in my project was maybe cause I use PreferenceDataStore... So I've edited the answer, please review it, and maybe set it to accepted answer?Paracelsus

© 2022 - 2024 — McMap. All rights reserved.