Calling setDefaultValue() on a custom Preference class does not set the default value. Why?

7.5k views Asked by At

I'm extending PreferenceActivity for my settings screen. In this preference activity i have a couple of preferences one of which is custom made. The problem is as follows:

in this custom preference (which extends from ListPreference) i want to be able to set the default value, so i override the setDefaultValue() method. In this method i do some parsing so it'll take the correct value. When i'm trying to read this value with the getValue() function it just returns null.

So i figured, what happens when i just put some hardcoded value in there (you know, maybe i did something wrong, wouldn't be the first time). Well, i still get null back.

Any ideas what i'm doing wrong?

Edit:
Setting the defaultValue in the xml file isn't really an option because the values aren't known until i retrieve them.

I made a workaround:

  • When app is started for the first time: get data
  • Set the values in the preference.

This way i set the default preference when i'm collection the data

7

There are 7 answers

1
Dave On BEST ANSWER

setDefaultValue doesn't work the way you think it does. Look at the source of Preference.java and you'll the logic behind it all.

The preferred way to set a default is to specify the android:defaultValue attribute in the preferences.xml file of your app.

0
android developer On

This is what I did and worked for me:

class DefaultValueEditTextPreference : androidx.preference.EditTextPreference {
    @Suppress("unused")
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
    @Suppress("unused")
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    @Suppress("unused")
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    @Suppress("unused")
    constructor(context: Context?) : super(context)

    init {
        text = ... //the default, dynamic text that you want to have
    }

}
0
beigirad On

I converted preferences .xml to code. All setDefaultValues works well there.

val screen = preferenceManager.createPreferenceScreen(context)

val editText = EditTextPreference(context).apply {
         setIcon(R.drawable.lobat_cloud)
         key = "key"
         title = "MyPreferences"
         ...

         setDefaultValue("My Default Value")
      }

screen.addPreference(editText)

// add other preferences

preferenceScreen = screen

more info

P.S: I found this way more smaller and clear than customizing all preferences or other answers.

0
ehartwell On

I finally found the solution (somewhere besides StackOverflow, for once).

When you create a custom Preference class,

  1. You need to implement onSetInitialValue as XåpplI'-I0llwlg'I - pointed out
  2. You also need to implement onGetDefaultValue(TypedArray a, int index)

For example, if the custom preference is saved as an int,

@Override
protected void onSetInitialValue(boolean restore, Object defaultValue) {
    setValue(restore ? getPersistedInt(FALLBACK_DEFAULT_VALUE) : (Integer) defaultValue);
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
   return a.getInteger(index, FALLBACK_DEFAULT_VALUE);
}

Now PreferenceManager.setDefaultValues() finally loads the android:defaultValue for the custom preferences too. Still no fix for nulls and false, but there are workarounds for those posted elsewhere.

1
Sandstone On

I think this works too at anytime.

 Preference aaa = (Preference) findPreference("xxx");
 aaa.setOnPreferenceClickListener(new OnPreferenceClickListener() {

              public boolean onPreferenceClick(Preference preference) {

                    // For edit text preference
                    ((EditTextPreference)preference).getEditText().setText("foobar");


                    // for list preference
                    (ListPreference)preference).setValue("foobar");

                    // etc ...

            return true;
              }
 });

This code will detect when the dialog is about to launch and populate the EditText or List in the dialog with your default value.

0
vir us On

You can extend preference and set the default value during constructing like this:

package com.example.package.preference;

public class CustomPreference extends ListPreference{

public CustomPreference(Context context) {
    super(context);
    init();
}

public CustomPreference(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

private void init() {
    Object anyDefaultValueFromCode = ...
    setDefaultValue(anyDefaultValueFromCode );
}
}

then you can use it from XML like this:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="alarm_prefs_screen"
android:title="@string/set_alarm" >

<com.example.package.preference.CustomPreference
    android:key="custom_preference"
    android:title="@string/any_title" />

</PreferenceScreen>
0
XåpplI'-I0llwlg'I  - On

If you want to call getValue() after calling setDefaultValue() to retrieve a default value the first time your PreferenceActivity opens, you need to override onSetInitialValue() in your Preference subclass. Otherwise, the default value will not be set when you call getValue() and it will return a null (as you experienced).

For example, if your default value is an integer, your onSetInitialValue() might look like this:

@Override
protected void onSetInitialValue(boolean restore, Object defaultValue)
{
    setValue(restore ? getPersistedInt(DEFAULT_VALUE) : (Integer) defaultValue);
}

DEFAULT_VALUE is just a private constant inside the Preference to be used in case the persisted int cannot be retrieved. setValue() is the public setter to complement your getValue() public getter, and should look something like this:

public int getValue()
{
    return mValue;
}

public void setValue(int value)
{
    if (value != mValue)
    {
        mValue = value;
        persistInt(value);
    }
}

For more information about onSetInitialValue(), refer to the API documentation here.

It's also a good idea to look at the source code of the Preference class (here) to understand why onSetInitialValue() needs to be implemented. In particular, have a look at setDefaultValue(), and then look at dispatchSetInitialValue().