How to create a negative NumberPicker

前端 未结 4 741
花落未央
花落未央 2020-12-11 03:35

Does anyone know of an easy way to allow negative numbers with Android\'s default numberpicker? I understand that it\'s the InputFilter that disallows this, but is there an

相关标签:
4条回答
  • 2020-12-11 03:53

    Using .setFormatter() doesn't allow entry of negative numbers. With .setDisplayedValues() the keyboard is shown textual instead of numeric. So here is the class SignedNumberPicker:

    class SignedNumberPicker extends android.widget.NumberPicker {
      int nToAdd=0,mMaxValue=0,mMinValue=0;
      EditText tv;
      private void init() {
        super.setFormatter(new Formatter() {
          @Override
          public String format(int i) {
            int r=i-nToAdd;
            return String.valueOf(r);
          }
        });
        tv.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
        tv.setFilters(new InputFilter[] {new InputTextFilter()});
        tv.setOnFocusChangeListener(new OnFocusChangeListener() {
          @Override
          public void onFocusChange(View view, boolean b) {
            if (b) {
              tv.selectAll();
            } else {
              String str = String.valueOf(((EditText) view).getText());
              if (TextUtils.isEmpty(str.replace("-",""))) {
                tv.setText(String.valueOf(getRealValue()));
              } else {
                textToValue();
              }
              InputMethodManager keyboard = (InputMethodManager) getContext().getSystemService(Context
                  .INPUT_METHOD_SERVICE);
              if (keyboard != null) {
                keyboard.hideSoftInputFromWindow(tv.getWindowToken(),0);
              }
              tv.setVisibility(INVISIBLE);
            }
            if (mOnTextFocusChangedListener!=null) {
              mOnTextFocusChangedListener.onTextFocusChanged(b);
            }
          }
        });
      }
      void textToValue() {
        try {
          setValue(Integer.valueOf(tv.getText().toString()));
        } catch (Exception e) {}
      }
      OnTextFocusChangedListener mOnTextFocusChangedListener =null;
      public interface OnTextFocusChangedListener {
        void onTextFocusChanged(boolean textViewHasFocus);
      }
      public void setOnTextFocusChangedListener(OnTextFocusChangedListener l) {
        mOnTextFocusChangedListener =l;
      }
      public SignedNumberPicker(Context context) {
        super(context);
        init();
      }
      public SignedNumberPicker(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
      }
      @Override
      public void addView(View child) {
        super.addView(child);
        init(child);
      }
      @Override
      public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        init(child);
      }
      @Override
      public void addView(View child, android.view.ViewGroup.LayoutParams params) {
        super.addView(child, params);
        init(child);
      }
      private void init(View view) {
        if(view instanceof EditText){
          tv=((EditText) view);
        }
      }
    
      @Override
      public void setMinValue(int minValue) {
        if (minValue<0) {
          nToAdd=-minValue;
          super.setMaxValue(super.getMaxValue()+nToAdd);
        }
        mMinValue=minValue;
        super.setMinValue(minValue+nToAdd);
      }
    
      @Override
      public void setMaxValue(int maxValue) {
        if (maxValue+nToAdd<0) {
          nToAdd=-maxValue;
        }
        mMaxValue=maxValue;
        super.setMaxValue(maxValue+nToAdd);
      }
    
      @Override
      public void setValue(int value) {
        super.setValue(value+nToAdd);
      }
      public int getRealValue() {
        if (tv.hasFocus()) textToValue();
        return super.getValue()-nToAdd;
      }
      private NumberPicker.OnValueChangeListener mOnValueChangedListener=null;
      @Override
      public void setOnValueChangedListener(OnValueChangeListener onValueChangedListener) {
        mOnValueChangedListener=onValueChangedListener;
        super.setOnValueChangedListener(new OnValueChangeListener() {
          @Override
          public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            mOnValueChangedListener.onValueChange(picker,oldVal-nToAdd,newVal-nToAdd);
          }
        });
      }
    
      //copied from NumberPicker source & modified
      //https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/widget/NumberPicker.java
      class InputTextFilter extends NumberKeyListener {
    
        // 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) {
          // We don't know what the output will be, so always cancel any
          // pending set selection command.
          /*if (mSetSelectionCommand != null) {
            mSetSelectionCommand.cancel();
          }*/
          //and we will ignore this
    
          CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
          if (filtered == null) {
            filtered = source.subSequence(start, end);
          }
    
          String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
              + dest.subSequence(dend, dest.length());
    
          if ("".equals(result)) {
            return result;
          }
          int val = getSelectedPos(result);
    
        /*
         * Ensure the user can't type in a value greater than the max
         * allowed. We have to allow less than min as the user might
         * want to delete some numbers and then type a new number.
         * And prevent multiple-"0" that exceeds the length of upper
         * bound number.
         */
          if (val > mMaxValue || (result.length() > String.valueOf(mMaxValue).length() && result
              .length()>String.valueOf(mMinValue).length()) || (val<0 && val<mMinValue)) {
            return "";
          } else {
            return filtered;
          }
        }
        private int getSelectedPos(String value) {
          try {
            return Integer.parseInt(value);
          } catch (NumberFormatException e) {
            // Ignore as if it's not a number we don't care
          }
          return mMinValue;
        }
        private final char[] DIGIT_CHARACTERS = new char[] {
            //THE MINUS SIGN
            '-',
            // 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'
        };
      }
    }
    

    Use it like "normal" NumberPicker, but feel free to use negative numbers. Instead of getValue(), use getRealValue(), beacuse getValue() is used internally by NumberPicker (!?). I also added setOnTextFocusChangedListener() method, which might be convenient for disabling OK button etc.

    Update: If you want to prevent user from entering the same value, you can disable OK button as long as keyboard is shown, and listen to value change:

    dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
    numberPicker.setOnTextFocusChangedListener(new SignedNumberPicker.OnTextFocusChangedListener() {
      @Override
      public void onTextFocusChanged(boolean textViewHasFocus) {
        if (textViewHasFocus) {
          dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
        } else {
          dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(numberPicker.getRealValue()!=currentValue);
        }
      }
    });
    numberPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
      @Override
      public void onValueChange(android.widget.NumberPicker picker, int oldVal, int newVal) {
        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(newVal!=currentValue);
      }
    });
    
    0 讨论(0)
  • 2020-12-11 04:00

    Here is the example which you are looking for.

    private static final int DEFAULT_MAX = 200;
    private static final int DEFAULT_MIN = 0;// change this acc to your requirement
    
    0 讨论(0)
  • 2020-12-11 04:02

    A more generic and elegant solution is to use NumberPicker.Formatter and use only positive numbers in the NumberPicker.

    Example if I want to select a number in [-50, 50]:

    final int minValue = -50
    final int maxValue = 50
    NumberPicker numberPicker = new NumberPicker(myActivity);
    numberPicker.setMinValue(0);
    numberPicker.setMaxValue(maxValue - minValue);
    numberPicker.setValue(myCurrentValue - minValue);
    numberPicker.setFormatter(new NumberPicker.Formatter() {
        @Override
        public String format(int index) {
            return Integer.toString(index + minValue);
        }
    });
    

    then to get back the selected value:

    int myNewValue = numberPicker.getValue() + minValue
    
    0 讨论(0)
  • 2020-12-11 04:14

    Use:

    
    String[] nums {"-1","-2","-3","-4"};
    numberpicker.setDisplayedValues(nums);
    
    or

    String[] nums = new String[4];
    for(int i=0; i<nums.length; i++)
    nums[i] = "-" + Integer.toString(i);
    numberpicker.setDisplayedValues(nums);
    

    Either of those will let you use any set of Strings for your NumberPicker. What you are doing is you are specifying a set of strings which you the pass to the NumberPicker. Then it will display your values instead of the default ones.

    0 讨论(0)
提交回复
热议问题