116

I was having trouble using the new Material Design toolbar in the support library on a Preference screen.

I have a settings.xml file as below:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/AddingItems"
        android:key="pref_key_storage_settings">

        <ListPreference
            android:key="pref_key_new_items"
            android:title="@string/LocationOfNewItems"
            android:summary="@string/LocationOfNewItemsSummary"
            android:entries="@array/new_items_entry"
            android:entryValues="@array/new_item_entry_value"
            android:defaultValue="1"/>

    </PreferenceCategory>
</PreferenceScreen>

The strings are defined elsewhere.

James Cross
  • 7,799
  • 8
  • 24
  • 30

13 Answers13

112

Please find the GitHub Repo: Here


A bit late to the party, but this is my solution that I am using as a work around continuing to use PreferenceActivity:

settings_toolbar.xml :

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?attr/actionBarSize"
    app:navigationContentDescription="@string/abc_action_bar_up_description"
    android:background="?attr/colorPrimary"
    app:navigationIcon="?attr/homeAsUpIndicator"
    app:title="@string/action_settings"
    />

SettingsActivity.java :

public class SettingsActivity extends PreferenceActivity {

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
        Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
        root.addView(bar, 0); // insert at top
        bar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

}

Result :

example


UPDATE (Gingerbread Compatibility) :

As per the comments, Gingerbread Devices are returning NullPointerException on this line:

LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();

FIX:

SettingsActivity.java :

public class SettingsActivity extends PreferenceActivity {

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        Toolbar bar;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            LinearLayout root = (LinearLayout) findViewById(android.R.id.list).getParent().getParent().getParent();
            bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
            root.addView(bar, 0); // insert at top
        } else {
            ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
            ListView content = (ListView) root.getChildAt(0);

            root.removeAllViews();

            bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
            

            int height;
            TypedValue tv = new TypedValue();
            if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
                height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
            }else{
                height = bar.getHeight();
            }

            content.setPadding(0, height, 0, 0);

            root.addView(content);
            root.addView(bar);
        }

        bar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
}

Any issues with the above let me know!


UPDATE 2: TINTING WORKAROUND

As pointed out in many dev notes PreferenceActivity does not support tinting of elements, however by utilising a few internal classes you CAN achieve this. That is until these classes are removed. (Works using appCompat support-v7 v21.0.3).

Add the following imports:

import android.support.v7.internal.widget.TintCheckBox;
import android.support.v7.internal.widget.TintCheckedTextView;
import android.support.v7.internal.widget.TintEditText;
import android.support.v7.internal.widget.TintRadioButton;
import android.support.v7.internal.widget.TintSpinner;

Then override the onCreateView method:

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    // Allow super to try and create a view first
    final View result = super.onCreateView(name, context, attrs);
    if (result != null) {
        return result;
    }

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        // If we're running pre-L, we need to 'inject' our tint aware Views in place of the
        // standard framework versions
        switch (name) {
            case "EditText":
                return new TintEditText(this, attrs);
            case "Spinner":
                return new TintSpinner(this, attrs);
            case "CheckBox":
                return new TintCheckBox(this, attrs);
            case "RadioButton":
                return new TintRadioButton(this, attrs);
            case "CheckedTextView":
                return new TintCheckedTextView(this, attrs);
        }
    }

    return null;
}

Result:

example 2


AppCompat 22.1

AppCompat 22.1 introduced new tinted elements, meaning that there is no longer a need to utilise the internal classes to achieve the same effect as the last update. Instead follow this (still overriding onCreateView):

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    // Allow super to try and create a view first
    final View result = super.onCreateView(name, context, attrs);
    if (result != null) {
        return result;
    }

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        // If we're running pre-L, we need to 'inject' our tint aware Views in place of the
        // standard framework versions
        switch (name) {
            case "EditText":
                return new AppCompatEditText(this, attrs);
            case "Spinner":
                return new AppCompatSpinner(this, attrs);
            case "CheckBox":
                return new AppCompatCheckBox(this, attrs);
            case "RadioButton":
                return new AppCompatRadioButton(this, attrs);
            case "CheckedTextView":
                return new AppCompatCheckedTextView(this, attrs);
        }
    }

    return null;
}

NESTED PREFERENCE SCREENS

A lot of people are experiencing issues with including the Toolbar in a nested <PreferenceScreen /> however, I have found a solution!! - After a lot of trial and error!

Add the following to your SettingsActivity:

@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    super.onPreferenceTreeClick(preferenceScreen, preference);

    // If the user has clicked on a preference screen, set up the screen
    if (preference instanceof PreferenceScreen) {
        setUpNestedScreen((PreferenceScreen) preference);
    }

    return false;
}

