Search code examples
material-designandroid-preferencesandroid-dialogfragmentandroid-dialog

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 a correct approach?


Solution

  • 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!