33

I have a simple preference screen defined like this

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory android:title="Security">
        <CheckBoxPreference 
            android:title="Require Pin on Start"
            android:summary="Require pin to run the application"
            android:key="@string/pref_require_pin"
            android:defaultValue="false" />
    </PreferenceCategory>

    <PreferenceCategory android:title="Settings">
        <ListPreference
           android:title="History Age (in days)"
           android:summary="Display items up to 30 days old"
           android:key="@string/pref_history_days"
           android:defaultValue="30"
           android:entries="@array/days_list"
           android:entryValues="@array/days_list"
           android:dialogTitle="Select History Age"/>
    </PreferenceCategory>
</PreferenceScreen>

I have a style setup already and used elsewhere in my app.

<style name="ListHeader">
    <item name="android:textColor">#000000</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textSize">12sp</item>
    <item name="android:background">#cccccc</item>
    <item name="android:paddingTop">6px</item>
    <item name="android:paddingBottom">6px</item>
    <item name="android:paddingLeft">12px</item>
</style>

and here is my activity

public class PreferencesActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.layout.preferences);
    }
}

How do I apply my custom style to the PreferenceCategory heading?

inazaruk
  • 74,247
  • 24
  • 188
  • 156
Josh
  • 16,286
  • 25
  • 113
  • 158

4 Answers4

57

You should take a look at Preference.Category style:

<style name="Preference.Category">
    <item name="android:layout">@android:layout/preference_category</item>
   <item name="android:shouldDisableView">false</item>
   <item name="android:selectable">false</item>
</style>

Let's take a look at preference_category.xml file:

<!-- Layout used for PreferenceCategory in a PreferenceActivity. -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    style="?android:attr/listSeparatorTextViewStyle"
    android:id="@+android:id/title"
/>

So you need to create custom theme that extends default android Theme and override listSeparatorTextViewStyle value with ListHeader style. And then apply this theme to Activity that extends PreferenceActivity .


Here is how you can do it.

First, in your styles.xml add next code:

<style name="PreferenceListHeader" 
       parent="@android:style/Widget.TextView.ListSeparator">

    <item name="android:textColor">#000000</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textSize">12sp</item>
    <item name="android:background">#cccccc</item>
    <item name="android:paddingTop">6px</item>
    <item name="android:paddingBottom">6px</item>
    <item name="android:paddingLeft">12px</item>
</style>

<style name="Theme.Custom" parent="@android:style/Theme">
    <item name="android:listSeparatorTextViewStyle">@style/PreferenceListHeader</item>               
</style>

Then in your AndroidManifest.xml add theme to your preference acitivity:

 <activity android:name=".MyPreferencesActivity" 
           android:theme="@style/Theme.Custom" 
           ... >
 ...
 </activity>

Here is a screenshot:

enter image description here

inazaruk
  • 74,247
  • 24
  • 188
  • 156
  • I'm not sure how to override `listSeparatorTextViewStyle` – Josh Jun 09 '11 at 19:01
  • Awesome. That's exactly what I was looking for. – Josh Jun 09 '11 at 20:37
  • 3
    you don't have to use a style you can just use the android:layout attribute on the PreferenceCategory – Nathan Schwermann Jul 23 '11 at 02:40
  • @schwiz - Your comment is great, but the layout that I added doesn't display the "title" attribute. Is there an example layout.xml file that works with your suggestion? – Camille Sévigny Feb 17 '12 at 15:28
  • 9
    Got some problems with "@android:style/Widget.TextView.ListSeparator", so I replaced it with "@android:attr/listSeparatorTextViewStyle". Don't forget to define layout_height and layout_width. – joshas Feb 23 '12 at 22:10
  • @CamilleSévigny here is a link to the layout in the android repo https://github.com/android/platform_frameworks_base/blob/gingerbread/core/res/res/layout/preference.xml – Nathan Schwermann Feb 26 '12 at 22:53
  • 6
    "error retrieving parent for item: No resources found that matches the given name @android:style/Widget.TextView.ListSeparator" – Marcin S. Sep 30 '12 at 02:22
  • maybe use @android:attr/listSeparatorTextViewStyle instead as said above – An-droid Mar 27 '14 at 13:36
  • @CamilleSévigny you need to use id as `android:id="@android:id/title"` then it will pick automatically! P.S: Know its late may help some one some day :) – Muhammad Babar Oct 02 '14 at 10:41
  • please note Lollipop uses different styles for preferences and categories. Take a look at https://github.com/android/platform_frameworks_base/blob/lollipop-mr1-release/core/res/res/values/styles_material.xml#L57 – Vadim Kotov May 14 '15 at 16:49
  • 3
    This answer just doesn't work anymore. android:style/Widget.TextView.ListSeparator doesn't exist, and android:attr/listSeparatorTextViewStyle" doesn't exist either. If you try to do exactly what's in this solution but pick any existing style to get it to compile, the app just crashes. I can't believe this is so messed up. – Stevey May 13 '17 at 07:55
