I want to change the values displayed in each Preference in my PreferenceActivity based on the change in another preference.
I have an app that stores metric (float) values in Preferences. I also have a Preference called metric where the user can switch between usage of metric or imperial units.
I only store metric values, but when the user switches to imperial the app displays the imperial values.
Everything works fine if the user only switches the metric preference and leaves the PreferenceActivity. When he goes back the values are displayed the correct way. If he goes to look at userHeight for example the display still shows the metric value though.
In my PreferenceActivity I want to refresh the displayed values when the metric Preference changes (I am making sure that when these values should be stored they will be converted back correctly):
this.metric.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final Boolean metric = Boolean.parseBoolean(newValue.toString());
changeSummaries(metric);
refreshDisplay(metric);
return true;
}
});
What do I have to implement in the refreshDisplay(...) method? It would also help to know at what point of the lifecycle of the PreferenceActivity are the changed values committed?
I found it. At least for my specific problem I found a simple solution.
I had already extended EditTextPreference to be able to store something other than String
Before opening the dialog the onBindDialogView method is called at that point the other changed Preference is committed (I thought at first it might not be - a quick test proved me wrong). Simply overriding onBindDialogView allows me to change the displayed value to the one I want to display:
#Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
getEditText().setText(getPersistedString(""));
}
#Override
protected String getPersistedString(String defaultReturnValue) {
final Float storedValue = getPersistedFloat(-1);
Float returnValue;
if (MyPreferences.isMetric(getContext())) {
returnValue = storedValue;
} else {
returnValue = this.unit.convertToImperial(storedValue);
}
return String.valueOf(returnValue);
}
Related
Im struggling to grasp how to store/read booleans from preferences in LIBGDX. What Im using them for is if the user have bought an item, then the boolean should turn true and be stored as true until a different boolean is turned true , then the first one will be turned false. Hope Im exlpaining myself in an understandable way.
This is what I´ve done:
Screen where they make the purchase and presses the button to make the change:
PurcScreen implements Screen {
changeStone1.addListener(new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
ss.prefs.getBoolean("stone1", true);
game.setScreen(ss);
}
});
And this is the screen where the object they have purchased should be drawn depending on wether or not the boolean is true/false:
MainScreen implements Screen {
public boolean stone1, stone2
public MainScreen {
stone1 = prefs.getBoolean("stone1");
stone2 = prefs.getBoolean("stone2");
if(stone1){
setStone1();
}
if(stone2){
setStone2();
}
show() {
if(stone1) {
setStone2();
prefs.putBoolean("stone1", true);
prefs.putBoolean("stone2", false);
}
if(stone2) {
setStone2();
prefs.putBoolean("stone1", false);
prefs.putBoolean("stone2", true);
}
btnSave.addListener(new ChangeListener() {
#override
public void changed(ChangeEvent event, Actor actor) {
prefs.putBoolean("stone1", stone1);
prefs.putBoolean("stone2", stone2);
prefs.flush();
}
With this code, the booleans arn´t stored. stone2´s image/object is drawn. But as I click savebutton and re-enter the screen the original stone1 object is drawn again.
Get your preferences object from the framework (make sure you get the same name when reading and writing)
Preferences prefs = Gdx.app.getPreferences("My Preferences");
Save a boolean to the prefs object:
prefs.putBoolean("soundOn", true);
prefs.flush();
Read the boolean from the prefs object:
prefs.getBoolean("soundOn");
Reference wiki:
https://github.com/libgdx/libgdx/wiki/Preferences
First of all, ensure you've imported the correct .flush dependency, there is one in both Android and Java.
Try .apply() instead of .flush()
Documentation here
As SharedPreferences instances are singletons within a process, it's safe to replace any instance of commit() with apply() if you were already ignoring the return value.
Further Reading
As explained here:
Get Key, not value, of the ListPreference selection - Possible?
and many other places, it is quite easy to access ListPreferences labels from PreferenceActivity.
What I need to do is to read the label corresponding to the current selected from the main activity.
But I can't find a way.
For example, in my main activity I have
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
I would like to use something like:
ListPreference listPreference = (ListPreference) settings.findPreference("list_of_countries");
And then handle it to get current value and label.
Update 01
I tried to handle another way to retrieve the summary (the name of a country in my example) value which I need to say the customer what he selected. I can't give him the code!
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
String stringSummary = preference.getSummary().toString();
String stringKey = preference.getKey().toString();
if (stringKey.equals("list_of_countries")){
MyActivity.countryName = stringSummary;
}
Nothing to do... the OnPreferenceChangeListener is called.... BEFORE the preference change, i.e., when one item is clicked, so what I succeed to retrieve is just the past summary.
I found no way.
I have a PreferenceScreen in which the user is capable, if system can't autodetect it, to enter the device's phone number. I'm still learning this part of Android but I managed to understand a bit of PreferenceScreens by examples provided by Android SDK itself and a few tutorials.
What I want is that the user can save the phone number only if null or valid, where by "valid" I mean running a generic validation logic (ie. an anonymous method that returns true or false, that can be reused in any possible situation*) or better, just to simplify things, ^(\+39)?3[0-9]{9}$
For now I have the following XML snip
<EditTextPreference
android:inputType="phone"
android:key="#string/preference_phoneNo"
android:selectAllOnFocus="true"
android:singleLine="true"
android:summary="#string/pref_phoneNumber_description"
android:title="#string/pref_phoneNumber" />
and following code by courtesy of Eclipse New Activity wizard:
private void setupSimplePreferencesScreen() {
if (!isSimplePreferences(this)) {
return;
}
addPreferencesFromResource(R.xml.pref_general);
bindPreferenceSummaryToValue(findPreference(getString(R.string.preference_phoneNo)));
}
addPreferenceFromResource is supposed to load the XML node and add the preference to the screen, while binPreferenceSummaryToValue is supposed to make description text change when preference is updated. Just for sake of completeness for those who don't like code courtesy of the IDE, the second method is provided by Eclipse who also provides a private class in the code file that is
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
*/
In the general case, what should I do to perform validation logic before the preference gets saved when I click OK on the preference editor? Where is the validation logic to be put in a PreferenceScreen?
*Aren't we all here to learn?
Android has a built in helper method for this.
String phoneNumber = ...;
boolean valid = PhoneNumberUtils.isGlobalPhoneNumber(phoneNumber);
For a generic, re-usable method, here's the implementation of that method in PhoneNumberUtils, courtesy of AOSP (Apache licensed)
private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
Pattern.compile("[\\+]?[0-9.-]+");
...
public static boolean isGlobalPhoneNumber(String phoneNumber) {
if (TextUtils.isEmpty(phoneNumber)) {
return false;
}
Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
return match.matches();
}
Validation should occur within a Preference.OnPreferenceChangeListener , in the onPreferenceChange method. Simply return false if you don't want the value to be saved.
Example snippet:
private static Preference.OnPreferenceChangeListener myListener =
new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof PhoneNumberPreference) {
return isGlobalPhoneNumber(value);
}
}
...
As a note, since you're starting off with the Settings activity generated by the Eclipse wizard, this listener has already been built for you. You just need to edit it to include validation of the phone number (assuming that's what's being edited), and to return false if the number is invalid, so it won't be saved to preferences.
I have an Android preferences screen where some of the preferences are interdependent.
In other words, if one of the preferences is set to a certain value(s) then another two of the others are available.
If not, then they are not because they are meaningless.
Specifically, I have an option with 3 possible values: Prompt, Yes and No.
When the value is set to No I want to lock the other 2 options.
How do I do this in Android 2.1?
Basically just call setEnabled() on the preferences you want to enable/disable in the OnPreferenceChangeListener for the 3-way preference. e.g.:
otherPrefOne = (ListPreference)findPreference("OTHER_PREF_1");
otherPrefTwo = (ListPreference)findPreference("OTHER_PREF_2");
ThreeWayPref = (ListPreference)findPreference("3WAY_PREF");
ThreeWayPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (((String)newValue).equals("No")) {
otherPrefOne.setEnabled(false);
otherPrefTwo.setEnabled(false);
} else {
otherPrefOne.setEnabled(true);
otherPrefTwo.setEnabled(true);
}
return true;
}
});
What I want to do is I am working on a game of life program. I want to take the time delay and make it a preference, but I want to make it available for people to type in a specific time. The number can be in miliseconds or seconds.
However I'm a little stuck on how to proceed, I haven't been able to find a simple preference that already handles this, but there might be one. Is there an easy way to make this preference and confirm that the entered data is an integer or afloat?
Use an EditTextPreference and set the input type to TYPE_CLASS_NUMBER. This will force the user to enter numbers and not letters.
EditTextPreference pref = (EditTextPreference)findPreference("preference_name");
pref.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
You can also enforce it with the xml attribute android:numeric. The possible relevant values for this attribute are decimal and integer.
You can also do this directly in your preferences.xml. Something like this would work:
<EditTextPreference
android:defaultValue="100"
android:dialogTitle="#string/pref_query_limit"
android:inputType="number"
android:key="pref_query_limit"
android:summary="#string/pref_query_limit_summ"
android:title="#string/pref_query_limit" />
If you are using a PreferenceActivity which you probably are, there is not one available.
You will need to do something like this:
/**
* Checks that a preference is a valid numerical value
*/
Preference.OnPreferenceChangeListener numberCheckListener = new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
//Check that the string is an integer.
return numberCheck(newValue);
}
};
private boolean numberCheck(Object newValue) {
if( !newValue.toString().equals("") && newValue.toString().matches("\\d*") ) {
return true;
}
else {
Toast.makeText(ActivityUserPreferences.this, newValue+" "+getResources().getString(R.string.is_an_invalid_number), Toast.LENGTH_SHORT).show();
return false;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//get XML preferences
addPreferencesFromResource(R.xml.user_preferences);
//get a handle on preferences that require validation
delayPreference = getPreferenceScreen().findPreference("pref_delay");
//Validate numbers only
delayPreference.setOnPreferenceChangeListener(numberCheckListener);
}
In Android Jetpack Preference things changed, to access EditText you have to access like this
val preference = findPreference<EditTextPreference>(getString(R.string.pref_numdefault_key))
preference?.setOnBindEditTextListener {
it.inputType = InputType.TYPE_CLASS_NUMBER
}