Creating a Preference Screen with support (v21) Toolbar

71,470

Solution 1

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);
    }
}

I hope this helps someone.

Solution 2

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.

Solution 3

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.

Solution 4

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

Solution 5

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>
Share:
71,470

Related videos on Youtube

James Cross
Author by

James Cross

Updated on July 08, 2022

Comments

  • James Cross
    James Cross almost 2 years

    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.

  • Madhur Ahuja
    Madhur Ahuja over 9 years
    I have tried this approach. The problem is it doesn't displays the toolbar in child preference screens.
  • James Cross
    James Cross over 9 years
    Humm, I didn't try using child ones. You mean the PreferenceHeader approach?
  • Lucas S.
    Lucas S. over 9 years
    I think he's talking about PreferenceScreen embeded in the root preference XML.
  • Crossle Song
    Crossle Song over 9 years
    root.addView(toolbar); why? root.addView(toolbarContainer);
  • Sven Dubbeld
    Sven Dubbeld over 9 years
    Whoops, missed a variable while renaming, fixed it.
  • midhunhk
    midhunhk over 9 years
    I liked the approach, but sadly won't work if target api is less than API 11
  • davidcesarino
    davidcesarino over 9 years
    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.
  • Gábor
    Gábor over 9 years
    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.
  • android developer
    android developer over 9 years
    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" .
  • andQlimax
    andQlimax over 9 years
    it give nullpointer exception in gingebread, root is null..any solution?
  • vbence
    vbence over 9 years
    No support for loadHeadersFromResource - that's what makes a PreferenceActivity.
  • andQlimax
    andQlimax over 9 years
    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 :(
  • David Passmore
    David Passmore over 9 years
    @andQlimax I am using this on all my devices. From api 10 to api 21 and all work. Can you clarify?
  • andQlimax
    andQlimax over 9 years
    See here for example: 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
  • David Passmore
    David Passmore over 9 years
    @andQlimax Ok I will have to look into this and I will update when i have any information.
  • Sterling
    Sterling over 9 years
    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.
  • anthonylawson
    anthonylawson about 9 years
    I implemented this and it works fine, but I noticed that a simple CheckBoxPreference does not animate anymore...
  • David Passmore
    David Passmore about 9 years
    @andQlimax I have updated my answer with a solution for the tinting issue
  • Ferran Negre
    Ferran Negre about 9 years
    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().ge‌​tParent().getParent(‌​); throws an error: java.lang.ClassCastException: android.support.v7.internal.widget.FitWindowsFrameLayout cannot be cast to android.widget.LinearLayout
  • David Passmore
    David Passmore about 9 years
    @FerranNegre I don't see why there is any issues here as there has been no changes to the layouts as per here android.googlesource.com/platform/frameworks/base/+/refs/hea‌​ds/… . I will investigate further and come back with any updates
  • Eugene Wechsler
    Eugene Wechsler about 9 years
    This is the best answer in this thread. Author of this article 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
    Eugene Wechsler about 9 years
    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
    Eugene Wechsler about 9 years
    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.
  • MrBrightside
    MrBrightside about 9 years
    @EugeneWechsler Yes, indeed, ActionBarActivity has been deprecated now.
  • Joaquin Iurchuk
    Joaquin Iurchuk about 9 years
    Thanks! I was using fragment tag in the XML instead of a simple FrameLayout.
  • Tomas
    Tomas about 9 years
    Work this solution also on nested screens? Is there a better example?
  • MrBrightside
    MrBrightside about 9 years
    @Tomas I haven't tried yet, but it should work on nested screens too. If works for you, tell us please.
  • Tim Autin
    Tim Autin almost 9 years
    Thanks a lot ! Working for me on a Galaxy Nexus (4.3) and on the emulator with nested screens (lollipop).
  • Ferran Negre
    Ferran Negre almost 9 years
    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?
  • MrBrightside
    MrBrightside almost 9 years
    Sorry, but I'm not using nested screens. Maybe @Tomas can help you posting a sample code.
  • Tomas
    Tomas almost 9 years
    @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 and maybe some useful information from this page, but I am not sure, I don't keep the test code. Sorry for that :(
  • Tim Rae
    Tim Rae almost 9 years
    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
  • Tim Rae
    Tim Rae almost 9 years
    From looking at the code, it doesn't work for nested preferences...?
  • android developer
    android developer almost 9 years
    @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?
  • Tim Rae
    Tim Rae almost 9 years
    When you have a PreferenceScreen inside a PreferenceScreen like this
  • android developer
    android developer almost 9 years
    I never used such a thing. reading the docs : :developer.android.com/reference/android/preference/… , 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).
  • Tim Rae
    Tim Rae almost 9 years
    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
  • android developer
    android developer almost 9 years
    What about headers? I've heard they are used too.
  • android developer
    android developer almost 9 years
    @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.
  • Tim Rae
    Tim Rae almost 9 years
    Thanks for trying, but no it doesn't work. Change you settings file to this
  • android developer
    android developer almost 9 years
    @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.
  • Tim Rae
    Tim Rae almost 9 years
    The toolbar disappears when you click on the General text nested preference in the gist from my last comment
  • android developer
    android developer almost 9 years
    @TimRae I see. So maybe the best thing would be to re-set the preferences on a normal Preference clicking.
  • Zach Sperske
    Zach Sperske almost 9 years
    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?
  • David Passmore
    David Passmore almost 9 years
    @ZachSperske You could try wrapping the Toolbar in a AppBarLayout similar to this answer on SO - stackoverflow.com/a/31026359/566127 See my update for an example.
  • Zach Sperske
    Zach Sperske almost 9 years
    Sadly, doesn't seem to work on pre L devices. Thanks for the reply though!
  • Ridcully
    Ridcully almost 9 years
    I use the resource from the appcompat library for the up-icon: R.drawable.abc_ic_ab_back_mtrl_am_alpha
  • tasomaniac
    tasomaniac almost 9 years
    With this solution, I think the Toolbar will scroll with the content right? Because it is just an item in the internal ListView.
  • Srujan Barai
    Srujan Barai over 8 years
    @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.
  • Gábor
    Gábor over 8 years
    Not with this updated, new solution. That works just as expected.
  • fattire
    fattire over 8 years
    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?
  • Gábor
    Gábor over 8 years
    Just a shot in the dark but maybe if you specify both?
  • f470071
    f470071 over 8 years
    Which PreferenceActivity are you using? Default old android or something from support libraray. Could ypu please be exact with class.
  • f470071
    f470071 over 8 years
    This look like an old 2.x preference activity. So what is the point of using it?
  • David Passmore
    David Passmore over 8 years
    @f470071 I am using the base PreferenceActivity class
  • Shashank Srivastava
    Shashank Srivastava over 8 years
    @DavidPassmore for me the preference list is overlapping on the toolbar
  • David Passmore
    David Passmore over 8 years
    @ShashankSrivastava This is reflected if you are using Android 6, I am working on a solution for this. Thanks for the update.
  • Arkadiusz 'flies' Rzadkowolski
    Arkadiusz 'flies' Rzadkowolski over 8 years
    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.
  • The 29th Saltshaker
    The 29th Saltshaker about 8 years
    Can confirm that this solution does not work (compiling for API 23)
  • TeeTracker
    TeeTracker about 8 years
    However PreferenceFragment doesn't support header-group which can only be used in PrefeenceActivity.
  • khusrav
    khusrav almost 8 years
    on N Preview it gives 'ClassCastException' can't cast FrameLayout to LinearLayout on setting up nested screen.
  • khusrav
    khusrav almost 8 years
    you have to getParent() again to access the root LinearLayout.
  • ShadowGod
    ShadowGod over 7 years
    Like @khusrav says, on API 24 you have to add another getParent()
  • Tyler Pfaff
    Tyler Pfaff over 7 years
    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.
  • Simon
    Simon over 7 years
    @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
  • Thomas Vos
    Thomas Vos over 7 years
    Looks promising but doesn't work for me. imgur.com/lSSVCIo (Pixel C emulator).
  • Martin Sing
    Martin Sing over 7 years
    Github Link for the lazy
  • dsharew
    dsharew about 7 years
    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?
  • Pratik Butani
    Pratik Butani about 7 years
    You are awesome :)
  • Eido95
    Eido95 over 6 years
    In Support Library Revision Archive, Revision 22.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
    Eido95 over 6 years
    Didn't work for me, root.getChildAt(0); returns null.
  • mbac32768
    mbac32768 almost 5 years
    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