'MaterialAlertDialogBuilder for DialogPreferences in PreferenceFragments

In preference screens, I'd like to use MaterialComponent's dialogs (using MaterialAlertDialogBuilder) instead of AlertDialog from AppCompat. However, AppCompat's preference framework hardcodes using the AlertDialog.Builder.

From what I can see, the only way to override this is to override: PreferenceFragmentCompat.onDisplayPreferenceDialog() and copy a bunch of the logic from that superclass (in terms of showing the dialog fragment etc).

Is this the correct approach or is there a better way?



Solution 1:[1]

Here is my solution for ListPreference:

class MaterialListPreferenceDialogFragment : ListPreferenceDialogFragmentCompat() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val context: Context? = activity
        mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE
        val builder = MaterialAlertDialogBuilder(requireActivity())
            .setTitle(preference.dialogTitle)
            .setIcon(preference.dialogIcon)
            .setPositiveButton(preference.positiveButtonText, this)
            .setNegativeButton(preference.negativeButtonText, this)
        val contentView = onCreateDialogView(context)
        if (contentView != null) {
            onBindDialogView(contentView)
            builder.setView(contentView)
        } else {
            builder.setMessage(preference.dialogMessage)
        }
        onPrepareDialogBuilder(builder)

        val dialog = builder.create()
        //if (needInputMethod()) {
        //    requestInputMethod(dialog)
        //}
        return dialog
    }

    /* Override the methods that access mWhichButtonClicked (because we cannot set it properly here) */

    /** Which button was clicked.  */
    private var mWhichButtonClicked = 0

    override fun onClick(dialog: DialogInterface?, which: Int) {
        mWhichButtonClicked = which
    }

    override fun onDismiss(dialog: DialogInterface) {
        onDialogClosedWasCalledFromOnDismiss = true
        super.onDismiss(dialog)
    }

    private var onDialogClosedWasCalledFromOnDismiss = false

    override fun onDialogClosed(positiveResult: Boolean) {
        if (onDialogClosedWasCalledFromOnDismiss) {
            onDialogClosedWasCalledFromOnDismiss = false
            // this means the positiveResult needs to be calculated from our mWhichButtonClicked
            super.onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE)
        } else {
            super.onDialogClosed(positiveResult)
        }
    }
}

fun PreferenceFragmentCompat.showListPreferenceDialog(preference: ListPreference) {
    val dialogFragment = MaterialListPreferenceDialogFragment().apply {
        arguments = Bundle(1).apply {
            putString("key", preference.key)
        }
    }
    dialogFragment.setTargetFragment(this, 0)
    dialogFragment.show(parentFragmentManager, "androidx.preference.PreferenceFragment.DIALOG")
}

and then in the PreferenceFragmentCompat subclass:

override fun onDisplayPreferenceDialog(preference: Preference) {
    if (preference is ListPreference) {
        showListPreferenceDialog(preference)
    } else {
        super.onDisplayPreferenceDialog(preference)
    }
}

Feedback welcome!

Solution 2:[2]

For me it looks like adding this to my theme makes the preference dialog appear in material design:

<item name="alertDialogTheme">@style/ThemeOverlay.MaterialComponents.MaterialAlertDialog</item>

At least the font & color are being adapted, the dialog corners however are still not as rounded as the on coming from MaterialAlertDialogBuilder

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Edric
Solution 2 Edric