I implemented a custom preference by extending RingtonePrefernce. I did override getSummary method which dynamically returns the value of pref when pref is updated.
But the problem is on main pref screen the updated value is not reflected when pref is closed.
The only time the pref summay is updated in main screen if i scroll down the main pref screen down/up and when the pref goes out of screen and comes back on screen. So basically when its redrawn.
How do i solve this?
Code for main perf screen which lists all prefs
public class MainActivity extends PreferenceActivity implements
OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
#SuppressLint("Deprecation")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SoundUtils.stopPlaying();
ringtoneManager = new RingtoneManager(getApplicationContext());
featureManager = FeatureManager.getInstance(this);
beepActionManager= new BeepActionManager();
addPreferencesFromResource(R.xml.preferences_settings);
//other code
}
}
And in preferences_settings my pref is added as
<PreferenceCategory android:title="#string/sound_settings">
<com.mindedges.beephourly.utils.CustomRintonePreference
android:defaultValue="content://settings/system/notification_sound"
android:key="ringtone_pref"
android:ringtoneType="all"
android:title="#string/hr_beep_tone_title"
android:summary="#string/hr_beep_tone_summary"/>
<com.mindedges.beephourly.utils.CustomRintonePreference
</PreferenceCategory>
I checked your code and I created test project, found solution.
I think you should notify your custom preference that you changed it.
'RingtonePreference' has callback method called 'onSaveRingtone' and you need to call 'notifyChnaged' function at overriding of 'onSaveRingtone'.
Here is the code I tested.
public class CustomPreference extends RingtonePreference {
public CustomPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomPreference(Context context) {
super(context);
}
#Override
protected void onSaveRingtone(Uri ringtoneUri) {
super.onSaveRingtone(ringtoneUri);
notifyChanged();
}
#Override
public CharSequence getSummary() {
return "Date " + System.currentTimeMillis();
}
}
As far as I can tell, the ListView in the PreferenceScreen needs to be invalidated using the ListView.invalidate() function in when onResume() of PreferenceScreen is called, after the RingtonePreference has closed.
Related
I am facing a problem while trying to consume click event of a preference. There's a preference, and on clicking it, I open a sub screen with different preference categories. What I want to achieve is that if a certain condition is false, a toast should popup, and the sub screen shouldn't open. In case it's true, it should work normally and open the sub screen. For this, I tried the following piece of code :
mPref = (Preference) findPreference("abc");
mPref.setOnPreferenceClickListener(this);
#Override
public boolean onPreferenceClick(Preference preference) {
// TODO Auto-generated method stub
if (!condition) {
Toast.makeText(getActivity(), "Error", Toast.LENGTH_SHORT).show();
return true; // Consume click event.
}
return false;
}
The relevant part of xml is as follows:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
android:key="abc"
android:summary="#string/string_turn_on"
android:textColor="#android:color/white"
android:title="#string/string_turn_on">
<PreferenceCategory
android:key="asa"
android:title="a" >
<com.example.gaurav.CustomPref
android:defaultValue="0"
android:key="st"
android:dependency="e"
android:showDefault="true"
android:summary="summary"
android:title="t" />
<com.example.gaurav.CustomPref
android:defaultValue="0"
android:key="st"
android:dependency="e"
android:showDefault="true"
android:summary="so"
android:title="so" />
</PreferenceCategory>
<PreferenceCategory
android:title="other" >
<CheckBoxPreference
android:defaultValue="false"
android:key="e"
android:title="eb" />
</PreferenceCategory>
</PreferenceScreen>
I have changed the strings etc, but I don't think they cause this error, so it won't matter. It's the preference with key "abc" which launches a sub screen which I want to prevent from launching. Any help regarding this would be appreciated.
try adding this line in the onCreate method
addPreferencesFromResource(R.xml.preferences);
edit:
#Override
public boolean onPreferenceClick(Preference preference) {
if (condition) {
return true; // Consume click event.
}
Toast.makeText(getActivity(), "Error", Toast.LENGTH_SHORT).show();
return false;
}
I've just had this problem and I solved it creating a custom Preference class which overrides onClick event and setOnPreferenceClickListener
This is the custom class:
public class InterceptableEditTextPreference extends EditTextPreference {
private OnPreferenceClickListener customOnPreferenceClickListener;
public InterceptableEditTextPreference(Context context) {
super(context);
}
public InterceptableEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InterceptableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onClick() {
if (customOnPreferenceClickListener != null) {
boolean handled = customOnPreferenceClickListener.onPreferenceClick(this);
if (handled) {
return;
}
}
super.onClick();
}
#Override
public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
customOnPreferenceClickListener = onPreferenceClickListener;
}
}
This way when you set the listener, the returned value is taken into account to continue with the default click behaviour (show the dialog) or not:
editTextPreference.setOnPreferenceClickListener(preference -> {
if(someCondition) {
return true; // This prevent the PreferenceDialog to appear
// To manually show the dialog you can call:
// getPreferenceManager().showDialog(preference);
}
return false;
});
Explanation
The previous "workaround" should be the default behaviour but for some reason, analyzing the SDK Preference class we find this:
Which means onClick() is always called (if the preference is enabled and selectable) and not depend on the boolean value returned by mOnClickListener.onPreferenceClick
I've been trying to get a switch preference working in Android whereby I can intercept and handle differently, in certain cases, when they switch it on/off vs when they click the whole preference.
This is what I'm trying to accomplish:
User goes into preferences tags are off and no tags are stored (ie: tag preference is empty)
User turns on preference for tags, and since no tags are stored currently it launches a tag search activity for user to find the tag. - works fine.
If tag already exists, and they change the state ONLY then update the value as normal. - works fine
Here's my issue:
If they click the preference though and they already have a tag saved, don't change the state (regardless if it's enabled or disabled), launch the tag search activity. - this DOESN'T work.
What I've found so far is that in the final scenario above, I get a call to onPreferenceChanged, followed by a call to onPreferenceClicked, followed by a subsequent call to onPreferenceChanged. This seems to be my problem. The first call to onPreferenceChanged causes my listener on my SharedPreferences to be called telling it that it's now enabled.
If I didn't receive the first call to onPreferenceChanged then I wouldn't have an issue.
Here is the relevant parts where I'm setting the listeners
SwitchPreference tagPref = (SwitchPreference) findPreference(PreferencesConstants.PREFERENCE_TAG_ENABLED);
tagPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Log.e("BLAH", "onPrefChanged....is it handled by OnClick?" + Boolean.toString(handledByClick));
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
boolean enabled = prefs.getBoolean(PreferencesConstants.PREFERENCE_TAG_ENABLED, false);
Log.e("BLAH", "value stored in prefs? " + Boolean.toString(enabled));
if (newValue instanceof Boolean) {
enabled = (Boolean) newValue;
}
Log.e("BLAH", "New value? " + Boolean.toString(enabled));
if (!handledByClick) {
if (enabled && (currentTag == null || currentTag.isEmpty())) {
Log.e("BLAH", "Enabled and CurrentTag empty!");
Intent intent = new Intent(getActivity(), TagSearchActivity.class);
startActivityForResult(intent, 0);
return false; // always return false, we'll handle
// updating
// this value manually.
} else {
return true;
}
}
Log.e("BLAH", "returning false (AS IN WE HANDLED IT).");
return false;
}
});
tagPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
handledByClick = true;
Log.e("BLAH", "onprefClick");
Intent intent = new Intent(getActivity(), TagSearchActivity.class);
startActivityForResult(intent, 0);
return true;
}
});
Here are the relevant log lines after running it with a saved tag, and clicking the preference.
01-18 15:55:05.593: E/BLAH(13261): onPrefChanged....is it handled by OnClick?false
01-18 15:55:05.593: E/BLAH(13261): value stored in prefs? true
01-18 15:55:05.593: E/BLAH(13261): New value? false
01-18 15:55:05.613: E/DifferentClass(13261): On Shared Preferences Changed - tagEnabled
01-18 15:55:05.652: E/DifferentClass(13261): disabled TAG in cancelAlarmService
01-18 15:55:05.662: E/AnotherClass(13261): Updating Feed List. Old Size: 33, New Size: 14
01-18 15:55:05.682: E/BLAH(13261): onprefClick
01-18 15:55:05.812: E/BLAH(13261): onPrefChanged....is it handled by OnClick?true
01-18 15:55:05.812: E/BLAH(13261): value stored in prefs? false
01-18 15:55:05.822: E/BLAH(13261): New value? false
01-18 15:55:05.822: E/BLAH(13261): returning false (AS IN WE HANDLED IT).
I have been working with the same issue for ages now and you can go about it two ways.
Implementing a switchpreference with custom actions for every event:
forevercrashed made some good points. I tried follow them, but for me they didn't do it. I bet they work, but I needed more functionallity in an easier way. Xgouchet (second Link) uses Headers and custom xml layouts which uses custom placements and measurements (height, witdth, padding etc.). I needed a solution without altering Googles built in auto-generated layout.
The super easy and powerful way: implement your own SwitchPreference!
Just make a class extend SwitchPreference and then implement/override like so:
public class AutoUploadSwitchPreference extends SwitchPreference {
public AutoUploadSwitchPreference(Context context) {
super(context);
}
public AutoUploadSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AutoUploadSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onClick() {
//super.onClick(); THIS IS THE IMPORTANT PART!
}
By overriding onClick() and commenting out / deleting super.onClick() makes the SwitchPreference NOT call callChangeListener(Object newValue). Now you can click the preference and nothing happens, not until you want it to.
(One bug that would occur otherwise was having multiple calls to onPreferenceChange in the fragment)
Now! To make things happen: Here is the structure I have used.
Create a SettingsActivity
In it make sure you fetch preferences, resources etc.
in onCreate() in your Activity - launch a PreferenceFragment
This needs to be a custom class extending PreferenceFragment, see how here : PreferenceFragment
In your custom Fragment, get hold of your custom-preference. You can use findPreference("custom_switch_key").
add an OnPreferenceChangeListener on the preference
I personally make my fragment implement the listener and pass this as argument.
The return statement is important. This is what makes the actual change in the switch. If you return true the switch will change into the newValue. If you return false, it will not. If you use return false; you can change the value with setChecked(true|false) on the switchpreference.
when you implement onPreferenceChange(Preference preference, Object newValue) you can add whatever functionality you want from pressing the switch-slider only
the functionality from clicking the preference can be done in three ways:
Implement the onClick() further in the custom SwitchPreference class
Implement the method onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) in the fragment
Implement an onPreferenceClickListener like you did for the ChangeListener.
Sorry if this is a long post. It is my first and I have been through so many stackoverflow-pages about this and no one was accurate, just wanted to get it right ;)
After searching for hours more I came across a couple posts that will be helpful to others in this situation.
This one was the solution I opted for given my problem: How do I create one Preference with an EditTextPreference and a Togglebutton?
It's a very detailed answer and is very helpful in understanding preferences.
The other post I came across was this one: http://xgouchet.fr/android/index.php?article4/master-on-off-preferences-with-ice-cream-sandwich
It will give you pretty much the same look and feel as the one above, but requires more work and because of my requirements wouldn't work for me.
i think you are asking about a feature that doesn't exist.
however , since the preference activity uses a listView , you can use some tricks to customize it and handle it however you wish .
here's a post i've made about customizing it , based on this website . what i've asked there is how to add a listView , but i didn't know that a preference activity actually uses a listview .
This took me ages, and none of the answers here worked. I finally found the simplest answer, no custom layout required, in a Github Gist, which I'll preserve here, but I have modified it to work with SwitchPreferenceCompat instead of SwitchPreference.
First, create the custom preference.
package com.mendhak.gpslogger.ui.components;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import androidx.appcompat.widget.SwitchCompat;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreferenceCompat;
/**
* Custom preference for handling a switch with a clickable preference area as well
*/
public class SwitchPlusClickPreference extends SwitchPreferenceCompat {
//
// Public interface
//
/**
* Sets listeners for the switch and the background container preference view cell
* #param listener A valid SwitchPlusClickListener
*/
public void setSwitchClickListener(SwitchPlusClickListener listener){
this.listener = listener;
}
private SwitchPlusClickListener listener = null;
/**
* Interface gives callbacks in to both parts of the preference
*/
public interface SwitchPlusClickListener {
/**
* Called when the switch is switched
* #param buttonView
* #param isChecked
*/
public void onCheckedChanged(SwitchCompat buttonView, boolean isChecked);
/**
* Called when the preference view is clicked
* #param view
*/
public void onClick(View view);
}
public SwitchPlusClickPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SwitchPlusClickPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwitchPlusClickPreference(Context context) {
super(context);
}
//
// Internal Functions
//
/**
* Recursively go through view tree until we find an android.widget.Switch
* #param view Root view to start searching
* #return A Switch class or null
*/
private SwitchCompat findSwitchWidget(View view){
if (view instanceof SwitchCompat){
return (SwitchCompat)view;
}
if (view instanceof ViewGroup){
ViewGroup viewGroup = (ViewGroup)view;
for (int i = 0; i < viewGroup.getChildCount();i++){
View child = viewGroup.getChildAt(i);
if (child instanceof ViewGroup){
SwitchCompat result = findSwitchWidget(child);
if (result!=null) return result;
}
if (child instanceof SwitchCompat){
return (SwitchCompat)child;
}
}
}
return null;
}
#Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final SwitchCompat switchView = findSwitchWidget(holder.itemView);
if (switchView!=null){
switchView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null)
listener.onCheckedChanged((SwitchCompat) v, ((SwitchCompat)v).isChecked());
}
});
switchView.setChecked(getSharedPreferences().getBoolean(getKey(),false));
switchView.setFocusable(true);
switchView.setEnabled(true);
//Set the thumb drawable here if you need to. Seems like this code makes it not respect thumb_drawable in the xml.
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener!=null) listener.onClick(v);
}
});
}
// //Get a handle on the 2 parts of the switch preference and assign handlers to them
// #Override
// protected void onBindView (View view){
// super.onBindView(view);
//
// final Switch switchView = findSwitchWidget(view);
// if (switchView!=null){
// switchView.setOnClickListener(new View.OnClickListener() {
// #Override
// public void onClick(View v) {
// if (listener != null)
// listener.onCheckedChanged((Switch) v, ((Switch)v).isChecked());
// }
// });
// switchView.setChecked(getSharedPreferences().getBoolean(getKey(),false));
// switchView.setFocusable(true);
// switchView.setEnabled(true);
// //Set the thumb drawable here if you need to. Seems like this code makes it not respect thumb_drawable in the xml.
// }
//
// view.setOnClickListener(new View.OnClickListener() {
// #Override
// public void onClick(View v) {
// if (listener!=null) listener.onClick(v);
// }
// });
// }
}
Next in your preference XML, add an entry:
<com.mendhak.gpslogger.ui.components.SwitchPlusClickPreference
android:key="google_drive_enabled"
android:title="#string/google_drive_setup_title"
android:icon="#drawable/googledrive"/>
And then in the code for your Preference Activity class, the real magic is that you use the built-in callbacks for changed, and clicked.
((SwitchPlusClickPreference)findPreference(PreferenceNames.AUTOSEND_GOOGLE_DRIVE_ENABLED)).setSwitchClickListener(new SwitchPlusClickPreference.SwitchPlusClickListener() {
#Override
public void onCheckedChanged(SwitchCompat buttonView, boolean isChecked) {
//The switch bit changed. handle it.
}
#Override
public void onClick(View view) {
// The text bit was clicked on, handle it
}
});
I have an EditPreference in a PreferenceActivity and I have a variable that tells me if I should allow the user to access this preference or to show some alert.
My problem is that I couldn't find how to cancel the preference dialog before it's displayed and to show my alert (according to the variable).
I tried to return true/false in the preference onClick or in onTreeClick but that didn't do anything, the dialog still popped.
On Android 2.1+ .
Thanks.
The DialogPreference.onClick(), which handles clicks to the preference itself, is protected, so you can't override it in your own PreferenceActivity class members.
However, you can extend the class to achieve what you need. Below is a very minimalist example:
package com.example.test;
import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
public class MyEditTextPreference extends EditTextPreference {
private Runnable alternative = null;
public MyDatePickerDialog(Context context,
AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyDatePickerDialog(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyDatePickerDialog(Context context) {
super(context);
}
public void setAlternativeRunnable(Runnable runnable) {
alternative = runnable;
}
// this will probably handle your needs
#Override
protected void onClick() {
if (alternative == null) super.onClick();
else alternative.run();
}
}
In your XML file:
<com.example.test.MyEditTextPreference
android:key="myCustom"
android:title="Click me!" />
In your PreferenceActivity:
MyEditTextPreference pref = (MyEditTextPreference) this.findPreference("myCustom");
pref.setAlternativeRunnable(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplication(), "Canceled!", Toast.LENGTH_SHORT)
.show();
}
});
As a final note, let me say that whenever you can't find a way to do what you want, think about taking a look at how the Android classes themselves work. Most of the times, they will give you good insights to achieve what you want.
In this case, it's the DialogInterface.onClick() method, as described above. So you know you need to override it somehow to achieve that. In this case, the solution is extending the EditTextPreference class itself.
New to Android, I have some code when the user changes a preference I update the Summary field in the UI preference to be the value they entered. However, when the preference activity is created I'd like to set the Summary fields to be the values in the corresponding preferences.
Please advise. Thanks.
public class MyPreferenceActivity extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preference);
SharedPreferences sp = getPreferenceScreen().getSharedPreferences();
sp.registerOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
Preference pref = findPreference(key);
if (pref instanceof EditTextPreference) {
EditTextPreference etp = (EditTextPreference) pref;
pref.setSummary(etp.getText());
}
}
}
I am new too so may not be the best code but this is similar to what I am doing. You probably want to register you listener onResume and unregister it onPause though rather than onCreate. I hope this helps.
Mainly you just need to grab the pref, the pref value and set the summary.
public class MyPreferenceActivity extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preference);
SharedPreferences sp = getPreferenceScreen().getSharedPreferences();
EditTextPreference editTextPref = (EditTextPreference) findPreference("thePrefKey");
editTextPref
.setSummary(sp.getString("thePrefKey", "Some Default Text"));
}
protected void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
Preference pref = findPreference(key);
if (pref instanceof EditTextPreference) {
EditTextPreference etp = (EditTextPreference) pref;
pref.setSummary(etp.getText());
}
}
}
This worked for me.
public class PrefsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener
{
private Preference pref;
private String summaryStr;
String prefixStr;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.prefs);
SharedPreferences sharedPref = getPreferenceScreen().getSharedPreferences();
sharedPref.registerOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
//Get the current summary
pref = findPreference(key);
summaryStr = (String)pref.getSummary();
//Get the user input data
prefixStr = sharedPreferences.getString(key, "");
//Update the summary with user input data
pref.setSummary(summaryStr.concat(": [").concat(prefixStr).concat("]"));
}
}
What I usually do is:
1 - Make a new class which extends the kind of preference I need to show (1 per preference type)
2 - Inside its code, do the appropriate actiob to show the updated summary
3 - Refer this class in the res/xml/preferences.xml file
Let me swo a small example, good for an EditTextPreference:
CLS_Prefs_Edit.java
/**
* CLS_Prefs_Edit class
*
* This is the class that allows for a custom EditTextPrefence
* (auto refresh summary).
*
* #category Custom Preference
* #author Luca Crisi (luca.crisi.lc#gmail.com)
* #copyright Luca Crisi
* #version 1.0
*/
package com.your_name.your_app;
/* -------------------------------- Imports --------------------------------- */
import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
public final class CLS_Prefs_Edit
extends EditTextPreference
{
/* ---------------------------- Constructors ---------------------------- */
public CLS_Prefs_Edit(final Context ctx, final AttributeSet attrs)
{
super(ctx, attrs);
}
public CLS_Prefs_Edit(final Context ctx)
{
super(ctx);
}
/* ----------------------------- Overrides ------------------------------ */
#Override
public void setText(final String value)
{
super.setText(value);
setSummary(getText());
}
}
res/xml/preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
>
<PreferenceCategory android:title="#string/pref_phone_cat">
<!-- NORMAL EditTextPreference, NO summary update -->
<!-- <EditTextPreference -->
<!-- android:widgetLayout="#layout/arr_dn" -->
<!-- android:key="phone" -->
<!-- android:title="#string/pref_phone_title" -->
<!-- android:summary="#string/pref_phone_summ" -->
<!-- android:defaultValue="" -->
<!-- android:inputType="phone" -->
<!-- android:digits="+1234567890" -->
<!-- /> -->
<!-- MY EditTextPreference, WITH summary update -->
<com.your_name.your_app.CLS_Prefs_Edit
android:widgetLayout="#layout/arr_dn"
android:key="phone"
android:title="#string/pref_phone_title"
android:summary="#string/pref_phone_summ"
android:defaultValue=""
android:inputType="phone"
android:digits="+1234567890"
/>
</PreferenceCategory>
</PreferenceScreen>
Of course, set your strings in /res/values/strings, and you're done.
Note that this solution works for both PreferenceFragments and PreferenceActivities.
I'm using it for an app tha runs on 2.2 Froyo (showing a PreferenceActivity) as well as on 4.4 KitKat (showing a PreferenceFragment)
I hope it helps.
First, for your class:
class SettingsFragment : SettingsBaseFragment(),
// Make sure you implement the OnSharedPreferenceChangeListener
SharedPreferences.OnSharedPreferenceChangeListener,
Preference.OnPreferenceClickListener {}
Next, in the onSharedPreferenceChanged, you have a reference to the sharedPreference, which you can use to get the preference result. Then you can use the latest result you get to alter the summary:
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
when (key) {
SETTINGS_AUTO_SETTLEMENT -> refreshSummaryForAutoSettlement(sharedPreferences?.getBoolean(SETTINGS_AUTO_SETTLEMENT, false))
}
}
// helper function for altering the summary for the preference
private fun refreshSummaryForAutoSettlement(isAutoSettlementEnabled: Boolean?) {
when (isAutoSettlementEnabled != null && isAutoSettlementEnabled) {
true -> findPreference(SETTINGS_AUTO_SETTLEMENT).summary = getString(R.string.auto_settlement_enabled_summary)
false -> findPreference(SETTINGS_AUTO_SETTLEMENT).summary = getString(R.string.auto_settlement_disabled_summary)
}
}
I am using a PreferenceActivity to let the user set some values.
I am feeding it the xml file with the defined preferences.
I have set all the android:defaultValue="" for them.
When I start my application, I need the preferences, or if they are not set yet manually, I want the default values:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean value = prefs.getBoolean("key"), false);
However, when android:defaultValue="true" I still get false. So, it looks like the defaultValues set in the XML are not used anywhere but when initializing the preferences-screen.
I don't want to hardcode the default values in the getBoolean() method. So, is there a way get the default-values with only defining these in 1 place?
this question is similar to mine:
initialize-preferences-from-xml-in-main-activity
Just use this code in onCreate method:
PreferenceManager.setDefaultValues(this, R.xml.preference, false);
It will load your preferences from XML, and last parameter (readAgain) will guarantee that user preferences won't be overwritten. That means setting the readAgain argument to false means this will only set the default values if this method has never been called in the past so you don't need to worry about overriding the user's settings each time your Activity is created
Take a look into PreferenceManager.setDefaultValues in Android API for further investigation.
Be aware that if you are using
getSharedPreferences(String sharedPreferencesName, int sharedPreferencesMode)
to retrieve preferences you have to use
PreferenceManager.setDefaultValues(Context context, String sharedPreferencesName, int sharedPreferencesMode, int resId, boolean readAgain)
to set defaults!
For example:
PreferenceManager.setDefaultValues(this, PREFS_NAME, Context.MODE_PRIVATE, R.xml.preference, false);
I hope this can help someone.
in Pixel's accepted answer:
PreferenceManager.setDefaultValues(this, R.xml.preference, false);
it is stated that the false means that defaults won't be overwritten. This is not what it does, it is just an efficiency flag to stop the parsing if your application has more than one entry point. Unfortunately the test is not made per preference file, so if you have more than one preference file you must code true on all but the first.
If you are worried about efficiency, you could code something like this.
final static private int SPL = 1;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (sp.getInt("spl", 0) != SPL)
{
PreferenceManager.setDefaultValues(this, R.xml.prefs1, true);
PreferenceManager.setDefaultValues(this, R.xml.prefs2, true);
sp.edit().putInt("spl", SPL).apply();
}
If you ever add more shared preferences, just set SPL to a hight number.
For example extending DialogPreference I do this:
#Override
protected void onSetInitialValue(boolean restore, Object defaultValue) {
super.onSetInitialValue(restore, defaultValue);
if (restore) {
mValue = shouldPersist() ? getPersistedString(mDefault) : mDefault;
} else {
mValue = mDefault;
}
}
mDefault can be:
mContext.getResources().getString(attrs.getAttributeResourceValue(androidns,"defaultValue", 100));
something you have indexed in R.
Also make sure you have never used the SharedPreferences before. To make sure they are not changed (which means setDefaultValues(this,xml,false) has no effect) uninstall your App and upload it again to be sure no values are touched. This helped me.
define class extends android.preference.Preference
public class IntegerPreference extends Preference {
public IntegerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public IntegerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public IntegerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public IntegerPreference(Context context) {
super(context);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
super.onSetInitialValue(restorePersistedValue, defaultValue);
persistInt((Integer) defaultValue);
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInt(index, -1);
}
}
public class StringSetPreference extends Preference {
public StringSetPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public StringSetPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public StringSetPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StringSetPreference(Context context) {
super(context);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
super.onSetInitialValue(restorePersistedValue, defaultValue);
persistStringSet((Set<String>) defaultValue);
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return Stream.of(a.getTextArray(index)).map(String::valueOf).collect(Collectors.toSet());
}
}
define preference XML resource
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<com.ainirobot.preferencetest.IntegerPreference
android:defaultValue="101"
android:key="III" />
<com.ainirobot.preferencetest.FloatPreference
android:defaultValue="1.2"
android:key="FFF" />
<com.ainirobot.preferencetest.StringPreference
android:defaultValue="SSS"
android:key="SSS" />
<com.ainirobot.preferencetest.BooleanPreference
android:defaultValue="True"
android:key="BBB" />
<com.ainirobot.preferencetest.StringSetPreference
android:defaultValue="#array/sset"
android:key="SSET" />
</PreferenceScreen>
then initialize default value and access
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
Map<String, ?> allKeys = PreferenceManager.getDefaultSharedPreferences(this).getAll();
int iii = PreferenceManager.getDefaultSharedPreferences(this).getInt("III", -1);
float fff = PreferenceManager.getDefaultSharedPreferences(this).getFloat("FFF", 0);
Log.d(TAG, "allKeys=" + allKeys + " iii=" + iii + " fff=" + fff);
//Logcat
10-13 06:53:06.986 12594 12594 D MainActivity: allKeys={III=101, BBB=true, SSS=SSS, FFF=1.2, SSET=[XXX, ZZZ, YYY]} iii=101 fff=1.2