EditTextPreference - 只有数值 inputType - 不工作

EditTextPreference - only numeric value inputType - isn't working

<android.support.v7.preference.EditTextPreference
            android:key="prefTest"
            android:title="test number input"
            android:inputType="numberDecimal|numberSigned"
            android:defaultValue="800"/>

它仍然显示常规键盘并允许我输入任何字符

android.support.v7有什么问题吗?

EditTextPreference 小部件应采用与常规 EditText 相同的属性,因此请使用:

android:inputType="number"
android:digits="0123456789"

在 edittext 中使用它,因为它只接受定义的数字。如果它不起作用,则使用 Android-Support-Preference-V7-Fix 库。

修复了 EditTextPreference 将 XML 属性(如 inputType)转发给 EditText,就像原来的首选项一样。

您可以从首选项中检索 EditText,然后从那里设置输入类型或使用 KeyListeners 通知键盘:

EditText et = (EditText) editTextPref.getEditText();
et.setKeyListener(DigitsKeyListener.getInstance());

found the answer on older thread

我同意原来的支持偏好有一些问题,但为了解决这个问题,我们只需要添加自定义布局并指定 EditText 为 android:inputType="number"

<android.support.v7.preference.EditTextPreference
            android:dialogLayout="@layout/preference_dialog_edittext_custom"

以便您可以复制原始 preference_dialog_layout.xml 文件并进行编辑。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="48dp"
    android:layout_marginBottom="48dp"
    android:overScrollMode="ifContentScrolls">

  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginStart="24dp"
      android:layout_marginEnd="24dp"
      android:orientation="vertical">

    <TextView android:id="@android:id/message"
        style="?android:attr/textAppearanceSmall"
        android:layout_marginBottom="48dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="?android:attr/textColorSecondary" />

    <EditText
        android:id="@android:id/edit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"
        android:layout_marginStart="-4dp"
        android:layout_marginEnd="-4dp" />

  </LinearLayout>

</ScrollView>

android.support.v7.preference.EditTextPreference 不包含 getEditText() 方法。

但是我们可以扩展android.support.v7.preference.EditTextPreferenceDialogFragmentCompat来设置inputType。

public class EditTextPreferenceDialogFragmentCompat extends android.support.v7.preference.EditTextPreferenceDialogFragmentCompat {

private EditText mEditText;
private int mInputType;

public static EditTextPreferenceDialogFragmentCompat newInstance(String key, int inputType) {
    EditTextPreferenceDialogFragmentCompat fragment = new EditTextPreferenceDialogFragmentCompat();
    Bundle b = new Bundle(2);
    b.putString("key", key);
    b.putInt("inputType", inputType);
    fragment.setArguments(b);
    return fragment;
}

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mInputType = this.getArguments().getInt("inputType");
}

protected void onBindDialogView(View view) {
    this.mEditText = view.findViewById(android.R.id.edit);
    mEditText.setInputType(mInputType);
    super.onBindDialogView(view);
}

}

然后让你的activity实现PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback

使用您的 EditTextPreferenceDialogFragmentCompat 而不是 android.support.v7.preference.EditTextPreferenceDialogFragmentCompat