public void setUpNestedScreen(PreferenceScreen preferenceScreen) {
    final Dialog dialog = preferenceScreen.getDialog();

    Toolbar bar;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        LinearLayout root = (LinearLayout) dialog.findViewById(android.R.id.list).getParent();
        bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
        root.addView(bar, 0); // insert at top
    } else {
        ViewGroup root = (ViewGroup) dialog.findViewById(android.R.id.content);
        ListView content = (ListView) root.getChildAt(0);

        root.removeAllViews();

        bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);

        int height;
        TypedValue tv = new TypedValue();
        if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
            height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
        }else{
            height = bar.getHeight();
        }

        content.setPadding(0, height, 0, 0);

        root.addView(content);
        root.addView(bar);
    }

    bar.setTitle(preferenceScreen.getTitle());

    bar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            dialog.dismiss();
        }
    });
}

The reason that PreferenceScreen's are such a pain is because they are based as a wrapper dialog, so we need to capture the dialog layout to add the toolbar to it.


Toolbar Shadow

By design importing the Toolbar does not allow for elevation and shadowing in pre-v21 devices, so if you would like to have elevation on your Toolbar you need to wrap it in a AppBarLayout:

settings_toolbar.xml :

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

   <android.support.v7.widget.Toolbar
       .../>

</android.support.design.widget.AppBarLayout>

Not forgetting to add the add the Design Support library as a dependency in build.gradle file:

compile 'com.android.support:support-v4:22.2.0'
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:design:22.2.0'

Android 6.0

I have investigated the reported overlapping issue and I cannot reproduce the issue.

The full code in use as above produces the following:

enter image description here

If I am missing something please let me know via this repo and I will investigate.

Community
  • 1
  • 1
David Passmore
  • 6,089
  • 4
  • 46
  • 70
  • it give nullpointer exception in gingebread, root is null..any solution? – andQlimax Dec 29 '14 at 12:13
  • your solution works great. but there is a problem with this approach, infact without extending ActionBarActivity which is mandatory (from documentation) to get the material theme on <5.0, colorAccent (just to make an example) is not applied to checkboxes in devices < 5.0. This seems a real pain..maybe I have to remove the preference activity and use a linear layout to simulate a preference screen, otherwise I don't see any way to use material theme in devices from api level 8 to 21. Preference fragment is "only" >11 :( – andQlimax Jan 03 '15 at 18:55
  • @andQlimax I am using this on all my devices. From api 10 to api 21 and all work. Can you clarify? – David Passmore Jan 03 '15 at 19:00
  • See here for example: https://code.google.com/p/android/issues/detail?id=79436 that happens because the activity does not extend action bar activity. See the screenshots, the material theme is not fully applied in pre lollipop devices – andQlimax Jan 03 '15 at 19:18
  • @andQlimax Ok I will have to look into this and I will update when i have any information. – David Passmore Jan 03 '15 at 19:28
  • 1
    @andQlimax I have updated my answer with a solution for the tinting issue – David Passmore Mar 13 '15 at 22:35
  • This solution was working fine for me. However, I updated my appcompat v7 to 22.0.0 and now, this line `LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();` throws an error: `java.lang.ClassCastException: android.support.v7.internal.widget.FitWindowsFrameLayout cannot be cast to android.widget.LinearLayout` – Ferran Negre Mar 22 '15 at 14:43
  • @FerranNegre I don't see why there is any issues here as there has been no changes to the layouts as per here https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/layout/preference_list_content.xml . I will investigate further and come back with any updates – David Passmore Mar 22 '15 at 19:18
  • Great answer, thanks for the help you've provided us all! However, I'm having troubles getting a shadow to appear beneath the bar. Any advice? – Zach Sperske Jun 29 '15 at 19:17
  • @ZachSperske You could try wrapping the Toolbar in a AppBarLayout similar to this answer on SO - http://stackoverflow.com/a/31026359/566127 See my update for an example. – David Passmore Jun 29 '15 at 19:25
  • Sadly, doesn't seem to work on pre L devices. Thanks for the reply though! – Zach Sperske Jun 29 '15 at 21:16
  • @DavidPassmore I thought of something similar (as at the top of the answer) but the `content_frame` in `getFragmentManager().beginTransaction().replace(R.id.content_frame, new MyPreferenceFragment()).commit();` has a Red underline saying me something is wrong. I did create a `frame` in xml file with same id. Not sure where the problem is. – Srujan Barai Aug 31 '15 at 18:43
  • Which PreferenceActivity are you using? Default old android or something from support libraray. Could ypu please be exact with class. – f470071 Nov 03 '15 at 15:20
  • @f470071 I am using the base `PreferenceActivity` class – David Passmore Nov 19 '15 at 21:20
  • 3
    @DavidPassmore for me the preference list is overlapping on the toolbar – Shashank Srivastava Dec 23 '15 at 11:20
  • 1
    @ShashankSrivastava This is reflected if you are using Android 6, I am working on a solution for this. Thanks for the update. – David Passmore Dec 23 '15 at 20:45
  • This code produced NullPointerException, while using on 4.0.4 (Sony Ericsson). I had to use: Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN instead of ICS. – Arkadiusz 'flies' Rzadkowolski Jan 29 '16 at 10:18
  • on N Preview it gives 'ClassCastException' can't cast FrameLayout to LinearLayout on setting up nested screen. – khusrav May 29 '16 at 07:30
  • you have to getParent() again to access the root LinearLayout. – khusrav May 29 '16 at 07:37
  • Like @khusrav says, on API 24 you have to add another `getParent()` – ShadowGod Oct 16 '16 at 06:25
  • @David Passmore very nice example but in the notifications screen the checkbox is not visible. I am on API 25. Is there a fix? Code is according your github. EDIT: solved I applied the wrong theme in the manifest – Simon Nov 29 '16 at 17:30
  • You are awesome :) – Pratik Butani Apr 03 '17 at 18:09
