53

Together with the M release, there are new support libraries. One of them that seems to be very useful is the v7 Preference Support library.

It does not seem to have PreferenceActivity or something similar, how do we integrate it to our app?

Randy Sugianto 'Yuku'
  • 71,383
  • 57
  • 178
  • 228

5 Answers5

94

You have to extend AppCompatActivity, which is required for fragment, and include a subclass of PreferenceFragmentCompat. The abstract fragment requires to override one method, in which you should place your preference inflation logic. And last, your activity theme needs to specify a preferenceTheme attribute.

Read the announcement here. With preference-v7 library you can replace PreferenceFragment (API 11+) with PreferenceFragmentCompat subclass, and SwitchPreference (API 14+) with SwitchPreferenceCompat and have your settings screen work from API 7.

Below is how I made it work:

SettingsActivity.java

public class SettingsActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);
    }
}

layout/activity_settings.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent" >
    <fragment
        android:name=".SettingsFragment"
        android:tag=".SettingsFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

SettingsFragment.java

public class SettingsFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle bundle, String s) {
        addPreferencesFromResource(R.xml.preferences);
    }
}

xml/preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <android.support.v7.preference.PreferenceCategory
        ...>

        <android.support.v7.preference.ListPreference
            ... />

        <android.support.v7.preference.SwitchPreferenceCompat
            ... />

        ...

    </android.support.v7.preference.PreferenceCategory>

    ...

</android.support.v7.preference.PreferenceScreen>

values/styles.xml

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
    ...
</style>

preference-v7 default theme

<style name="PreferenceThemeOverlay">
    <item name="preferenceScreenStyle">@style/Preference.PreferenceScreen</item>
    <item name="preferenceFragmentStyle">@style/PreferenceFragment</item>
    <item name="preferenceCategoryStyle">@style/Preference.Category</item>
    <item name="preferenceStyle">@style/Preference</item>
    <item name="preferenceInformationStyle">@style/Preference.Information</item>
    <item name="checkBoxPreferenceStyle">@style/Preference.CheckBoxPreference</item>
    <item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat</item>
    <item name="dialogPreferenceStyle">@style/Preference.DialogPreference</item>
    <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference</item>
    <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList</item>
</style>
hidro
  • 12,333
  • 6
  • 53
  • 53
12

hidro's answer is right but one more thing here to notice:

Just use normal preference xml tags such as PreferenceScreen instead of the full class name. The support library will convert them automatically.

Why: If you use the full class name the code suggestion and layout preview will not work properly.

So you should write xml like this:

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

    <PreferenceCategory
        ...>

        <ListPreference
            ... />

        <SwitchPreferenceCompat
            ... />

        ...

    </PreferenceCategory>

    ...

</PreferenceScreen>
Junyue Cao
  • 421
  • 6
  • 13
  • 1
    How do you know the tags will be replaced with support library counterparts? – Alexey Andronov Jul 03 '17 at 09:09
  • 1
    @sfk92fksdf The source code is here: https://android.googlesource.com/platform/frameworks/support/+/84765ea/v7/preference/src/android/support/v7/preference/PreferenceInflater.java#216 The second parameter prefixes is in line 64 `init(PreferenceManager preferenceManager)` just like `setDefaultPackages(new String[] {"android.support.v7.preference."});` – Junyue Cao Jul 03 '17 at 14:54
  • interesting, thanks. Although I wouldn't rely on implementation details :-) – Alexey Andronov Jul 04 '17 at 01:42
9

With the new preference support library v7 you can use the PreferenceFragmentCompat with any Activity or AppCompatActivity

public static class PrefsFragment extends PreferenceFragmentCompat {

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

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
}

You have to set preferenceTheme in your theme:

<style name="AppTheme" parent="@style/Theme.AppCompat.Light">
  ...
  <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

In this way you can customize the preferenceTheme to style the layouts used for each preference type without affecting other parts of your Activity.

CodeZero
  • 179
  • 2
  • 15
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • 15
    This is incorrect and caused me 30 minutes of headache. `PreferenceFragmentCompat` should not override `onCreate(Bundle b)`, it should override `onCreatePreferences(Bundle bundle, String s)` – Cord Rehn Jul 25 '16 at 18:04
  • @Cord Rehn: What was a reason of your headache? I have no problems overriding `onCreate` as for non-Preference fragment. `onCreatePreferences` is implemented too but has empty body. I am using `com.android.support:preference-v14:25.3.1`. – isabsent Mar 23 '18 at 03:23
0

You are right it doesn't exist on the appcompat v7, but Google actually added AppCompatDelegate abstract class as a delegate you can use to inject the AppCompat's support to any activity. You can find more from this answer.

This is an example of how to inject the AppCompatDelegate into your activity from the AppCompat's samples from Google, You can find it here.

Community
  • 1
  • 1
Ahmed Hegazy
  • 12,395
  • 5
  • 41
  • 64
  • 4
    This is unrelated to the preference support library as native `PreferenceActivity` on which this `AppCompatPreferenceActivity` is based does not work in any way with classes introduced in `preference-v7` library. This method will only provide you with material themed widgets on all platforms but not consistent preference item looks - margins, font sizes, item layout in general. – Eugen Pechanec Aug 30 '15 at 14:43
0

I tried to implement Hidro's answer above with an activity which also contained a toolbar and it gave me the following a layout inflation exception because of the following error:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.support.v4.app.FragmentHostCallback.getContext()' on a null object reference

I haven't been able to solve these, so have resorted to the following:

public class SettingsActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  if(savedInstanceState == null)
    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new SettingsFragment()).commit();
  }
}

With the following layout for the SettingsActivity:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
  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:fitsSystemWindows="true">
  <include layout="@layout/toolbar"/>
  <FrameLayout android:id="@+id/fragment_container"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>

Posted here as it may help if other people encounter the same exception

QuantumTiger
  • 970
  • 1
  • 10
  • 22