What to use instead of "addPreferencesFromResource" in a PreferenceActivity?
Asked Answered
E

6

365

I just noticed the fact that the method addPreferencesFromResource(int preferencesResId) is marked deprecated in Android's documentation (Reference Entry).

Unfortunately, no alternative method is provided in the method's description.

Which method should be used instead in order to connect a preferenceScreen.xml to the matching PreferenceActivity?

Ensure answered 25/7, 2011 at 21:3 Comment(3)
A very straightforward approach is provided by WannaGetHigh at #23524306Placeman
The solution there still uses addPreferencesFromResource(int preferencesResId). Am I missing something ?Mihalco
@Mihalco Yes but it has been moved from the Activity to the Fragment to reflect the new way of doing it -> fragment.Mom
K
334

No alternative method is provided in the method's description because the preferred approach (as of API level 11) is to instantiate PreferenceFragment objects to load your preferences from a resource file. See the sample code here: PreferenceActivity

Knorr answered 25/7, 2011 at 21:15 Comment(6)
Thank you very much for your answer. I was just following deprecated instructions from "Video2Brain-Android Development" which led me to using PreferenceActivity's version of the method. Btw: I would love to rate your answer as useful if I only could.Ensure
Now only if the PreferenceFragment was included in the compatibility package it would make sense to use it #5501931Handkerchief
Since I use Action Bar Sherlock, I followed the following blog to solve this problem, see... commonsware.com/blog/2012/10/16/…Alluring
Still you need to call addPreferencesFromResource(int PreferencesID) if you want the app to be backwards compatible with api level erlier than 11 (Android 3.0). But I guess you could consider those old devices derecated as well.Rudich
@EinarSundgren I'm Nexus One owner and my max available version is 2.2: you won't stop me! I'll NEVER be deprecated! By the Power of Grayskull...I Have the Power!Maniemanifest
@above use AppCompatPreferenceActivityPudding
T
189

To add more information to the correct answer above, after reading an example from Android-er I found you can easily convert your preference activity into a preference fragment. If you have the following activity:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.my_preference_screen);
    }
}

The only changes you have to make is to create an internal fragment class, move the addPreferencesFromResources() into the fragment, and invoke the fragment from the activity, like this:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
    }

    public static class MyPreferenceFragment extends PreferenceFragment
    {
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.my_preference_screen);
        }
    }
}

There may be other subtleties to making more complex preferences from fragments; if so, I hope someone notes them here.

Tenia answered 18/11, 2012 at 15:50 Comment(7)
Nice, thanks for this. Java and this type of thinking is so far removed from my nice little .net world :)Communalism
Why would they depreciate addPreferencesFromResources() in trade for PreferenceFragment? It looks unnecessary from a beginner point of view.Dermoid
@Dermoid One reason might be that PreferencesFragment supports preference headers out of the box, whereas you have to use a workaround to use those in PreferenceActivity. If you want to have backward compatibility, also have a look at this post.Eadwina
Call requires API level 11Intermeddle
so what about if the API is on Level 9? @IntermeddlePascale
Awesome answer! is there a way to achieve this without changing android.R.id.content? seem inelegant to me for some reason... (correct me if im wrong)Droughty
I wonder why ListPreference radio buttons stop to work with this approach? (android 4.0.3)Incoming
C
38

@Garret Wilson Thank you so much! As a noob to android coding, I've been stuck with the preferences incompatibility issue for so many hours, and I find it so disappointing they deprecated the use of some methods/approaches for new ones that aren't supported by the older APIs thus having to resort to all sorts of workarounds to make your app work in a wide range of devices. It's really frustrating!

Your class is great, for it allows you to keep working in new APIs wih preferences the way it used to be, but it's not backward compatible. Since I'm trying to reach a wide range of devices I tinkered with it a bit to make it work in pre API 11 devices as well as in newer APIs:

import android.annotation.TargetApi;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;

public class MyPrefsActivity extends PreferenceActivity
{
    private static int prefs=R.xml.myprefs;

    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        try {
            getClass().getMethod("getFragmentManager");
            AddResourceApi11AndGreater();
        } catch (NoSuchMethodException e) { //Api < 11
            AddResourceApiLessThan11();
        }
    }

    @SuppressWarnings("deprecation")
    protected void AddResourceApiLessThan11()
    {
        addPreferencesFromResource(prefs);
    }

    @TargetApi(11)
    protected void AddResourceApi11AndGreater()
    {
        getFragmentManager().beginTransaction().replace(android.R.id.content,
                new PF()).commit();
    }

    @TargetApi(11)
    public static class PF extends PreferenceFragment
    {       
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(MyPrefsActivity.prefs); //outer class
            // private members seem to be visible for inner class, and
            // making it static made things so much easier
        }
    }
}

Tested in two emulators (2.2 and 4.2) with success.