107

You can use a PreferenceFragment, as an alternative to PreferenceActivity. So, here is the wrapping Activity example:

public class MyPreferenceActivity extends ActionBarActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pref_with_actionbar);

        android.support.v7.widget.Toolbar toolbar = (android.support.v7.widget.Toolbar) findViewById(uk.japplications.jcommon.R.id.toolbar);
        setSupportActionBar(toolbar);

        getFragmentManager().beginTransaction().replace(R.id.content_frame, new MyPreferenceFragment()).commit();
    }
}

And here is the layout file (pref_with_actionbar):

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_height="@dimen/action_bar_height"
        android:layout_width="match_parent"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:theme="@style/ToolbarTheme.Base"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_below="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

And finally the PreferenceFragment:

public static class MyPreferenceFragment extends PreferenceFragment{
    @Override
    public void onCreate(final Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);
    }
}
starball
  • 20,030
  • 7
  • 43
  • 238
James Cross
  • 7,799
  • 8
  • 24
  • 30
  • 39
    I have tried this approach. The problem is it doesn't displays the toolbar in child preference screens. – Madhur Ahuja Oct 26 '14 at 04:15
  • Humm, I didn't try using child ones. You mean the PreferenceHeader approach? – James Cross Oct 26 '14 at 08:08
  • 2
    I think he's talking about PreferenceScreen embeded in the root preference XML. – Lucas S. Nov 02 '14 at 21:08
  • 5
    I liked the approach, but sadly won't work if target api is less than API 11 – midhunhk Dec 02 '14 at 15:54
  • 12
    It will work with neither. Practically, there doesn't seem to be any way to create materially designed, toolbared, nested preference screens. If you use an `ActionBarActivity` to get the toolbar and related functions, there will be no `onBuildHeaders()` to override and no actual preference support in the activity. If you use the old `PreferenceActivity`, you don't have the toolbar and related functions (yes, you can have a `Toolbar` and layout but you can't call `setSupportActionBar()`. So, either with preference headers or nested preference screens, we seem stuck. – Gábor Dec 10 '14 at 23:42
  • No support for `loadHeadersFromResource` - that's what makes a PreferenceActivity. – vbence Jan 03 '15 at 17:41
  • I implemented this and it works fine, but I noticed that a simple `CheckBoxPreference` does not animate anymore... – anthonylawson Feb 23 '15 at 16:48
  • 1
    I agree with Gabor's comment. This solution does not work in general. There are better one bellow with emulating Toolbar (no ActionBar, but who cares) and also new support lib released with AppCompatDelegate on board. – Eugene Wechsler Apr 27 '15 at 01:09
  • Thanks! I was using fragment tag in the XML instead of a simple FrameLayout. – Joaquin Iurchuk Apr 27 '15 at 16:55
  • Can confirm that this solution does not work (compiling for API 23) – The 29th Saltshaker Apr 10 '16 at 17:38
  • However PreferenceFragment doesn't support header-group which can only be used in PrefeenceActivity. – TeeTracker May 04 '16 at 12:29
  • I tried this and am getting this error ``` java.lang.ClassCastException: android.support.v7.preference.PreferenceScreen cannot be cast to android.preference.GenericInflater$Parent``` any help? – dsharew Feb 06 '17 at 09:37
  • In [Support Library Revision Archive, Revision 22.1.0](https://developer.android.com/topic/libraries/support-library/rev-archive.html#rev22-1-0), Changes for v7 appcompat library, it says: "Updated the `AppCompatActivity` as the base class for activities that use the support library action bar features. This class replaces the **deprecated** `ActionBarActivity`." – Eido95 Sep 04 '17 at 18:15
47

Completely new update.

With some experimentation, I seem to have found the working AppCompat 22.1+ solution for nested preference screens.

First, as it's mentioned in many answers (including one here), you'll need to use the new AppCompatDelegate. Either use the AppCompatPreferenceActivity.java file from the support demos (https://android.googlesource.com/platform/development/+/58bf5b99e6132332afb8b44b4c8cedf5756ad464/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java) and simply extend from it, or copy the relevant functions into your own PreferenceActivity. I'll show the first approach here:

public class SettingsActivity extends AppCompatPreferenceActivity {

  @Override
  public void onBuildHeaders(List<Header> target) {
    loadHeadersFromResource(R.xml.settings, target);

    setContentView(R.layout.settings_page);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    ActionBar bar = getSupportActionBar();
    bar.setHomeButtonEnabled(true);
    bar.setDisplayHomeAsUpEnabled(true);
    bar.setDisplayShowTitleEnabled(true);
    bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
    bar.setTitle(...);
  }

  @Override
  protected boolean isValidFragment(String fragmentName) {
    return SettingsFragment.class.getName().equals(fragmentName);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case android.R.id.home:
        onBackPressed();
        break;
    }
    return super.onOptionsItemSelected(item);
  }
}

The accompanying layout is rather simple and usual (layout/settings_page.xml):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="0dp"
    android:orientation="vertical"
    android:padding="0dp">
  <android.support.v7.widget.Toolbar
      android:id="@+id/toolbar"
      android:layout_width="match_parent"
      android:layout_height="?attr/actionBarSize"
      android:background="?attr/colorPrimary"
      android:elevation="4dp"
      android:theme="@style/..."/>
  <ListView
      android:id="@id/android:list"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>
</LinearLayout>

The preferences themselves are defined as usual (xml/settings.xml):

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
  <header
      android:fragment="com.example.SettingsFragment"
      android:summary="@string/..."
      android:title="@string/...">
    <extra
        android:name="page"
        android:value="page1"/>
  </header>
  <header
      android:fragment="com.example.SettingsFragment"
      android:summary="@string/..."
      android:title="@string/...">
    <extra
        android:name="page"
        android:value="page2"/>
  </header>
  ...
</preference-headers>

No real difference to solutions on the net until this point. Actually, you can use this even if you don't have nested screens, no headers, just a single screen.

We use a common PreferenceFragment for all deeper pages, differentiated by the extra parameters in the headers. Each page will have a separate XML with a common PreferenceScreen inside (xml/settings_page1.xml et al.). The fragment uses the same layout as the activity, including the toolbar.

public class SettingsFragment extends PreferenceFragment {

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getActivity().setTheme(R.style...);

    if (getArguments() != null) {
      String page = getArguments().getString("page");
      if (page != null)
        switch (page) {
          case "page1":
            addPreferencesFromResource(R.xml.settings_page1);
            break;
          case "page2":
            addPreferencesFromResource(R.xml.settings_page2);
            break;
          ...
        }
    }
  }

  @Override
  public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View layout = inflater.inflate(R.layout.settings_page, container, false);
    if (layout != null) {
      AppCompatPreferenceActivity activity = (AppCompatPreferenceActivity) getActivity();
      Toolbar toolbar = (Toolbar) layout.findViewById(R.id.toolbar);
      activity.setSupportActionBar(toolbar);

      ActionBar bar = activity.getSupportActionBar();
      bar.setHomeButtonEnabled(true);
      bar.setDisplayHomeAsUpEnabled(true);
      bar.setDisplayShowTitleEnabled(true);
      bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
      bar.setTitle(getPreferenceScreen().getTitle());
    }
    return layout;
  }

  @Override
  public void onResume() {
    super.onResume();

    if (getView() != null) {
      View frame = (View) getView().getParent();
      if (frame != null)
        frame.setPadding(0, 0, 0, 0);
    }
  }
}

