Preference Summary or Secondary Text
In android settings guidelines we can read :
Secondary text below is for status, not description…
Before Ice Cream Sandwich, we often displayed secondary text below a label to further describe it or provide instructions. Starting in Ice Cream Sandwich, we're using secondary text for status,unless it's a checkbox setting.
Unfortunately, at time of writitng, there doesn't seem to be a simple, automated way of doing this.
In this post we are going to look at how to achieve it. We can get an example from AdvancedPreferences.java in the Android code samples (API Demos).
The core of example is
Here we set summary for each type of Preference.
In RingtonePreference, I have choosen to display title of ringtone, and in MultiSelectListPreference I have used a token like ";". It is just an example.
In your custom preference you can do everything, but you have to pay attention to setSummary(int value) method.
In my case I have an integer value, so I had to override the method.
If the summary has a String formatting marker in it (i.e. “%s” or “%1$s”), then the current entry value will be substituted in its place when it’s retrieved.
As a result, if you set “summary” that contains “%” char (like "5%"), you can have java.util.UnknownFormatConversionException: Conversion: because it may be an unknown format.
The reason is here (standard method in ListPreference)
I suggest you create a subclass of ListPreference, override getSummary() method and return whatever you need, skipping the call to String.format().
Alternatively you can obtain percent character in String.format() by specifying "%%".
A note about use of passwords with Shared Preferences. Passwords are always a tricky thing to store, and I'd be particularly wary of storing them as clear text.
If possible I'd consider modifying the server to use a negotiated token for providing access, something like OAuth.
However if you use Shared Preferences with passwords, you have to change the above code if you don't want to see password in summary as clear text.
A final note about RingtonePreference.
The initial value is loaded by
However it does not fire onSharedPreferenceChanged when a ringtone is selected. If you see SharedPreferences.java source file, you can note this:
Wrapping up, in this example we have seen how set summary in common android preferences using OnSharedPreferenceChangeListener.
You can get full code from GitHub:
Secondary text below is for status, not description…
Before Ice Cream Sandwich, we often displayed secondary text below a label to further describe it or provide instructions. Starting in Ice Cream Sandwich, we're using secondary text for status,unless it's a checkbox setting.
Unfortunately, at time of writitng, there doesn't seem to be a simple, automated way of doing this.
In this post we are going to look at how to achieve it. We can get an example from AdvancedPreferences.java in the Android code samples (API Demos).
- Step 1: Create a class called MyPreferenceFragment
This class extends PreferenceFragment and implements onSharedPreferenceChangeListener as shown below.
public class MyPreferenceFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preference_summary); } - Step 2: Listen for changes in preferences
We can use registerOnSharedPreferenceChangeListener method like this:
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
You need to register it in onResume and similarly you also need to unregister the listener which is done in onPause method.
@Override public void onResume() { super.onResume(); // Set up a listener whenever a key changes getPreferenceScreen().getSharedPreferences() .registerOnSharedPreferenceChangeListener(this); initSummary(); } @Override protected void onPause() { super.onPause(); // Unregister the listener whenever a key changes getPreferenceScreen().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); } - Step 3: Implement onSharedPreferenceChanged method
Implementing OnSharedPreferenceChangeListener we must use the onSharedPreferenceChanged method. Here you can listen for specific preference keys and use setSummary on the related preference.
Method onSharedPreferenceChanged has two parameters:- Instance of SharedPreferences having the key and value of the Preferences defined in the preferences screen via xml
- Key of the preferences whose value has changed.
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { //update summary updatePrefsSummary(sharedPreferences, findPreference(key)); } - Step 4: Set summary for each preference
We can use a method like this:
/** * Update summary * * @param sharedPreferences * @param pref */ protected void updatePrefsSummary(SharedPreferences sharedPreferences, Preference pref) { if (pref == null) return; if (pref instanceof ListPreference) { // List Preference ListPreference listPref = (ListPreference) pref; listPref.setSummary(listPref.getEntry()); } else if (pref instanceof EditTextPreference) { // EditPreference EditTextPreference editTextPref = (EditTextPreference) pref; editTextPref.setSummary(editTextPref.getText()); } else if (pref instanceof MultiSelectListPreference) { // MultiSelectList Preference MultiSelectListPreference mlistPref = (MultiSelectListPreference) pref; String summaryMListPref = ""; String and = ""; // Retrieve values Set values = mlistPref.getValues(); for (String value : values) { // For each value retrieve index int index = mlistPref.findIndexOfValue(value); // Retrieve entry from index CharSequence mEntry = index >= 0 && mlistPref.getEntries() != null ? mlistPref .getEntries()[index] : null; if (mEntry != null) { // add summary summaryMListPref = summaryMListPref + and + mEntry; and = ";"; } } // set summary mlistPref.setSummary(summaryMListPref); } else if (pref instanceof RingtonePreference) { // RingtonePreference RingtonePreference rtPref = (RingtonePreference) pref; String uri; if (rtPref != null) { uri = sharedPreferences.getString(rtPref.getKey(), null); if (uri != null) { Ringtone ringtone = RingtoneManager.getRingtone( getActivity(), Uri.parse(uri)); pref.setSummary(ringtone.getTitle(getActivity())); } } } else if (pref instanceof NumberPickerPreference) { // My NumberPicker Preference NumberPickerPreference nPickerPref = (NumberPickerPreference) pref; nPickerPref.setSummary(nPickerPref.getValue()); } }
- Step 5: Init initial values
You'll also need to update the values when you register the listener to ensure you get the right initial values.
In this way you are sure that summary is processed and updated appropriately even when screen comes up for the first time.
We can use a method like this:
/* * Init summary */ protected void initSummary() { for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) { initPrefsSummary(getPreferenceManager().getSharedPreferences(), getPreferenceScreen().getPreference(i)); } } /* * Init single Preference */ protected void initPrefsSummary(SharedPreferences sharedPreferences, Preference p) { if (p instanceof PreferenceCategory) { PreferenceCategory pCat = (PreferenceCategory) p; for (int i = 0; i < pCat.getPreferenceCount(); i++) { initPrefsSummary(sharedPreferences, pCat.getPreference(i)); } } else { updatePrefsSummary(sharedPreferences, p); } }
The core of example is
updatePrefsSummary
method.Here we set summary for each type of Preference.
In RingtonePreference, I have choosen to display title of ringtone, and in MultiSelectListPreference I have used a token like ";". It is just an example.
In your custom preference you can do everything, but you have to pay attention to setSummary(int value) method.
In my case I have an integer value, so I had to override the method.
This is necessary because the standard method does this:@Override public void setSummary(int value) { setSummary(String.valueOf(value)+" " + minutes); }
A note about ListPreference. In android doc we can read:/** * Sets the summary for this Preference with a resource ID. * * @see #setSummary(CharSequence) * @param summaryResId The summary as a resource. */ public void setSummary(int summaryResId) { setSummary(mContext.getString(summaryResId)); }
If the summary has a String formatting marker in it (i.e. “%s” or “%1$s”), then the current entry value will be substituted in its place when it’s retrieved.
As a result, if you set “summary” that contains “%” char (like "5%"), you can have java.util.UnknownFormatConversionException: Conversion: because it may be an unknown format.
The reason is here (standard method in ListPreference)
If you want to use value like "5%" in ListPreference, in API Level 11 or higher, you need to be carefuf.@Override public CharSequence getSummary() { final CharSequence entry = getEntry(); if (mSummary == null || entry == null) { return super.getSummary(); } else { return String.format(mSummary, entry); } }
I suggest you create a subclass of ListPreference, override getSummary() method and return whatever you need, skipping the call to String.format().
Alternatively you can obtain percent character in String.format() by specifying "%%".
A note about use of passwords with Shared Preferences. Passwords are always a tricky thing to store, and I'd be particularly wary of storing them as clear text.
If possible I'd consider modifying the server to use a negotiated token for providing access, something like OAuth.
However if you use Shared Preferences with passwords, you have to change the above code if you don't want to see password in summary as clear text.
A final note about RingtonePreference.
The initial value is loaded by
initPrefsSummary()
and it is correct.However it does not fire onSharedPreferenceChanged when a ringtone is selected. If you see SharedPreferences.java source file, you can note this:
Note: currently this class does not support use across multiple
processes. This will be added later.
For our purposes we can do a workaround.
We can use OnPreferenceChangeListener interface with ringtonePreference.
Implementing OnPreferenceChangeListener and using the onPreferenceChange method you can listen when a RingtonePreference has been changed by the user and you can use setSummary on the related preference.class RingToneOnPreferenceChangeListener implements OnPreferenceChangeListener{ @Override public boolean onPreferenceChange(Preference pref, Object newValue) { if (newValue!=null && newValue instanceof String){ String uri=(String)newValue; Ringtone ringtone = RingtoneManager.getRingtone(getActivity(), Uri.parse(uri)); pref.setSummary(ringtone.getTitle(getActivity())); } return true; } } protected void initPrefsSummary(SharedPreferences sharedPreferences,Preference p){ if (p instanceof PreferenceCategory){ PreferenceCategory pCat = (PreferenceCategory)p; for(int i=0;i
Wrapping up, in this example we have seen how set summary in common android preferences using OnSharedPreferenceChangeListener.
You can get full code from GitHub:
Comments
Post a Comment