Why my code looks so crappy:

I'm a noob to android coding, and I'm not the greatest java fan.

In order to avoid the deprecated warning and to force Eclipse to allow me to compile I had to resort to annotations, but these seem to affect only classes or methods, so I had to move the code onto two new methods to take advantage of this.

I wouldn't like having to write my xml resource id twice anytime I copy&paste the class for a new PreferenceActivity, so I created a new variable to store this value.

I hope this will be useful to somebody else.

P.S.: Sorry for my opinionated views, but when you come new and find such handicaps, you can't help it but to get frustrated!

Chiffchaff answered 12/12, 2012 at 5:23 Comment(3)
Oh well... I just noticed I'd have to change outer class name twice anytime I copy&paste it. There's actually a way to avoid this, by passing prefs to innerclass. You may not create an inner class constructor accepting prefs as a parameter as it is apparently not recommended for PreferenceFragment derived classes. Also, you may not create a method in inner class to get prefs and call addPreferencesFromResource right away, since addPreferencesFromResource needs to be called after super.onCreate has been called, and onCreate doesn't get called right after PreferenceFragment derived class has...Chiffchaff
...been instantiated. So you need to create a new variable in the inner class, create a public set method for it in the inner class, leave the addPreferencesFromResource where it is after the call to super.onCreate, inside onCreate, instantiate the inner class, set the prefs, and use it in the call to getFragmentManager()... as before.Chiffchaff
Your code has been a lifesaver!! I was trying to target both old and new phone, and wasted 3 days..And the Answer is so simple. ThanksStereoscopy
E
22

My approach is very close to Garret Wilson's (thanks, I voted you up ;)

In addition it provides downward compatibility with Android < 3.

I just recognized that my solution is even closer to the one by Kevin Remo. It's just a wee bit cleaner (as it does not rely on the "expection" antipattern).

public class MyPreferenceActivity extends PreferenceActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            onCreatePreferenceActivity();
        } else {
            onCreatePreferenceFragment();
        }
    }

    /**
     * Wraps legacy {@link #onCreate(Bundle)} code for Android < 3 (i.e. API lvl
     * < 11).
     */
    @SuppressWarnings("deprecation")
    private void onCreatePreferenceActivity() {
        addPreferencesFromResource(R.xml.preferences);
    }

    /**
     * Wraps {@link #onCreate(Bundle)} code for Android >= 3 (i.e. API lvl >=
     * 11).
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void onCreatePreferenceFragment() {
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new MyPreferenceFragment ())
                .commit();
    }
}

For a "real" (but more complex) example see NusicPreferencesActivity and NusicPreferencesFragment.

Eadwina answered 31/7, 2013 at 19:18 Comment(6)
@SuppressLint("NewApi") - avoid - be more precise. Have you run it for low apis? It will throw VerifyErrorRemediable
I testes the above on the emulator running API level 10, the APK was built using SDK Version 19. What SDK version did you use for building? What device API level did you run it on? Did you run it on the emulator or on a physical device? When exactly did the error occur? If Ur building with SDK <= 16, see this response.Eadwina
You are missing the point - and add @Remediable if you want me to be notified. For the VE read here: #20272093. The @SuppressLint("NewApi") is just bad styleRemediable
@M Well then, how about a proposal on how to avoid @SuppressLint("NewApi") in this particular situation?Eadwina
@TargetApi(Build.VERSION_CODES.HONEYCOMB) - not all warnings for any api :)Remediable
@M Now that's a concrete proposal for improvement! Thanks, I updated the post.Eadwina
D
6

Instead of exceptions, just use:

if (Build.VERSION.SDK_INT >= 11)

and use

@SuppressLint("NewApi")

to suppress the warnings.

Deshawndesi answered 22/12, 2012 at 11:29 Comment(2)
because it does nothing else than crash :DCyathus
if it's crash ... then try and catch it :DPascale
C
0

Instead of using a PreferenceActivity to directly load preferences, use an AppCompatActivity or equivalent that loads a PreferenceFragmentCompat that loads your preferences. It's part of the support library (now Android Jetpack) and provides compatibility back to API 14.

In your build.gradle, add a dependency for the preference support library:

dependencies {
    // ...
    implementation "androidx.preference:preference:1.0.0-alpha1"
}

Note: We're going to assume you have your preferences XML already created.

For your activity, create a new activity class. If you're using material themes, you should extend an AppCompatActivity, but you can be flexible with this:

public class MyPreferencesActivity extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_preferences_activity)
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, MyPreferencesFragment())
                    .commitNow()
        }
    }
}

Now for the important part: create a fragment that loads your preferences from XML:

public class MyPreferencesFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.my_preferences_fragment); // Your preferences fragment
    }
}

For more information, read the Android Developers docs for PreferenceFragmentCompat.

Cp answered 6/8, 2018 at 16:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.