Finally, a quick summary of how this actually works. The new AppCompatDelegate allows us to use any activity with AppCompat features, not only those extending from the activities actually in AppCompat. This means that we can turn the good old PreferenceActivity into a new one and add the toolbar as usual. From that point on, we can stick to the old solutions regarding preference screens and headers, without any deviation from the existing documentation. There is just one important point: don't use onCreate() in the activity because it will lead to errors. Use onBuildHeaders() for all operations like adding the toolbar.

The only real difference is, and that's what makes it work with nested screens is that you can use the same approach with the fragments. You can use their onCreateView() the same way, inflating your own layout instead of the system one, adding the toolbar the same way as in the activity.

Vlad
  • 820
  • 10
  • 29
Gábor
  • 9,466
  • 3
  • 65
  • 79
  • 2
    What a great little workaround! This is the only solution I've found which will show the material toolbar on a descendant PreferenceScreen. Well done sir. – Sterling Jan 17 '15 at 16:27
  • I use the resource from the appcompat library for the up-icon: `R.drawable.abc_ic_ab_back_mtrl_am_alpha` – Ridcully Jul 04 '15 at 12:38
  • With this solution, I think the Toolbar will scroll with the content right? Because it is just an item in the internal ListView. – tasomaniac Jul 21 '15 at 21:39
  • Not with this updated, new solution. That works just as expected. – Gábor Sep 07 '15 at 10:45
  • Strangely, this solution doesn't seem to recognize a `PreferenceFragmentCompat` instead of `PreferenceFragment`. Setting up a `preference-header` with `xmlns:app="http://schemas.android.com/apk/res-auto" ` and then `app:fragment` instead of `android:fragment` doesn't load any new prefs screen. So having issues w/the backwards compatibility... suggestions? – fattire Oct 01 '15 at 23:05
  • Just a shot in the dark but maybe if you specify both? – Gábor Oct 02 '15 at 07:58
  • I don't understand how this could work, the fragment that extends PreferenceFragment needs a recyclerview which is obtainted by calling super.oncreateview(), however if we call super.oncreateview() then return a custom view, none of the preferences show up in the fragment. – Tyler Pfaff Nov 03 '16 at 19:26
  • sorry to necromance, but any idea what causes this in AppCompatPreferenceActivity in your solution? Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.ViewGroup.setLayoutTransition(android.animation.LayoutTransition)' on a null object reference – mbac32768 May 07 '19 at 21:55
