PreferenceActivity: save value as integer
Asked Answered
W

7

53

Using a simple EditTextPreference in my preferences activity:

<EditTextPreference
    android:key="SomeKey"
    android:title="@string/some_title"
    android:summary="..."
    android:numeric="integer"
    android:maxLength="2"
/>

Is there a way that this configuration value would be saved as integer? Seems now it just allows to enter numbers, but the value is still saved as string:

Calling:

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
int value = preferences.getInt("SomeKey", -1);

throws me java.lang.ClassCastException: java.lang.String, and:

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
String value = preferences.getString("SomeKey", "-1");

retrieves the value successfully.

How to make PreferenceActivity to save value as integer by default?

Wizard answered 15/9, 2010 at 20:4 Comment(0)
J
76

You could extend EditTextPreference:

public class IntEditTextPreference extends EditTextPreference {

    public IntEditTextPreference(Context context) {
        super(context);
    }

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

    public IntEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected String getPersistedString(String defaultReturnValue) {
        return String.valueOf(getPersistedInt(-1));
    }

    @Override
    protected boolean persistString(String value) {
        return persistInt(Integer.valueOf(value));
    }
}

It would be better to overwrite onSetInitialValue() and setText() methods, but then you would have to copy some code from a base class. Above solution is simplier, but it's quite tricky - "string" methods do something with ints. Try to not extend this class further ;-)

You could use it from XML by:

<package.name.IntEditTextPreference
    android:key="SomeKey"
    android:title="@string/some_title"
    android:summary="..."
    android:numeric="integer"
    android:maxLength="2"
/>
Jelene answered 20/9, 2010 at 21:22 Comment(10)
@Brutall, I'd like to say thanks to you by improving the code. But someone said to me that the new code is... too long, and he rejected it. Oh, but what do people do if the old code is bad, and need to be replaced, but they don't do it because the new one is... long? Even if it is better. Oh my, that's GREAT! This is place for programmers? I have some open source projects, and I'm opening for any changes to my code. How shame to him! Anyway, thank you so much, Brutall.Noelianoell
Btw, all I do is: set input type to number; add a TextWatcher, to make sure the text user enters is not empty, and default value is 0.Noelianoell
im getting a runtime error on return String.valueOf(getPersistedInt(-1));, cannot start activity due to that lineSonatina
@Noelianoell but is there no preference that defaults to type int? This is crazy that it isn't just there, that all this extra code is needed. Why isn't there just an "EditIntPreference" tag by default?Jonahjonas
@Noelianoell and it's doubly annoying because I just want to expose some internal integer preferences for debugging, they aren't even normally something the user should edit!Jonahjonas
Well, not working. gives error in line return String.valueOf(getPersistedInt(-1)); java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.IntegerDieterich
The following functions support defaultValue ;-) @Override protected String getPersistedString(String defaultReturnValue) { return String.valueOf(getPersistedInt(defaultReturnValue == null ? -1 : Integer.valueOf(defaultReturnValue))); } @Override protected boolean persistString(String value) { return persistInt(value != null ? Integer.valueOf(value) : -1); }Pithead
2019 UPDATE: android:numeric is deprecated, you should use android:inputType="number".Hillhouse
I'd suggest to replace that "-1" there with "Integer.valueOf(defaultReturnValue)" so it uses the actual default value defined in the preference XML fileLancelancelet
@Hillhouse inputType does nothing since EditTextPreference is not an EditText itself. The EditText that really matters is in a dialog so in order to achieve the desired effect one have to override the onBindEditText method and set the input type from there: editText.setInputType(InputType.TYPE_CLASS_NUMBER);Sacrilege
J
6

Even if you set android:numeric="integer" it'll be text preference - as its name suggest. You could easily convert string value to int using Integer.valueOf(). Also you could overwrite PreferenceActivity to do conversion automatically on exit.


I think the best solution is to write simple method to get this value from preferences. Something like:

public static int getSomePref(Context context) {
    SharedPreferences prefs =
        PreferenceManager.getDefaultSharedPreferences(context);
    String value = prefs.getString("SomeKey", null);
    return value == null ? -1 : Integer.valueOf(value);
}

Then you could very easily use it from your code.

Jelene answered 15/9, 2010 at 22:16 Comment(5)
Where can I override this? By default PreferenceActivity does the saving automatically, should I override some method for getting the value or do all saving myself instead?Wizard
Ok, changing type of preference isn't good, cause you may get ClassCastException problems. I have updated my answer.Jelene
Yes, I thought about wrapper myself, but somewhat I still don't like this solution... SharedPreferences has getInt method already, so still hoping somehow to find a way how to tell PreferenceActivity to save the value as int - all functionality is there... just doesn't work as whished...Wizard
You could always wrote your own Preference class.Jelene
The OP has asked how to SAVE the value as an Integer. This answer does NOT address that question, it merely provides a workaround to get an Integer back while still saving a STRING.Lancelancelet
S
4

