Recently I have added a preference screen to one of my Android apps and now is a good time to put all what I have learnt down on paper, more for future reference. I'll present a quick tutorial and then discuss more advanced topics and pitfalls. Note that I will not be discussing the new Preference Fragments API, for now my Nexus 7 is more than happy running the deprecated preference APIs.
[FREE][ANDROID] Crossword Solver
Creating the Preference XML
The preference screen is defined in an XML file, Android can take this XML file and create an activity (user interface) and deal with the data binding and storage. Very nice indeed.
In the res folder create a new folder called xml. The new xml folder is where we will place our preferences.xml file. Eclipse can create the file for us
- Right click on the xml folder
- Click on New on the pop-up menu
- Click on Android XML File
- The New Android XML File dialog will be displayed
- Select Preference in the Resource Type drop down list
- Enter file name, preferences.xml
- Select PreferenceScreen in the root element list
- Press Finish
Creating the Preference User Interface
Below is the code from my preference.xml
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="@string/pref_title"> <PreferenceCategory android:title="@string/pref_search_cat" android:key="search_category"> <CheckBoxPreference android:key="show_subanagrams_pref" android:summary="@string/pref_findsubanag_summary" android:title="@string/pref_findsubanag_title" android:defaultValue="True" /> <ListPreference android:key="result_limit_pref" android:title="@string/pref_resultlimit_title" android:summary="@string/pref_resultlimit_summary" android:defaultValue="200" android:entries="@array/resultLimits" android:entryValues="@array/resultLimitsValues" /> </PreferenceCategory> </PreferenceScreen>
I have a CheckBoxPreference to toggle a boolean value and a ListPreference to select a string value. I've put the display strings in the strings.xml file in my values folder. You'll notice that the list preference references an array resource. You can create the array resources by creating a resource file (another Android XML file) in the values folder and call it arrays.xml. Below is my arrays.xml file:
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="resultLimits" > <item name="50">50 Results</item> <item name="100">100 Results</item> <item name="200">200 Results</item> </string-array> <string-array name="resultLimitsValues"> <item name="50">50</item> <item name="100">100</item> <item name="200">200</item> </string-array> </resources>
Next we need to create an activity class for the preference screen and inflate the XML in preferences.xml. Create a new class under your src folder called SettingsActivity.java:
package com.mpdbailey.cleverdicandroid; import android.os.Bundle; import android.preference.PreferenceActivity; public class SettingsActivity extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { // Note that none of the preferences are actually defined here. // They're all in the XML file res/xml/preferences.xml. super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); } }
This class extends from PreferenceActivity instead of Activity and calls addPreferencesFromResource(R.xml.preferences) to create the user interface from the XML in preferences.xml.
Thats it! There is no need to worry how Android binds the data to the preference UI controls, its all done under the bonnet. Android also takes care of storing the data for you.
Using Preferences From Your App
In my MainActivity class, I have the following code that fires up the Preference Activity:
@Override public boolean onOptionsItemSelected(MenuItem item) { Intent intent; switch (item.getItemId()) { case (R.id.menu_settings): intent = new Intent(this, SettingsActivity.class); startActivity(intent); break; default: return false; } return true; }
I've put a menu item called Settings on my menu and the code above will start the preference activity when the user clicks on the settings menu button.
The code below is also from the MainActivity class and details with the interaction with the preference data.
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs.registerOnSharedPreferenceChangeListener(this); LoadPreferences(prefs); } public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { LoadPreferences(prefs); } private void LoadPreferences(SharedPreferences prefs) { this.presenter.SetFindSubAngrams(prefs.getBoolean("show_subanagrams_pref", true)); int limit = Integer.parseInt(prefs.getString("result_limit_pref","200")); this.presenter.SetResultLimit(limit); } @Override protected void onDestroy() { super.onDestroy(); PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this); }
I get the SharedPreferences object using PreferenceManager.getDefaultSharedPreferences(this), which allows the various activities of my app access to the preference data.
The Preference User Interface will fire SharedPreferenceChanged events which I listen to in my MainActivity class to keep the app up to date with the latest preferences. These events fire as soon as you change a preference, toggle a check box on the preference form and the event will fire.
Note that when the app first starts that I call LoadPreferences() this is important as it sets up my app with the current preference information.
Application Life-Cycle Pitfalls
Be careful if you use anonymous inner classes to implement the OnSharedPreferenceChangeListener. You'll notice that in the code above I register the MainActivity class in OnCreate() and unregister in OnDestroy(). This is fine if the MainActivity object is the listener. If you use anonymous classes then these can be collected by the garbage collector when the app is paused, this means that your app will then stop responding to SharedPreferenceChanged events. For more details see this StackOverflow Question about the issue.
[FREE][ANDROID][DIET] Weight Tracker App,
Wrapping Up
The Android Preference Framework pretty much does all the work for you, its just a matter of hooking your code up to it. For an example project see the SipDemo in the Android SDK samples.
No comments:
Post a Comment