44

As I was following an old tutorial (Créez des applications pour Android -> openclassroom) I got stuck on this deprecated method addPreferencesFromResource(int id) from the PreferenceActivity class.

So my question is :

What is the new way of creating Preferences in Android ?

WannaGetHigh
  • 3,826
  • 4
  • 23
  • 31
  • 1
    It would be probably better to have this post in Q&A-style. This means put the question as the question, and put the answer to it as the answer instead of putting everything in the question. See also [self-answer](http://stackoverflow.com/help/self-answer) – donfuxx May 07 '14 at 16:59
  • Well i would love to do so but i don't have the reputation for it ;( i'll do it whenever i can :) – WannaGetHigh May 07 '14 at 17:06
  • This might be a helpful resource, but it is not a question that can be answered. You might want to take it down until you can self-answer. Once you can self-answer, you could put it back up. – ugo May 07 '14 at 17:20
  • OK but how do you get reputation then? Answering questions? Commenting? Sorry for all the questions but that's my first post as you may have noticed :) – WannaGetHigh May 07 '14 at 17:44
  • @WannaGetHigh ask questions and give helpful answers. See: http://stackoverflow.com/help/whats-reputation. I think once you have a reputation of 15 points, you can self-answer. – ugo May 07 '14 at 17:54
  • @Ugo thanks for the answer ill do this then ! – WannaGetHigh May 07 '14 at 18:09
  • Dude, it would be way cooler if you made a real question, followed by an answer. This current "question" doesn't say much, and won't be very good for googling. Pls try make it look more like other questions on this site. – Richard Le Mesurier May 08 '14 at 15:12

2 Answers2

103

I found this post (What to use instead of “addPreferencesFromResource” in a PreferenceActivity?) that help me understand that you have to go through a PreferenceFragment in order to do it.

In the following explanation I use your.package. just to show that you have to put the package name. Everybody has its own package so please replace it with your package.

lets begin :


1. Preference Fragment

  • Create your PreferenceFragment class

    MyPreferenceFragment

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


  • Then the associated xml resource

    fragment_preference.xml (in the folder res/xml of your project)

    <?xml version="1.0" encoding="utf-8"?>
    
    <PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <PreferenceCategory 
            android:title="FOO">
    
            <CheckBoxPreference
                android:key="checkBoxPref"
                android:title="check it out"
                android:summary="click this little box"/>
    
        </PreferenceCategory>
    
    </PreferenceScreen>
    

    That's all for the Fragment part.



2. Preference Activity

  • Create the PreferenceActivity class

    MyPreferenceActivity

    public class MyPreferenceActivity extends PreferenceActivity
    {
        @Override
        public void onBuildHeaders(List<Header> target)
        {
            loadHeadersFromResource(R.xml.headers_preference, target);
        }
    
        @Override
        protected boolean isValidFragment(String fragmentName)
        {
            return MyPreferenceFragment.class.getName().equals(fragmentName);
        }
    }
    

    Do not forget to override isValidFragment(String fragmentName) method as you will get punched in the face by your application ! ;) More seriously I have no idea why you need to do this but it is needed. If someone has an explanation about this I'd gladly read it :)

    EDIT :


    Thanks to kirtan403 I now know why it is needed : it has to be set because of an (android framework fragment injection).


    As you can see in the onBuildHeaders(List<Header> target) we load another xml file that contain the headers of the preference. In short, headers are the left part of the preference and the fragment are the right part (for tablet). For a phone you will first have the headers and when you click on an item the corresponding fragment will be put on top of the headers list.

    Read this article (Multi-pane development in Android with Fragments - Tutorial) the images explain themselves.


  • Then the associated xml resource

    headers_preference.xml (in the folder res/xml of your project)

    <?xml version="1.0" encoding="utf-8"?>
    
    <preference-headers
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <header 
            android:fragment="your.package.MyPreferenceFragment"
            android:title="Goto: Preference fragment"
            android:summary="An example of some preferences." />
    
    </preference-headers>
    

    As you may have noticed in the header section you have :

    android:fragment="your.package.MyPreferenceFragment"

    This will act as a Link to the fragment you want to show. On Tablet it will load on the right part and on the phone it will load on top of the current view.



3. Android Manifest

Now what you should do is to add your Activity to the AndroidManifest.xml file.

Inside the application section add these lines :

<activity
    android:name="your.package.MyPreferenceActivity"
    android:label="Preferences">
</activity>

You will probably tell me :

"Oh darling you forgot to put android:launchMode="singleTask" in your actvity"

But DO NOT PUT THIS as you will never load your fragment on phone. This error was solved by a great man ! This is the link to his blog (Android header preferences on small screen/phone).



4. Start the Preferences from Menu

