Android support EditTextPreference input type
Asked Answered
A

5

17

Is there any way to specify the input method type for android.support.v7.preference.EditTextPreference?

Amygdalate answered 20/1, 2016 at 17:28 Comment(1)
Related Android issue, no fix in sight.Raisaraise
A
5

Now one can use Android-Support-Preference-V7-Fix library.
Fixed EditTextPreference forwards the XML attributes (like inputType) to the EditText, just like the original preference did.

Amygdalate answered 23/6, 2016 at 10:38 Comment(2)
This library is far superior to android.support.v7.preference. More feature rich, and easier to Theme.Knorring
AndroidX has a setOnBindEditTextListener method which can be used to work around the issue. You can change the EditText input type from within the listener. See developer.android.com/reference/androidx/preference/…Hadsall
T
17

If you don't want to use a third party library, it is possible to specify a layout to the EditTextPreference

<EditTextPreference android:defaultValue="0"
                        android:key="some_key"
                        android:title="Title"
                        android:dialogLayout="@layout/preference_edit_text"/>

Then in res/layout/preference_edit_text.xml

<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <EditText android:id="@android:id/edit"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:inputType="number"
          android:singleLine="true" 
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintTop_toTopOf="parent"
          app:layout_constraintBottom_toBottomOf="parent"
          android:layout_marginStart="21dp" 
          android:layout_marginEnd="21dp"/>

</android.support.constraint.ConstraintLayout>

Please note that the edit text id must be : @android:id/edit but then you are free to use whatever you want inside the android:inputType field

I'm sure there's a better way to align the EditText rather than using 21dp margins but at least it works

Thereat answered 2/1, 2019 at 16:7 Comment(1)
Thanks a lot. Accepted answer offers to use 3rd party lib. More over it's not actually for android-support library but rather for androidX, which is not acceptable in my case.Fugal
A
5

Now one can use Android-Support-Preference-V7-Fix library.
Fixed EditTextPreference forwards the XML attributes (like inputType) to the EditText, just like the original preference did.

Amygdalate answered 23/6, 2016 at 10:38 Comment(2)
This library is far superior to android.support.v7.preference. More feature rich, and easier to Theme.Knorring
AndroidX has a setOnBindEditTextListener method which can be used to work around the issue. You can change the EditText input type from within the listener. See developer.android.com/reference/androidx/preference/…Hadsall
M
4

Here is my version of the answer from Cory Charlton, transfered to Jetpack preferences and written in Kotlin:

 import android.content.Context
 import android.content.SharedPreferences
 import android.text.InputType
 import android.util.AttributeSet
 import androidx.preference.EditTextPreference
 
 
 class EditIntegerPreference : EditTextPreference {
     constructor(context: Context?) : super(context) {
         setInputMethod()
     }
 
     constructor(context: Context?, attributeSet: AttributeSet?) : super(context, attributeSet) {
         setInputMethod()
     }
 
     constructor(context: Context?, attributeSet: AttributeSet?, defStyle: Int) : super(
         context,
         attributeSet,
         defStyle
     ) {
         setInputMethod()
     }
 
     override fun getText(): String =
         try {
             java.lang.String.valueOf(sharedPreferences.getInt(key, 0))
         } catch (e: Exception) {
             "0"
         }
 
     override fun setText(text: String?) {
         try {
             if (text != null) {
                 sharedPreferences?.edit()?.putInt(key, text.toInt())?.apply()
                 summary = text
             } else {
                 sharedPreferences?.remove(key)
                 summary = ""
             }
         } catch (e: Exception) {
             sharedPreferences?.remove(key)
             summary = ""
         }
     }
 
     override fun onSetInitialValue(defaultValue: Any?) {
         val defaultValueInt: Int =
                 when (defaultValue){
                     is Int -> defaultValue
                     is String -> try {defaultValue.toInt()} catch (ex: java.lang.Exception){0}
                     else -> 0
                 }
 
         text = sharedPreferences.getInt(key, defaultValueInt).toString()
     }
 
     private fun setInputMethod() {
         setOnBindEditTextListener {
             it.inputType = InputType.TYPE_CLASS_NUMBER
         }
     }
 
     fun SharedPreferences.remove(key: String) = edit().remove(key).apply()
 }
Mandi answered 30/6, 2020 at 15:54 Comment(1)
What if a PreferenceDatastore is used? How can we then bind the above to a custom DataStore?Oracular
S
3

Edit: The previous answers below were built on the stock android.preference.EditTextPreference and unfortunately don't work for the android.support.v7.preference.EditTextPreference.