public boolean onPreferenceDisplayDialog(@NonNull PreferenceFragmentCompat preferenceFragmentCompat, Preference preference) {
String key = preference.getKey();
if (/**show your dialog*/) {
    EditTextPreferenceDialogFragmentCompat f = EditTextPreferenceDialogFragmentCompat.newInstance(preference.getKey(), InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
    f.setTargetFragment(this, 0);
    f.show(getFragmentManager(), "android.support.v14.preference.PreferenceFragment.DIALOG");
    return true;
}
return false;

}

只需在 xml

中使用 android.support.v7.preference.EditTextPreference
<android.support.v7.preference.PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">
    <android.support.v7.preference.EditTextPreference
        .../>
</android.support.v7.preference.PreferenceScreen>

使用android.support.v14.preference.PreferenceFragment or android.support.v7.preference.PreferenceFragmentCompat even after choosing android:inputType="numberDecimal". This issue could be overcome by using android.preference.PreferenceFragment时会出现这种情况。不幸的是,它在 API 级别 28 中已弃用。

即使重构到 AndroidX,使用 androidx.preference.PreferenceFragment or androidx.preference.PreferenceFragmentCompat 也没有解决问题。

我想我会为 Android-TV 添加我的解决方案,以防有人到达这里并需要它。 android_su 的回复让我找到了正确的方向,不幸的是,LeanbackEditTextPreferenceDialogFragmentCompat 不支持 OnPreferenceDisplayDialogCallback。 相反,我不得不:

覆盖 onCreateView 并使用继承为 LeanbackEditTextPreferenceDialogFragmentCompat 实现一个新的 newInstance 函数:

public static class TvPreferencesDialog extends LeanbackEditTextPreferenceDialogFragmentCompat {

    public static TvPreferencesDialog newInstance(String key) {
        final Bundle args = new Bundle(1);
        args.putString(ARG_KEY, key);

        final TvPreferencesDialog fragment = new TvPreferencesDialog();
        fragment.setArguments(args);

        return fragment;
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View root = super.onCreateView(inflater, container, savedInstanceState);
        final String key = getArguments().getString(ARG_KEY);
        EditText editTextView = root.findViewById(android.R.id.edit);

        if (key.equalsIgnoreCase("some pref key 1")) {
            editTextView.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
        } else if (key.equalsIgnoreCase("some pref key 2")) {
            editTextView.setInputType(InputType.TYPE_CLASS_NUMBER);
            editTextView.addTextChangedListener(new MyTextWatcher());
        }

        return root;
    }    
}

覆盖 onPreferenceDisplayDialog 我的 LeanbackSettingsFragmentCompat:

@Override
public boolean onPreferenceDisplayDialog(@NonNull PreferenceFragmentCompat caller, Preference pref) {
    if (caller == null) {
        throw new IllegalArgumentException("Cannot display dialog for preference " + pref + ", Caller must not be null!");
    }
    final Fragment f;
    if (pref instanceof EditTextPreference) {
        f = TvPreferencesDialog.newInstance(pref.getKey());
        f.setTargetFragment(caller, 0);
        startPreferenceFragment(f);
        return true;
    } else {
        return super.onPreferenceDisplayDialog(caller, pref);
    }
}

开发人员设置指南中的 Customize your settings 部分建议您使用 OnBindEditTextListener 以编程方式设置输入类型,如下所示:

public class SettingsFragment extends PreferenceFragmentCompat {
    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.settings_screen, rootKey);

        EditTextPreference weeklyGoalPref = findPreference("weekly_goal");
        if (weeklyGoalPref != null) {
            weeklyGoalPref.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() {
                @Override
                public void onBindEditText(@NonNull EditText editText) {
                    editText.setInputType(InputType.TYPE_CLASS_NUMBER);
                }
            });
        }
    }
}

我尝试在 xml 中将输入类型定义为 "number",但仍然得到带字母的普通键盘。这种方法对我有用。

不幸的是,在 androidx.Preference(至少在 androidx.preference:preference-ktx:1.1.1)中 inputType 是不工作。

我不得不加载 xml 资源两次:

class SettingsFragment : PreferenceFragmentCompat() {
  override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
    preferenceManager.sharedPreferencesName = "alex"
    setPreferencesFromResource(R.xml.root_preferences, rootKey)
    val xml = resources.getXml(R.xml.root_preferences)
    while (xml.eventType != XmlPullParser.END_DOCUMENT) {
      xml.parseInputType()
      xml.next()
    }
  }
}

我使用了辅助扩展函数:

private fun XmlPullParser.parseInputType(preferenceManager: androidx.preference.PreferenceManager) {
  if (eventType == XmlPullParser.START_TAG && name == "EditTextPreference") {
    val dictionary = HashMap<String, String>()
    for (i in 0 .. attributeCount-1) {
      dictionary.put(getAttributeName(i), getAttributeValue(i))
    }
    val name = dictionary["key"] ?: return
    val pref = preferenceManager.findPreference<EditTextPreference>(name) ?: return
    val inputType = Integer.decode(attributes["inputType"] ?: "1")

    if (inputType != 0) {
      pref.setOnBindEditTextListener { editText -> 
        editText.inputType = inputType
      }
    }
  }
}

对于我使用的 androidx 库

    val preference = findPreference("pref_key") as EditTextPreference?
    
    preference!!.setOnBindEditTextListener { 
            editText -> editText.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_SIGNED
    }

内部 onCreatePreferences 函数 PreferenceFragmentCompat

AndroidX 在 2021 年仍然存在同样的问题...我通过复制“EditTextPreference.java”修复了它 class from the specified link in accepted answer 然后在我的 androidx.preference.EditTextPreference 上使用它而不是 androidx.preference.EditTextPreference XML 首选项屏幕

package myapp.preferences;

import android.content.Context;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.EditText;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.SparseArrayCompat;

import com.averydennison.containertracking.R;

public class EditTextPreference extends androidx.preference.EditTextPreference {
    @Nullable
    private OnBindEditTextListener onBindEditTextListener;

    private SparseArrayCompat<TypedValue> editTextAttributes = new SparseArrayCompat<>();

    private boolean disableMessagePaddingFix;

    @SuppressWarnings("unused")
    public EditTextPreference(Context context) {
        this(context, null);
    }