17

If you want to use PreferenceHeaders you can use the following approach:

import android.support.v7.widget.Toolbar;

public class MyPreferenceActivity extends PreferenceActivity

   Toolbar mToolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
        LinearLayout content = (LinearLayout) root.getChildAt(0);
        LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.activity_settings, null);

        root.removeAllViews();
        toolbarContainer.addView(content);
        root.addView(toolbarContainer);

        mToolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar);
    }

    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }

    // Other methods

}

layout/activity_settings.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_height="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:theme="@style/AppTheme"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

</LinearLayout>

You can use whatever layout you prefer here, just make sure you adjust it in the Java code as well.

And finally, your file with headers (xml/pref_headers.xml)

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">

    <header
        android:fragment="com.example.FirstFragment"
        android:title="@string/pref_header_first" />
    <header
        android:fragment="com.example.SecondFragment"
        android:title="@string/pref_header_second" />

</preference-headers>
Dalinaum
  • 1,142
  • 13
  • 18
Sven Dubbeld
  • 384
  • 1
  • 7
  • root.addView(toolbar); why? root.addView(toolbarContainer); – Crossle Song Nov 12 '14 at 04:40
  • Whoops, missed a variable while renaming, fixed it. – Sven Dubbeld Nov 13 '14 at 12:22
  • 1
    Excellent answer. The key here is `android.R.id.content`, considering we used to pass a `ListView` with `android.R.id.list` for the preference list itself (and still do if using the fragment-less, headerless, way) instead. – davidcesarino Dec 09 '14 at 20:34
  • 2
    I think it's better to check the code of Android, to see what it needs, instead of messing around with its views hirerchy (remove/add views it has). I think it's safer this way. I suggest checking out the file "preference_list_content" . – android developer Dec 25 '14 at 11:18
  • 2
    This is the best answer in this thread. Author of this [article](https://xisberto.wordpress.com/2014/11/08/how-to-combine-actionbar-and-preferenceactivity-headers-with-appcompat/) expanded it to full reference implementation, which I used. In fact, this is the only working solution for supporting sophisticated preferences in your app. – Eugene Wechsler Apr 27 '15 at 00:55
17

With the release of the Android Support Library 22.1.0 and the new AppCompatDelegate, here you can find a nice sample of an implementation of the PreferenceActivity with material support with backwards compatibility.

Update It works on nested screens too.

https://android.googlesource.com/platform/development/+/marshmallow-mr3-release/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java

F43nd1r
  • 7,690
  • 3
  • 24
  • 62
MrBrightside
  • 2,669
  • 3
  • 17
  • 24
  • 1
    Oh, that's great news! So it appears that solutions based on "extend PreferenceActivity" are better than those on "extend ActionBarActivity" in this new perspective. – Eugene Wechsler Apr 27 '15 at 01:02
  • 1
    @EugeneWechsler Yes, indeed, ActionBarActivity has been deprecated now. – MrBrightside Apr 27 '15 at 06:36
  • Work this solution also on nested screens? Is there a better example? – Tomas May 04 '15 at 06:03
  • @Tomas I haven't tried yet, but it should work on nested screens too. If works for you, tell us please. – MrBrightside May 04 '15 at 07:00
  • Thanks a lot ! Working for me on a Galaxy Nexus (4.3) and on the emulator with nested screens (lollipop). – Tim Autin May 08 '15 at 23:55
  • TimAutin or @Tomas Do you guys have an example of nested fragment preferences? I am trying this approach... I load the first fragment on onCreate using beginTransiction.replace(), then I click to a preference and it loads the second fragment but without the Toolbar. Also, if I add a custom layout to my PreferenceActivity, the nested fragment does not respect it. Could we extend this answer with a working example/code? – Ferran Negre May 13 '15 at 15:51
  • Sorry, but I'm not using nested screens. Maybe @Tomas can help you posting a sample code. – MrBrightside May 13 '15 at 15:56
  • @FerranNegre I only try it, but finally I don't use nested screens. But when I tried it, it's was working fine. I remember about combination AppCompatDelegate and [PreferenceHeaders](http://stackoverflow.com/a/26614696/3796931) and maybe some useful information from [this page](https://medium.com/google-developer-experts/how-to-add-toolbar-to-an-activity-which-doesn-t-extend-appcompatactivity-a07c026717b3), but I am not sure, I don't keep the test code. Sorry for that :( – Tomas May 18 '15 at 10:38
  • I couldn't get it to work with truly nested preferences... one solution though, is to split your preferences up into multiple files like [this](https://github.com/Frozen-Developers/android-cache-cleaner/blob/master/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/activity/SettingsActivity.java) – Tim Rae Jun 03 '15 at 16:11
  • This look like an old 2.x preference activity. So what is the point of using it? – f470071 Nov 03 '15 at 15:21
6

While the above answers seem elaborate, if you want a quick fix solution to use Toolbar with support API 7 and up all the while extending PreferenceActivity, I got help from this project below.

https://github.com/AndroidDeveloperLB/ActionBarPreferenceActivity

activity_settings.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/app_theme_light"
    app:popupTheme="@style/Theme.AppCompat.Light"
    app:theme="@style/Theme.AppCompat" />

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/padding_medium" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

SettingsActivity.java

public class SettingsActivity extends PreferenceActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_settings);

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

    addPreferencesFromResource(R.xml.preferences);

    toolbar.setClickable(true);
    toolbar.setNavigationIcon(getResIdFromAttribute(this, R.attr.homeAsUpIndicator));
    toolbar.setTitle(R.string.menu_settings);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            finish();
        }
    });

}

