javaandroiddialogcustomdialoglistpreference

Custom list preference dialog layout in Preference Screen, with custom list items


I want to create a custom list dialog when I click on a ListPreference in Settings Activity.

ListPreference in root_preferences.xml:

  <ListPreference
        android:icon="@drawable/pref_language"
        app:defaultValue="en"
        app:entries="@array/language_entries"
        app:entryValues="@array/language_values"
        app:iconSpaceReserved="false"
        app:key="@string/prefkey_language"
        app:summary="%s"
        app:title="@string/language_title" />

I want it to have rounded corners, and I also want custom listitem views (e.g. images instead of the default radiobuttons).

Actual result:

enter image description here

Expected result:

enter image description here

Can I do this in a way that I use ListPreference, but replace @array entries with an adapter?

Is there any other way to achieve this without needing to get rid of the PreferenceScreen?


Solution

  • Meanwhile, I have found a solution. It's probably not the cleanest one, but it works for me. If anyone knows a better solution, don't hesitate to let us know.

    1. Create a class that extends DialogPreference. Create as many classes as many custom dialogs you want to have, e.g. LanguagePreference, UnitPreference, etc.

      public class LanguagePreference extends DialogPreference implements Preference.OnPreferenceChangeListener {
      
         public LanguagePreference(Context context, AttributeSet attrs) {
             super(context, attrs);
             setOnPreferenceChangeListener(this);
         }
      
         @Override
         public boolean onPreferenceChange(Preference preference, Object newValue) {
             // do something
             return true;
         }
      }
      
    2. Now you can use these in root_preferences.xml.

       <PreferenceCategory app:title="@string/category">
           <com.company.yourapp.preferences.LanguagePreference
               android:icon="@drawable/pref_language"
               app:title="@string/language" />
      
           <com.company.yourapp.preferences.UnitPreference
               android:icon="@drawable/pref_unit"
               app:title="@string/unit" />
       </PreferenceCategory>
      
    3. Create a class that extends DialogFragment (may be abstract). Remove default background and title in onViewCreated().

      public abstract class RoundedPreferenceDialog extends DialogFragment {
      
          @Override
          public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
              if (getDialog() != null && getDialog().getWindow() != null) {
                  getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));  // remove default background so that dialog can be rounded
                  getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);  // remove default title
              }
              super.onViewCreated(view, savedInstanceState);
          }
      }
      
    4. Extend classes from RoundedPreferenceDialog, e.g. LanguagePreferenceDialog, UnitPreferenceDialog.

      public class LanguagePreferenceDialog extends RoundedPreferenceDialog {
      
         public static LanguagePreferenceDialog newInstance() {
             return new LanguagePreferenceDialog();
         }
      
         @Override
         public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
             View view = inflater.inflate(R.layout.dialogpreference_language, container, false);
      
             // init UI, list adapter, listeners
      
             return view;
         }
      }
      
    5. Override onDisplayPreferenceDialog in SettingsFragment. Create a condition for each custom dialog you have.

       @Override
       public void onDisplayPreferenceDialog(Preference preference) {
           if (preference instanceof LanguagePreference) {         // rounded language dialog
               LanguagePreferenceDialog.newInstance().show(getParentFragmentManager(), null);
           } else if (preference instanceof UnitPreference) {      // rounded unit dialog
               UnitPreferenceDialog.newInstance().show(getParentFragmentManager(), null);
           } else {
               super.onDisplayPreferenceDialog(preference);
           }
       }
      
    6. Now you can display a custom dialog instead of the default one. You just have to customize it the way you want. To make it rounded, set the background of the root layout to a custom rounded layout.

    dialogpreference_language.xml:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@+id/dialogLayout"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:background="@drawable/bg_dialog_rounded">
    
       <!-- titleTextView, languageListView, cancelButton, etc. -->
    </RelativeLayout>
    

    bg_dialog_rounded.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <corners android:radius="@dimen/dialog_radius" />
        <solid android:color="@color/white" />
    </shape>
    
    1. Set the style of the list items in your listitem layout file, e.g. listitem_language.xml, which is inflated in the adapter. (ImageView+TextView in the example below.)

    Final result:

    enter image description here