60

Is it possible to create an individual preference in an PreferenceScreen?

I would like to code color settings like that:

Color Preference

I know that choosing the color is easy realizable with the ListPreference, but it would be awesome with that kind of "checkboxes".

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
Jonas
  • 1,432
  • 3
  • 20
  • 27

5 Answers5

92

The Android Developer page only shows how to make a DialogFragment. It's still possible to customise the appearance of a Preference item though. In your XML you have to declare the root element as android:id="@android:id/widget_frame, and then declare TextView as android:title and android:summary. You can then declare other elements you want to appear in the layout. Here's an example showing a SeekBar which you could easily adapt to a multi-checkbox colour chooser.

seekbar_preference.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/widget_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@android:id/title"
        style="@android:style/TextAppearance.DeviceDefault.SearchResult.Title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title" />

    <TextView
        android:id="@android:id/summary"
        style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Summary" />

    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Then, in a class that derives from Preference, override the onCreateView() method:

SeekbarPreference.java

@Override
protected View onCreateView( ViewGroup parent )
{
  LayoutInflater li = (LayoutInflater)getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
  return li.inflate( R.layout.seekbar_preference, parent, false);
}

Then in the preferences.xml file use the preference:

preferences.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <com.example.SeekbarPreference
        android:key="pref_max_volume"
        android:title="@string/max_volume" />
    <com.example.SeekbarPreference
        android:key="pref_balance"
        android:title="@string/balance" />

</PreferenceScreen>

This gives a preference that looks as follows:

Seekbar preference item

You can easily adapt this method to show multiple checkboxes on a row as was in the original question.

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Dave
  • 2,248
  • 1
  • 21
  • 28
  • I wonder how to make such a custom preference with colors disabled.. anyone? – Ewoks May 05 '15 at 21:23
  • in this example `onCreateView()` called twice, and you should call `super.onCreate()` – theroom101 Aug 04 '16 at 08:36
  • 1
    Note that the layout for `SeekBarPreference` can actually be found here: `/platforms/android-24/data/res/layout/preference_widget_seekbar.xml`. The Android layouts do not use the `widget_frame` id as root elements, why do you say that has to be done? Actually, there is a comment in the XML saying "Preference should place its actual preference widget here." which comes *after* the title and summary `TextView`s but before a `LinearLayout` with id `widget_frame'. – user905686 Oct 22 '16 at 18:47
  • 1
    Hello Thank you for this wonderful guide, i have one concern if i want to change the values of the Title and the summary and implement a click listener on the layout how its done ? thank you! – Z. Kiwan Feb 04 '17 at 15:11
  • WARN: this sol is based on old preferences. New API (from v7 support lib) use little bit diff approach. See below stackoverflow.com/a/33842737 – Grzegorz Dev Jul 26 '18 at 14:08
  • 1
    In order to match the layout to the rest of the built-in preferences, you can find the latest androidx preference layout here: https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/preference/preference/res/layout/preference.xml copy it, modify it and watch for changes when you update AndroidX. – now Oct 05 '20 at 19:55
50

This is how I do it, using support library preference-v7.

preference

layout/preference_theme.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <Button android:id="@+id/theme_light" ... />
    <Button android:id="@+id/theme_dark"... />
    <Button android:id="@+id/theme_sepia"... />
    <Button android:id="@+id/theme_green"... />
</LinearLayout>

PreferenceTheme.java (custom Preference class)

import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;

public class PreferenceTheme extends Preference {

    public PreferenceTheme(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PreferenceTheme(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWidgetLayoutResource(R.layout.preference_theme);
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        holder.itemView.setClickable(false); // disable parent click
        View button = holder.findViewById(R.id.theme_dark);
        button.setClickable(true); // enable custom view click
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // persist your value here
            }
        });
        // the rest of the click binding
    }
}

preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.preference.PreferenceCategory
        android:title="Reading">
        <example.com.preference.PreferenceTheme
            android:key="pref_theme"
            android:title="Theme"
            android:defaultValue="light" />
        ...
    </android.support.v7.preference.PreferenceCategory>
</android.support.v7.preference.PreferenceScreen>
Steve Liddle
  • 3,685
  • 3
  • 27
  • 39
hidro
  • 12,333
  • 6
  • 53
  • 53
  • 1
    Can you give me a clue how to create and use `PreferenceTheme` instance in the `PreferenceActivity` with constructor parameters (context and attrs) and `findPreference`? – Sdghasemi Jan 23 '16 at 00:59
  • 4
    I wrote a blog post about using `preference-v7` here if you are interested http://www.hidroh.com/2015/11/30/building-custom-preferences-v7/ – hidro Jan 23 '16 at 02:43
  • 1
    How can I apply this if I use androidx instead of the support libs? – Anton Makov Apr 16 '19 at 00:53
8

Creating a custom preference is similar to creating a fragment or other UI components, by defining views and actions.

Android developers has a good guide on creating settings, which includes a section for creating custom preferences: http://developer.android.com/guide/topics/ui/settings.html#Custom

tbkn23
  • 5,205
  • 8
  • 26
  • 46
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/28537969) – Rob Mar 16 '21 at 16:51
  • Considering this answer is from 2013, and android released dozens of updates to its API, that might actually be a good thing :) But I see what you're saying. – tbkn23 Mar 17 '21 at 06:39
6

You can create your custom layout for preference and you can set it in android:layout attribute in Preference in res/xml like this:

<Preference
    ......................
    android:layout="@layout/your_layout" />

Or you can use an Activity instead of preference

Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
stinepike
  • 54,068
  • 14
  • 92
  • 112
0

Or a better alternative will be to extend the DialogPreference class. You can set a color picker widget as the dialog view and get the positive result inside the extended preference itself. Here is a similar question, but it is very useful for your scenario.

Community
  • 1
  • 1
C--
  • 16,393
  • 6
  • 53
  • 60