Finally you need to add the ability to show this Preference !! To do so you will need 3 things :

  • The Menu

    menu.xml (in the folder res/menu of your project)

    <?xml version="1.0" encoding="utf-8"?>
    
    <menu 
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item 
            android:id="@+id/preferences"
            android:title="Preferences" />
    
    </menu>
    


  • Loading this Menu in your Main activity (not the PreferenceActivity) under the method onCreateOptionsMenu(Menu menu)

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }
    


  • Starting the MyPreferenceActivity Activity when you click on that button.

    For that you will need to override the onOptionsItemSelected(MenuItem item) method in your Main activity.

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch(item.getItemId())
        {
            case R.id.preferences:
            {
                Intent intent = new Intent();
                intent.setClassName(this, "your.package.MyPreferenceActivity");
                startActivity(intent);
                return true;
            }
        }
    
        return super.onOptionsItemSelected(item);
    }
    



Et voila les amis !

I haven't tested this code. I took it and modified it from my own code so I may have not well copy pasted things. If you encounter errors tell me, I'll try to figure out the problem and fix this.

I hope this post will help some people out there :D

Cheers !

Community
  • 1
  • 1
WannaGetHigh
  • 3,826
  • 4
  • 23
  • 31
  • 1
    fwiw I have added your links in (pls include a link name, in case the link goes dead in future it makes it possible to google for the page) & added some formatting to make it easier to read. Also made some minor changes based on best practices on this site. Feel free to change any of my edits - it is YOUR question after all. Oh, and you may as well go ahead and delete your above comments, cos they are now out of date. G'luck – Richard Le Mesurier May 08 '14 at 17:51
  • 1
    @RichardLeMesurier Thanks a lot for your help ! It is indeed easier to read it and also thank you for the advices I'll remember it for my next answers/questions on this site ! Keep riding your wave :) – WannaGetHigh May 09 '14 at 07:36
  • 3
    @WannaGetHigh You mentioned about the isValidFragment() usage, why it is used. you can read more about it [here](https://securityintelligence.com/new-vulnerability-android-framework-fragment-injection/) . Because of the Vulnerability, this method was introduced in Android 4.4 Kitkat. – kirtan403 Nov 28 '15 at 19:18
  • @kirtan403 Thanks a lot for the link now I understand why they did this :) – WannaGetHigh Nov 30 '15 at 16:23
  • 1
    Hi, this was immensely helpful! any chance for a quick tip on how to display just the fragment, without any of the additional screen in the middle? – DorD Feb 11 '16 at 22:52
  • You need to override isValidFragment to prevent the Fragment Injection Vulnerability. https://securityintelligence.com/new-vulnerability-android-framework-fragment-injection/ – PrashanD May 18 '16 at 09:05
  • @Prashan ty for the link i already saw the exploit in kirtan403 comment :) – WannaGetHigh May 18 '16 at 15:10
19

I liked the solution from this post: http://alvinalexander.com/android/android-tutorial-preferencescreen-preferenceactivity-preferencefragment

.. because it seems the most compact for someone that just needs something very basic up and running quickly. It has only one .java file and two small xml files.

Activity Config REMINDERS

After adding the 3 files to your project, Don't forget to

A) Add the Prefs Activity to Manifest file
B) Add some way to launch the Prefs Activity .. e.g., a Button or Menu item

Add the following files to your project. Use the order they are listed in to avoid compile errors.

  1. Add /res/values/array.xml

    <resources>
        <string-array name="listArray">
            <item>Ace</item>
            <item>Club</item>
        </string-array>
    
        <string-array name="listValues">
            <item>Ace</item>
            <item>Club</item>
        </string-array>
    </resources>
    
  2. Add /res/xml/preferences.xml

    <?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
        <EditTextPreference android:title="Your Name"
                            android:key="username"
                            android:summary="Please provide your username"></EditTextPreference>
        <CheckBoxPreference android:title="Application Updates"
                            android:defaultValue="false"
                            android:summary="This option if selected will allow the application to check for latest versions."
                            android:key="applicationUpdates" />
        <ListPreference     android:title="Download Details"
                            android:summary="Select the kind of data that you would like to download"
                            android:key="downloadType"
                            android:defaultValue="Ace"
                            android:entries="@array/listArray"
                            android:entryValues="@array/listValues" />
    </PreferenceScreen>
    
  3. Add the Activity code

    public class AppPreferenceActivity extends PreferenceActivity
    {
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
    
            checkValues();
        }
    
        public static class MyPreferenceFragment extends PreferenceFragment
        {
            @Override
            public void onCreate(final Bundle savedInstanceState)
            {
                super.onCreate(savedInstanceState);
                addPreferencesFromResource(R.xml.preferences);
            }
        }
    
        private void checkValues()
        {
            SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
            String strUserName = sharedPrefs.getString("username", "NA");
            boolean bAppUpdates = sharedPrefs.getBoolean("applicationUpdates",false);
            String downloadType = sharedPrefs.getString("downloadType","1");
    
            String msg = "Cur Values: ";
            msg += "\n userName = " + strUserName;
            msg += "\n bAppUpdates = " + bAppUpdates;
            msg += "\n downloadType = " + downloadType;
    
            Toaster.shortDebug(msg);
        }
    }
    
Gene Bo
  • 11,284
  • 8
  • 90
  • 137