private static int getResIdFromAttribute(final Activity activity, final int attr) {
    if (attr == 0) {
        return 0;
    }
    final TypedValue typedvalueattr = new TypedValue();
    activity.getTheme().resolveAttribute(attr, typedvalueattr, true);
    return typedvalueattr.resourceId;
}
}
midhunhk
  • 5,560
  • 7
  • 52
  • 83
6

I too have been looking for a solution to adding the v7 support toolbar (API 25) to the AppCompatPreferenceActivity (that is automatically created by AndroidStudio when adding a SettingsActivity). After reading several solutions and trying each of them out I struggled to get the generated PreferenceFragment examples to display with a toolbar as well.

A modified solution that sort of worked was from "Gabor".

One of the caveats I faced was 'onBuildHeaders' only fires once. If you turn a device (like a phone) sideways, the view recreates and the PreferenceActivity is left without a toolbar again, however the PreferenceFragments would retain theirs.

I tried using 'onPostCreate' to call 'setContentView', while this worked to recreate the toolbar when the orientation changed, PreferenceFragments would then be rendered blank.

What I have come up with leverages just about every tip and answer I could read about this subject. I hope others find it useful as well.

We'll start with the Java

First in (the generated) AppCompatPreferenceActivity.java I modified 'setSupportActionBar' like so:

public void setSupportActionBar(@Nullable Toolbar toolbar) {
    getDelegate().setSupportActionBar(toolbar);
    ActionBar bar = getDelegate().getSupportActionBar();
    bar.setHomeButtonEnabled(true);
    bar.setDisplayHomeAsUpEnabled(true);
}

Second, I created a new class named AppCompatPreferenceFragment.java (it is current an unused name, although it may not stay that way!):

abstract class AppCompatPreferenceFragment extends PreferenceFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_settings, container, false);
        if (view != null) {
            Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar_settings);
            ((AppCompatPreferenceActivity) getActivity()).setSupportActionBar(toolbar);
        }
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        View frame = (View) getView().getParent();
        if (frame != null) frame.setPadding(0, 0, 0, 0);
    }
}

This is the portion of Gabor's answer that worked.

Last, To get consistency we need to make some changes to SettingsActivity.java:

public class SettingsActivity extends AppCompatPreferenceActivity {

