android numberpicker 无法读取键盘数字输入

android numberpicker can't read keyboard number input

我正在尝试创建一个每月 select 的数字选择器。 只要我通过滚动 select 值或者如果我使用键盘通过文本输入月份(例如 "jan" for januari),它就可以工作

我还希望我的用户能够向 select januari 输入“1”。 从 numberpicker 源代码来看,这似乎是可行的:

/**
 * @return The selected index given its displayed <code>value</code>.
 */
private int getSelectedPos(String value) {
    if (mDisplayedValues == null) {
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            // Ignore as if it's not a number we don't care
        }
    } else {
        for (int i = 0; i < mDisplayedValues.length; i++) {
            // Don't force the user to type in jan when ja will do
            value = value.toLowerCase();
            if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
                return mMinValue + i;
            }
        }

        /*
         * The user might have typed in a number into the month field i.e.
         * 10 instead of OCT so support that too.
         */
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {

            // Ignore as if it's not a number we don't care
        }
    }
    return mMinValue;
}

问题是,如果我尝试输入数字,EditText 只会保持为空。 这就是我初始化数字选择器的方式:

    //getting the months using Calendar
    Calendar cal = Calendar.getInstance();
    Map<String, Integer> monthMap = cal.getDisplayNames(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
    TreeMap<Integer, String> sorted = new TreeMap<>();
    for(Map.Entry<String, Integer> entry : monthMap.entrySet()) {
        sorted.put(entry.getValue(), entry.getKey());
    }

    String[] displayNames = sorted.values().toArray(new String[]{});

    mMonthPicker.setMinValue(0);
    mMonthPicker.setMaxValue(11);
    mMonthPicker.setDisplayedValues(displayNames);
    mMonthPicker.setWrapSelectorWheel(false);

我尝试使用给定的答案 here 将 edittext 的输入类型设置为 InputType.TYPE_NULL,但这并没有改变任何东西。

如果我尝试输入数字,编辑文本将保持为空。

我终于想通了。由于 EditText 上的过滤器,数字选择器不允许输入月份数字。

我通过从 numberpicker 源代码中复制 NumberKeyListener 的一些代码并对其进行调整使其接受数字输入来解决它。

然后我在 EditText 上添加了这个过滤器,我通过查看子视图并检查当前视图是否是 EditText 来查找它。 我在这里的答案中找到了查找 EditText 的代码:NumberPicker doesn't work with keyboard

我的代码如下所示:

mInputText = findInput(mMonthPicker);
mInputText.setFilters(new InputFilter[]{ new InputTextFilter() });

这是我的过滤器:

/**
 * Filter for accepting only valid indices or prefixes of the string
 * representation of valid indices.
 */
class InputTextFilter extends NumberKeyListener {

    /**
     * The numbers accepted by the input text's {@link android.view.LayoutInflater.Filter}
     */
   private final char[] DIGIT_CHARACTERS = new char[] {
            // Latin digits are the common case
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            // Arabic-Indic
            '\u0660', '\u0661', '\u0662', '\u0663', '\u0664', '\u0665', '\u0666', '\u0667', '\u0668'
            , '\u0669',
            // Extended Arabic-Indic
            '\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8'
            , '\u06f9',
            // Hindi and Marathi (Devanagari script)
            '\u0966', '\u0967', '\u0968', '\u0969', '\u096a', '\u096b', '\u096c', '\u096d', '\u096e'
            , '\u096f',
            // Bengali
            '\u09e6', '\u09e7', '\u09e8', '\u09e9', '\u09ea', '\u09eb', '\u09ec', '\u09ed', '\u09ee'
            , '\u09ef',
            // Kannada
            '\u0ce6', '\u0ce7', '\u0ce8', '\u0ce9', '\u0cea', '\u0ceb', '\u0cec', '\u0ced', '\u0cee'
            , '\u0cef'
    };

    // XXX This doesn't allow for range limits when controlled by a
    // soft input method!
    public int getInputType() {
        return InputType.TYPE_CLASS_TEXT;
    }

    @Override
    protected char[] getAcceptedChars() {
        return DIGIT_CHARACTERS;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        Log.v("filter", "source:" + source.toString());

        CharSequence filtered = String.valueOf(source.subSequence(start, end));

        Log.v("filter", "filtered:" + filtered.toString());
        if (TextUtils.isEmpty(filtered)) {
            return "";
        }
        String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
                + dest.subSequence(dend, dest.length());
        String str = String.valueOf(result).toLowerCase();
        try{
            int value = Integer.parseInt(str);

            if(1 <= value && value <= 12) {
                return source;
            }
        } catch(NumberFormatException e) {
            //continue with the checking
        }

        for (String val : mMonthPicker.getDisplayedValues()) {
            String valLowerCase = val.toLowerCase();
            if (valLowerCase.startsWith(str)) {
                final int selstart = result.length();
                final int selend = val.length();
                mInputText.post(new Runnable() {
                    @Override
                    public void run() {
                        mInputText.setSelection(selstart, selend);
                    }
                });
                return val.subSequence(dstart, val.length());
            }
        }
        return "";

    }
}

这是查找 EditText 的代码:

private EditText findInput(ViewGroup np) {
    int count = np.getChildCount();
    for (int i = 0; i < count; i++) {
        final View child = np.getChildAt(i);
        if (child instanceof ViewGroup) {
            findInput((ViewGroup) child);
        } else if (child instanceof EditText) {
            return (EditText) child;
        }
    }
    return null;
}

为了详细说明这一点,下面是我为获取 EditText 输入值所做的工作(在 SnyersK 代码上进行了扩展)。也可以将自定义编辑文本设置为禁用键盘输入,并在 xml:

中设置最小值和最大值

xml 使用:

<com.octane.smartlink.widgets.SmartLinkNumberPicker
    android:id="@+id/distance_tenths_of_distance"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    max="9"
    min="0"/>

Android 小部件 class:

public class SmartLinkNumberPicker extends NumberPicker { 

    //declare needed variables
    private EditText numberEditText;
    private boolean shouldDisableEditText = true; //set to true to disable edit text input and focus

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

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

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

    private void init(AttributeSet attrs) {
        numberEditText = populateEditText((ViewGroup) this);
        if(numberEditText != null && shouldDisableEditText)
            numberEditText.setFocusable(false);
        processAttributeSet(attrs);
    }

    private EditText populateEditText(ViewGroup viewGroup) {        
        int count = viewGroup.getChildCount();
        for(int i = 0; i < count; i++) {
            final View child = viewGroup.getChildAt(i);
            if (child instanceof ViewGroup)
                populateEditText((ViewGroup) child);
            else if (child instanceof EditText)
                return (EditText) child;
        }

        return null;
    }

    /*
     * used to set min, max attributes in xml 
     * set by min = "0" and max = "9" 
     */
    private void processAttributeSet(AttributeSet attrs) {
        this.setMinValue(attrs.getAttributeIntValue(null, "min", 0));
        this.setMaxValue(attrs.getAttributeIntValue(null, "max", 0));
    }

    /*
     * always returns a valid number since number input is only allowed
     * this will capture value set by either plus/minus buttons or edit text input
     */
    @Override
    public int getValue() {
        return Integer.valueOf(numberEditText.getEditableText().toString());
    }

}