19

@inazaruk gave the answer well enough but since the recent updates, ADT 18 and above, there are some restrictions on the styles giving the error

Error retrieving parent for item: No resource found that matches the given name '@android:style/Widget.TextView.ListSeparator'.

See this link for reason of the problem and the solution. Since this post doesn't provide a code for understanding i am providing my code here

 <style name="Widget.TextView.ListSeparator" parent="@android:style/Widget.TextView">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textSize">14sp</item>
    <item name="android:gravity">center_vertical</item>
</style>

<style name="PreferenceListHeader" parent="Widget.TextView.ListSeparator">
    <item name="android:textColor">#000000</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textSize">18sp</item>
    <item name="android:background">#cccccc</item>
    <item name="android:paddingTop">6dp</item>
    <item name="android:paddingBottom">6dp</item>
    <item name="android:paddingLeft">12dp</item>
</style>

<style name="PreferenceScreen" parent="android:Theme.NoTitleBar">
    <item name="android:listSeparatorTextViewStyle">@style/PreferenceListHeader</item>
    <item name="android:background">#F2B1DBF3</item>
</style>
Rohan Kandwal
  • 9,112
  • 8
  • 74
  • 107
  • 2
    Thanks for the update. I just ran into this exact problem with API level 19! – Robert Hui Sep 24 '14 at 01:27
  • i don't see any separator line? – Muhammad Babar Oct 02 '14 at 11:03
  • I have the same problem. I no longer see a divider line for the preference category. How do I enable this? – NSouth Dec 18 '14 at 17:52
  • In theory, you should be able to specify your own layout and put whatever you want into it (a line or anything else). In practice, it doesn't seem to work, it's ignored. – Gábor Jan 21 '15 at 19:09
  • 2
    please note Lollipop uses different styles for preferences and categories. Take a look at https://github.com/android/platform_frameworks_base/blob/lollipop-mr1-release/core/res/res/values/styles_material.xml#L57 – Vadim Kotov May 14 '15 at 16:50
  • This will no longer compile because Widget.TextView.ListSeparator is now private. This answer helped: https://stackoverflow.com/a/38279736/1870884 – Tina Jun 17 '21 at 20:13
15

Styling it as described in inazaruk's answer is simple enough but it only changes the style of the text in the heading, it doesn't offer a way to specify a whole new layout (the style will not apply the layout item). There is, however, a straightforward solution if you extend the class:

public class MyPreferenceCategory extends PreferenceCategory {

  public MyPreferenceCategory(Context context) {
    super(context);
    setLayoutResource(R.layout.yourlayout);
  }

  public MyPreferenceCategory(Context context, AttributeSet attrs) {
    super(context, attrs);
    setLayoutResource(R.layout.yourlayout);
  }
}

and simply use this instead of the original PreferenceCategory when defining your Preferences layout.

Your layout can, of course, have anything you like, including lines above or below the text, different backgrounds, padding, whatever. For instance, this will show a material design colored subtitle with a line above:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginTop="6dp"
        android:background="?attr/divider_color" />
    <TextView
        android:id="@+android:id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:padding="12dp"
        android:textColor="?attr/colorAccent"
        android:textSize="14sp"
        android:textStyle="bold" />
</LinearLayout>
Gábor
  • 9,466
  • 3
  • 65
  • 79
11

Like Gábor's answer but instead of extending PreferenceCategory you can do like this : 1. Make your custom layout.I named it preference_category.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:layout_marginTop="6dp"
    android:background="?attr/divider_color" />
<TextView
    android:id="@+android:id/title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:padding="12dp"
    android:textColor="?attr/colorAccent"
    android:textSize="14sp"
    android:textStyle="bold" />
</LinearLayout>

Important : the layout must contain a textview with this id : android:id="@+android:id/title"

2.and in the preference add this line : android:layout="@layout/preference_category"

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

<PreferenceCategory
    android:key="Font_Settings"
    android:title="@string/UISetting"
    android:layout="@layout/preference_category" >
... //  other preferences in the category
</PreferenceCategory>
</PreferenceScreen>
Ali.DM
  • 258
  • 5
  • 10