    boolean mAttachedFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mAttachedFragment = false;
        super.onCreate(savedInstanceState);
    }

    @Override
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }

    @Override
    public void onAttachFragment(Fragment fragment) {
        mAttachedFragment = true;
        super.onAttachFragment(fragment);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        //if we didn't attach a fragment, go ahead and apply the layout
        if (!mAttachedFragment) {
            setContentView(R.layout.activity_settings);
            setSupportActionBar((Toolbar)findViewById(R.id.toolbar_settings));
        }
    }

    /**
     * This fragment shows general preferences only. It is used when the
     * activity is showing a two-pane settings UI.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class GeneralPreferenceFragment extends AppCompatPreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            addPreferencesFromResource(R.xml.pref_general);
            setHasOptionsMenu(true);

            bindPreferenceSummaryToValue(findPreference("example_text"));
            bindPreferenceSummaryToValue(findPreference("example_list"));
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
            if (id == android.R.id.home) {
                startActivity(new Intent(getActivity(), SettingsActivity.class));
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
}

Some code has been left out of the activity for brevity. The key components here are 'onAttachedFragment', 'onPostCreate', and that the 'GeneralPreferenceFragment' now extends the custom 'AppCompatPreferenceFragment' instead of PreferenceFragment.

Code Summary: If a fragment is present, the fragment injects the new layout and calls the modified 'setSupportActionBar' function. If the fragment is not present, SettingsActivity injects the new layout on 'onPostCreate'

Now on to the XML (very simple):

activity_settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        layout="@layout/app_bar_settings"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

app_bar_settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/content_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".SettingsActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.NoActionBar.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar_settings"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.NoActionBar.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_settings" />

</android.support.design.widget.CoordinatorLayout>

content_settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".SettingsActivity"
    tools:showIn="@layout/app_bar_settings">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

End Result:

SettingsActivity

GeneralPreferenceFragment

SilverX
  • 1,509
  • 16
  • 18
5

I have a new (possibly neater) solution, that uses the AppCompatPreferenceActivity from the Support v7 samples. With this code in hand I created my own layout that includes a toolbar:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:fitsSystemWindows="true" tools:context="edu.adelphi.Adelphi.ui.activity.MainActivity">

    <android.support.design.widget.AppBarLayout android:id="@+id/appbar"
        android:layout_width="match_parent" android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
            android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay"/>

    </android.support.design.widget.AppBarLayout>

    <FrameLayout android:id="@+id/content"
        android:layout_width="match_parent" android:layout_height="match_parent"/>

</android.support.design.widget.CoordinatorLayout>

Then, in my AppCompatPreferenceActivity, I altered setContentView to create a my new layout, and place the provided layout inside my FrameLayout:

@Override
public void setContentView(@LayoutRes int layoutResID) {
    View view = getLayoutInflater().inflate(R.layout.toolbar, null);
    FrameLayout content = (FrameLayout) view.findViewById(R.id.content);
    getLayoutInflater().inflate(layoutResID, content, true);
    setContentView(view);
}

Then I just extend AppCompatPreferenceActivity, allowing me to call setSupportActionBar((Toolbar) findViewById(R.id.toolbar)), and inflate menu items in the toolbar as well. All while keeping the benefits of a PreferenceActivity.

Bryan
  • 14,756
  • 10
  • 70
  • 125
5

Let's keep it simple & clean here, without breaking any in-built layout

import android.support.design.widget.AppBarLayout;
import android.support.v4.app.NavUtils;
import android.support.v7.widget.Toolbar;

private void setupActionBar() {
    Toolbar toolbar = new Toolbar(this);

    AppBarLayout appBarLayout = new AppBarLayout(this);
    appBarLayout.addView(toolbar);

    final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
    final ViewGroup window = (ViewGroup) root.getChildAt(0);
    window.addView(appBarLayout, 0);

    setSupportActionBar(toolbar);

    // Show the Up button in the action bar.
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
    });
}
Samuel
  • 9,883
  • 5
  • 45
  • 57
4

I found this simple solution while working on this. First we need to create a layout for settings activity.

activity_settings.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.my.package">

    <android.support.v7.widget.Toolbar
        android:id="@+id/tool_bar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:elevation="@dimen/appbar_elevation"
        app:navigationIcon="?attr/homeAsUpIndicator"
        app:navigationContentDescription="@string/abc_action_bar_up_description"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <ListView
        android:id="@android:id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tool_bar" />

</RelativeLayout>

Make sure you add a list view with android:id="@android:id/list", otherwise it will throw NullPointerException

Next step is to add (Override) onCreate method in your settings activity

Settings.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_settings);
    Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);
    toolbar.setTitle(R.string.action_settings);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            finish();
        }
    });
}

Make sure you import android.suppoer.v7.widget.Toolbar. This should work pretty much on all APIs above 16 (Jelly Bean and up)

Apurva
  • 148
  • 1
  • 14
1

I would like to continue the marked solution of James Cross, since after that there's a problem of closing only the active nested screen (PreferenceFragment) in the way to not close the SettingsActivity as well.

Actually it does work on all nested screens (so I don't understand the solution of Gábor that I tried without success, well it works until a certain point but it's a mess of multiple toolbars), because when the user click a sub preference screen, only the fragment is changed (see <FrameLayout android:id="@+id/content_frame" .../>) not the Toolbar that remains always active and visible, but a custom behavior should be implemented to close each fragment accordingly.

In the main class SettingsActivity that extends ActionBarActivity the following methods should be implemented. Note that private setupActionBar() is called from onCreate()

private void setupActionBar() {
    Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
    //Toolbar will now take on default Action Bar characteristics
    setSupportActionBar(toolbar);
    getSupportActionBar().setHomeButtonEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case android.R.id.home:
        onBackPressed();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0) {
        getFragmentManager().popBackStackImmediate();
        //If the last fragment was removed then reset the title of main
        // fragment (if so the previous popBackStack made entries = 0).
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            getSupportActionBar()
                .setTitle(R.string.action_settings_title);
        }
    } else {
        super.onBackPressed();
    }
}

For the title of the chosen nested screen you should get the reference of your Toolbar and set the appropriate title with toolbar.setTitle(R.string.pref_title_general); (for instance).

There's no need to implement the getSupportActionBar() in all PreferenceFragment since only the view of the fragment is changed at every commit, not the Toolbar;

There's no need to create a fake ToolbarPreference class to add in each preference.xml (see Gábor's answer).

Davideas
  • 3,226
  • 2
  • 33
  • 51
1

Here's a library I've made that is based on AOSP code, which adds tinting to both the preferences and the dialogs, adds an action bar, and supports all versions from API 7 :

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • From looking at the code, it doesn't work for nested preferences...? – Tim Rae Jun 03 '15 at 16:14
  • @TimRae I'm not sure I've tested what you are talking about. Please explain what you mean. What's the scenario you are trying to use exactly? – android developer Jun 03 '15 at 19:57
  • When you have a `PreferenceScreen` inside a `PreferenceScreen` like [this](https://github.com/ankidroid/Anki-Android/blob/develop/AnkiDroid/src/main/res/xml/preferences.xml) – Tim Rae Jun 03 '15 at 23:37
  • I never used such a thing. reading the docs : :http://developer.android.com/reference/android/preference/PreferenceScreen.html , I see it can help with going between screens. You say I should add it too? I will check it out . Thank you. Also, please use Github next time for such a thing (requests and issues). – android developer Jun 04 '15 at 05:15
  • Yeah it's useful when you have too many preferences for a single screen... There are several places already in this thread where people mention nested screens, so I think here is an appropriate place to comment – Tim Rae Jun 04 '15 at 05:56
  • What about headers? I've heard they are used too. – android developer Jun 04 '15 at 06:47
  • @TimRae I can't see that it doesn't work. It works fine with PreferenceScreen. However, this class is considered "final" so I can't extend it (to make its layout the same), yet what I can do is to dynamically set the preferenceScreen instances to have the needed layout. I've added a "fix" for this, and you can get it right ow. – android developer Jun 04 '15 at 21:48
  • Thanks for trying, but no it doesn't work. Change you settings file to [this](https://gist.github.com/timrae/77259451ee647a73443c) – Tim Rae Jun 05 '15 at 05:47
  • @TimRae Can't confirm anything's wrong (except for maybe the toolbar that was gone and it leaves this screen when changing orientation, for some reason). Tested on a real device (SGS3) with Lollipop 5.1.1 . What exactly doesn't work? Please write about it on Github, and let me know. Also, I think you can overcome this by replacing the current preferences with new ones when clicking on them, and store&restore the current state. – android developer Jun 05 '15 at 11:49
  • The toolbar disappears when you click on the `General text` nested preference in the gist from my last comment – Tim Rae Jun 05 '15 at 12:34
  • @TimRae I see. So maybe the best thing would be to re-set the preferences on a normal Preference clicking. – android developer Jun 05 '15 at 12:36
1

Well, this is still an issue for me today (18 Nov 2015). I have tried all the solutions from this thread but there were two main things I couldn't solve:

  • Nested preference screens appeared without toolbar
  • Preferences didn't have the Material look on pre-Lollipop devices

So I ended up creating a library with a more complicated solution. Basically, I had to internally apply styles to the preferences if we are using a pre-Lollipop device and I also handled the nested screens using a custom fragment (restoring all the nested hierarchy taking advantage of the PreferenceScreen key).

The library is this one: https://github.com/ferrannp/material-preferences

And if you are interested in the source code (too long to post it here), this is basically the core of it: https://github.com/ferrannp/material-preferences/blob/master/library/src/main/java/com/fnp/materialpreferences/PreferenceFragment.java

Ferran Negre
  • 3,712
  • 3
  • 34
  • 56