In the android.preference.EditTextPreference the EditText control is created programmatically and the AttributeSet from the Preference is passed to it.

android.preference.EditTextPreference Source:

public EditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);

    mEditText = new EditText(context, attrs);

    // Give it an ID so it can be saved/restored
    mEditText.setId(com.android.internal.R.id.edit);

    /*
     * The preference framework and view framework both have an 'enabled'
     * attribute. Most likely, the 'enabled' specified in this XML is for
     * the preference framework, but it was also given to the view framework.
     * We reset the enabled state.
     */
    mEditText.setEnabled(true);
}

White allows us to set the inputType on the Preference itself and have it pass through to the EditText. Unfortunately the android.support.v7.preference.EditTextPreference appears to create the EditText in the Layout

See this issue for ideas on working around this:

Just wanted to let you know that subclassing EditTextPreferenceDialogFragment and overriding onAddEditTextToDialogView as well as overriding PreferenceFragmentCompat#onDisplayPreferenceDialog to show that subclass as needed seems to be working fine, thanks for the help.


Create your own class that extends the EditTextPreference and set it there.

Here's my EditIntegerPreference class:

public class EditIntegerPreference extends EditTextPreference {
    public EditIntegerPreference(Context context) {
        super(context);
    }

    public EditIntegerPreference(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }

    public EditIntegerPreference(Context context, AttributeSet attributeSet, int defStyle) {
        super(context, attributeSet, defStyle);

        getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
        getEditText().setSelectAllOnFocus(true);
    }

    @Override
    public String getText() {
        try {
            return String.valueOf(getSharedPreferences().getInt(getKey(), 0));
        } catch (Exception e) {
            return getSharedPreferences().getString(getKey(), "0");
        }
    }

    @Override
    public void setText(String text) {
        try {
            if (getSharedPreferences() != null) {
                getSharedPreferences().edit().putInt(getKey(), Integer.parseInt(text)).commit();
            }
        } catch (Exception e) {
            // TODO: This catch stinks!
        }
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
        getEditText().setSelectAllOnFocus(true);

        if (restoreValue) {
            getEditText().setText(getText());
        } else {
            super.onSetInitialValue(restoreValue, defaultValue != null ? defaultValue : "");
        }
    }
}

Note that it is possible to add the inputType attribute to the the EditTextPreference

android:inputType="number"

The reason I didn't go this route is that I wanted my preference to get stored as an Integer and not a String

Soprano answered 20/1, 2016 at 17:35 Comment(5)
Unfortunately android.support.v7.preference.EditTextPreference doesn't contain getEditText() method.Amygdalate
@Amygdalate is there a particular reason you need android.support.v7.preference.EditTextPreference? I ask because android.preference.EditTextPreference has existed since API level 1.Soprano
Yep. To provide settings for my app, I'm using PreferenceFragmentCompat.Amygdalate
@Amygdalate bummer I hadn't used PreferenceFragmentCompat yet. Not sure why the support implementation is a regression from the stock Preference. I'll see if I can find anything else.Soprano
@Amygdalate I updated my answer with possible, although more involved, solutions.Soprano
O
0

Workaround for Kotlin + DataStore + androidx.Preference

Disclaimer!

Probably this isn't the way to do it!
Ideally one should only:

  • set the input to int
  • override in a DataStore class: putInt/getInt

Extension Function

In my case I had a PreferenceFragmentCompat, so:

fun PreferenceFragmentCompat.setNumericInput(
@StringRes prefRes: Int, initialValue: String) {

  val preference = findPreference(getString(prefRes)) as EditTextPreference?

  preference?.setOnBindEditTextListener { editText ->
    editText.inputType = InputType.TYPE_CLASS_NUMBER or 
      InputType.TYPE_NUMBER_FLAG_SIGNED

    // set the initial value: I read it from the DataStore and then
    // pass it as the 2nd argument to setNumericInput.
    // BTW, I do store stringPreferenceKeys, as it's the putString method 
    // that get's triggered
    if (editText.text.isEmpty()) editText.setText(initialValue)

    editText.setSelection(editText.text.length) // put cursor at the end
  }

  // to use it in the summary do something like:
  preference?.setOnPreferenceChangeListener { it, newValue ->
    it.summary = "updated: $newValue"
    true
  }
}

Also, in my Activity that extends BaseSettingsActivity I replace the management from SharedPreferences using:

preferenceManager.preferenceDataStore = dataStoreCvLogger 
Oracular answered 22/12, 2021 at 22:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.