I know this is an old question with an already accepted answer but I think my solution can be helpful for someone searching for a more complete answer. I have just improved @broot answer a litte and there goes my solution:

Override the EditTextPreference to provide text to int conversion:

public class IntEditTextPreference extends EditTextPreference implements EditTextPreference.OnBindEditTextListener {
    private String mText;
    
    public IntEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setOnBindEditTextListener(this);
    }

    public IntEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnBindEditTextListener(this);
    }

    public IntEditTextPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOnBindEditTextListener(this);
    }

    public IntEditTextPreference(Context context) {
        super(context);
        setOnBindEditTextListener(this);
    }

    /**
     * Saves the text to the current data storage.
     *
     * @param text The text to save
     */
    public void setText(String text) {
        final boolean wasBlocking = shouldDisableDependents();

        mText = text;

        int value = Integer.parseInt(text);

        persistInt(value);

        final boolean isBlocking = shouldDisableDependents();
        if (isBlocking != wasBlocking) {
            notifyDependencyChange(isBlocking);
        }

        notifyChanged();
    }

    /**
     * Gets the text from the current data storage.
     *
     * @return The current preference value
     */
    public String getText() {
        return mText;
    }

    @Override
    protected void onSetInitialValue(Object defaultValue) {
        int value;
        if (defaultValue != null) {
            String strDefaultValue = (String) defaultValue;

            int defaultIntValue = Integer.parseInt(strDefaultValue);
            value = getPersistedInt(defaultIntValue);
        } else {
            value = getPersistedInt(0);
        }

        setText(Integer.toString(value));
    }

    @Override
    public boolean shouldDisableDependents() {
        return TextUtils.isEmpty(mText) || super.shouldDisableDependents();
    }

    @Override
    public void onBindEditText(@NonNull EditText editText) {
        editText.setInputType(InputType.TYPE_CLASS_NUMBER);
    }
}

In the preferences xml:

<your.package.here.IntEditTextPreference
            android:key="some_key"
            android:title="@string/some_title"
            android:defaultValue="5"
            app:useSimpleSummaryProvider="true"/>

Note: Don't use android:numeric nor android:inputType. Since EditTextPreference is not an EditText itself setting those attributes will do nothing. In order to achieve the desired effect on the EditText from the Dialog opened by the EditTextPreference, just set the input type in your custom EditTextPreference by implementing EditTextPreference.OnBindEditTextListener as you can see in the code above.

That's what worked for me.

Sacrilege answered 13/2, 2021 at 17:54 Comment(0)
F
3

Even though an Answer has been parked accepted I would like to share one more shorter way to achieve this :

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
int value = Integer.parseInt(preferences.getString("SomeKey", "-1"));

Since you have already set that only numbers can be entered this won't through any exception. yet to complete my answer :

<EditTextPref
    android:key="SomeKey"
    android:title="@string/some_title"
    android:summary="..."
    android:numeric="integer"
    android:maxLength="2" />
Flitting answered 18/1, 2016 at 10:25 Comment(2)
If you want the value to be persisted as "int", i.e. you want to use preferences.getInt() to load the preference, you need to use the accepted answer.Guereza
2019 UPDATE: android:numeric is now deprecated, you should use instead android:inputType="number".Hillhouse
F
0

I had the same Problem. (I wanted SharedPreference to give me a port number that i stored in a preferences xml file as defaultValue).

Implementing all the SharedPreferences methods would be much effort, so writing a custom method in the class that instanced the SharedPreferences, as broot suggested would be best i think.

You can aswell just use the Static method of Integer in the line where you need it:

int number = Integer.valueOf(settings.getString("myNumberString", "0"));
Fructify answered 18/1, 2016 at 9:58 Comment(0)
T
0

I think this is the shortest one I could come up with:

int CheckInterval = Integer.parseInt(sharedPreferences.getString("check_frequency","60"));
Tussah answered 16/3, 2019 at 9:12 Comment(0)
F
0

Combination of answers from broot and Iogui on Kotlin:

class IntEditTextPreference @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = R.attr.editTextPreferenceStyle,
    defStyleRes: Int = android.R.attr.editTextPreferenceStyle,
) : EditTextPreference(context, attrs, defStyleAttr, defStyleRes),
    EditTextPreference.OnBindEditTextListener {

    init {
        setOnBindEditTextListener(this)
    }

    override fun getPersistedString(defaultReturnValue: String?): String {
        val defaultIntValue = defaultReturnValue?.toIntOrNull() ?: -1
        return getPersistedInt(defaultIntValue).toString()
    }

    override fun persistString(value: String?): Boolean {
        return value?.toIntOrNull()?.let { intValue ->
            persistInt(intValue)
        } ?: false
    }

    override fun onBindEditText(editText: EditText) {
        editText.inputType = InputType.TYPE_CLASS_NUMBER
    }

}

xml:

<your.package.here.IntEditTextPreference
    android:key="some_key"
    android:title="@string/some_title"
    android:defaultValue="5">
Fleeting answered 2/6 at 7:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.