    @SuppressWarnings("WeakerAccess")
    public EditTextPreference(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.editTextPreferenceStyle);
    }

    @SuppressWarnings("WeakerAccess")
    public EditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    @SuppressWarnings("WeakerAccess")
    public EditTextPreference(Context context, final AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        //TypedArray a = getContext().obtainStyledAttributes(
        //        attrs, R.styleable.EditTextPreference, defStyleAttr, defStyleRes);
        //disableMessagePaddingFix = a.getBoolean(R.styleable.EditTextPreference_pref_disableMessagePaddingFix, false);
        //a.recycle();

        processAttrs(attrs);

        super.setOnBindEditTextListener(new OnBindEditTextListener() {
            @Override
            public void onBindEditText(@NonNull EditText editText) {
                if (!disableMessagePaddingFix) {
                    fixMessagePadding(editText);
                }

                int n = editTextAttributes.size();
                for (int i = 0; i < n; i++) {
                    int attr = editTextAttributes.keyAt(i);
                    TypedValue value = editTextAttributes.valueAt(i);

                    int data = value.data;
                    // TODO resolve resources?

                    switch (attr) {
                        case android.R.attr.inputType:
                            editText.setInputType(data);
                            break;
                        case android.R.attr.textAllCaps:
                            editText.setAllCaps(data == 1);
                            break;
                        case android.R.attr.lines:
                            editText.setLines(data);
                            break;
                        case android.R.attr.minLines:
                            editText.setMinLines(data);
                            break;
                        case android.R.attr.maxLines:
                            editText.setMaxLines(data);
                            break;
                        case android.R.attr.ems:
                            editText.setEms(data);
                            break;
                        case android.R.attr.minEms:
                            editText.setMinEms(data);
                            break;
                        case android.R.attr.maxEms:
                            editText.setMaxEms(data);
                            break;
                    }
                }

                if (onBindEditTextListener != null) {
                    onBindEditTextListener.onBindEditText(editText);
                }
            }
        });
    }

    private void fixMessagePadding(@NonNull View view) {
        ViewParent parent = view.getParent();
        if (parent instanceof ViewGroup) {
            View msgView = ((ViewGroup) parent).findViewById(android.R.id.message);

            if (msgView != null) {
                ViewGroup.LayoutParams layoutParams = msgView.getLayoutParams();

                if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
                    ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) layoutParams;
                    marginLayoutParams.bottomMargin = 0;
                    msgView.setLayoutParams(marginLayoutParams);
                }
            }
        }
    }

    private void processAttrs(AttributeSet attributeSet) {
        if (attributeSet == null) {
            return;
        }

        int n = attributeSet.getAttributeCount();

        for (int i = 0; i < n; i++) {
            int nameRes = attributeSet.getAttributeNameResource(i);
            int resId = attributeSet.getAttributeResourceValue(i, 0);

            TypedValue value = null;

            switch (nameRes) {
                case android.R.attr.inputType:
                    value = new TypedValue();
                    value.resourceId = resId;
                    value.data = attributeSet.getAttributeIntValue(i, InputType.TYPE_CLASS_TEXT);
                    value.type = TypedValue.TYPE_INT_HEX;
                    break;
                case android.R.attr.minEms:
                case android.R.attr.maxEms:
                case android.R.attr.ems:
                case android.R.attr.minLines:
                case android.R.attr.maxLines:
                case android.R.attr.lines:
                    value = new TypedValue();
                    value.resourceId = resId;
                    value.data = attributeSet.getAttributeIntValue(i, -1);
                    value.type = TypedValue.TYPE_INT_DEC;
                    break;
                case android.R.attr.textAllCaps:
                    value = new TypedValue();
                    value.resourceId = resId;
                    value.data = attributeSet.getAttributeBooleanValue(i, false) ? 1 : 0;
                    value.type = TypedValue.TYPE_INT_BOOLEAN;
                    break;
            }

            if (value != null) {
                editTextAttributes.put(nameRes, value);
            }
        }
    }

    /**
     * Returns the {@link OnBindEditTextListener} used to configure the {@link EditText}
     * displayed in the corresponding dialog view for this preference.
     * <p>
     * NOTE that this will return the internal {@link OnBindEditTextListener} instead of the one set
     * via {@link #setOnBindEditTextListener(OnBindEditTextListener)}.
     *
     * @return The {@link OnBindEditTextListener} set for this preference, or {@code null} if
     * there is no OnBindEditTextListener set
     * @see OnBindEditTextListener
     */
    @Nullable
    //@Override
    public OnBindEditTextListener getOnBindEditTextListener() {
        return this.onBindEditTextListener;
        //return super.getOnBindEditTextListener();
    }

    @Override
    public void setOnBindEditTextListener(@Nullable OnBindEditTextListener onBindEditTextListener) {
        this.onBindEditTextListener = onBindEditTextListener;
    }

    @Deprecated
    public EditText getEditText() {
        throw new UnsupportedOperationException("Use OnBindEditTextListener to modify the EditText");
    }

    @Override
    public void setText(String text) {
        String oldText = getText();
        super.setText(text);
        if (!TextUtils.equals(text, oldText)) {
            notifyChanged();
        }
    }
}