13

Is it possible to do that in a more convenient way than handling it in the OnScrollListener event? Pity it doesn't have a step size attribute...

user940016
  • 2,878
  • 7
  • 35
  • 56

6 Answers6

32

The NumberPicker in Android has a method called setDisplayedValues. You can use this one to show custom values (it takes an array of Strings) and then map them when you need the value. So if you need steps of 5 in an minute picker, for example, you can create an array like this:

String[] minuteValues = new String[12];

for (int i = 0; i < minuteValues.length; i++) {
    String number = Integer.toString(i*5);
    minuteValues[i] = number.length() < 2 ? "0" + number : number;
}

minutePicker.setDisplayedValues(minuteValues);

And then when you get the value in the OnValueChangeListener, you just need to cast it back to an integer:

Integer.parseInt(minuteValues[newVal]);
kevinpelgrims
  • 2,148
  • 1
  • 18
  • 29
  • Note that Android displays the custom values in ascending order from top to bottom. You might want to reverse it by replacing `i*5` with `55-i*5` – Bip901 Jul 27 '21 at 12:43
16

To set a step count of '5' for example, use the NumberPicker.Formatter:

    NumberPicker.Formatter formatter = new NumberPicker.Formatter() {
        @Override
        public String format(int value) {
            int temp = value * 5;
            return "" + temp;
        }
    };
    numberPicker.setFormatter(formatter);
easycheese
  • 5,859
  • 10
  • 53
  • 87
  • 1
    actually, this solution is simple but tricky at the same time, I may prefer this way – khalifavi Dec 15 '15 at 14:41
  • 1
    Don't forget to still set the numberPicker.setMaxValue to something reasonable. If you don't set it at all the picker will only show 0. For a hour in 5 minute increments for example you have to set it to 11, since the picker is going to hold 11 numbers (0 - 55). – ckn Jan 06 '16 at 22:56
  • It might also be a good idea to set numberPicker.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); Otherwise the number field in the picker will be able to become focused for input. – ckn Jan 07 '16 at 02:59
  • 2
    @ckn Then the initial number just isn't displayed until scrolled! It usually isn't displayed but would bring up the keyboard for input. Any solution for that? – Flyview Sep 12 '16 at 19:11
7

Why not just add an OnValueChangeListener Something like:

numberPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {

     @Override
     public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
         picker.setValue((newVal < oldVal)?oldVal-5:oldVal+5);
     }

});
ajpolt
  • 1,002
  • 6
  • 10
  • Ok, but that's just like using the onScrollListener. Is using events the only way to accomplish this effect? – user940016 Oct 20 '12 at 04:54
  • Yes, I suppose you could extend the class to have a step size, and then override the onClick method. That's probably more code than just using the listener though. – ajpolt Oct 22 '12 at 16:08
  • The listener method should be onValueChange instead of onChange and the setter should be setOnValueChangedListener now. – Loolooii Jan 30 '14 at 15:37
7

The NumberPicker in Android has a method called setDisplayedValues. You can use this one to show custom values (it takes an array of Strings) and then map them when you need the value.

For example, you can create a function like this:

public String[] getArrayWithSteps (int iMinValue, int iMaxValue, int iStep)
{ 
   int iStepsArray = (iMaxValue-iMinValue) / iStep+1; //get the lenght array that will return

   String[] arrayValues= new String[iStepsArray]; //Create array with length of iStepsArray 

   for(int i = 0; i < iStepsArray; i++)
   {
     arrayValues[i] = String.valueOf(iMinValue + (i * iStep));
   }

   return arrayValues;
}

So, you should call the method> NumberPicker.setDisplayedValues, for example:

int min = 5;
int max = 180;
int step = 10;

String[] myValues = getArrayWithSteps(min, max, step); //get the values with steps... Normally 

//Setting the NumberPick (myNumberPick)
myNumberPick.setMinValue(0);
myNumberPick.setMaxValue((max-step) / min + 1); //Like iStepsArray in the function
//Because the Min and Max Value should be the range that will show. 
//For example, Min = 0 and Max = 2, so the NumberPick will display the first three strings in the array String (myValues); 
myNumberPick.setDisplayedValues(myValues);//put on NumberPicker 

For get the Value in the NumberPick:

String sValue = String.valueOf(10+(myNumberPick.getValue()*5)); //->> (iMinValue + (myNumberPick.getValue()*iStep))
Guihgo
  • 632
  • 10
  • 13
  • 1
    myNumberPick.setMaxValue((max-step)/min+1); //Like iStepsArray in the function It's better if it's : (max-min)/step. - Min could be 0 and divide by 0 it's wrong. - without +1 because you will add 1 value more – Sigvent Mar 07 '17 at 13:06
1

When using methods described above, one needs to be aware that the picker allows user not only to select a value by scrolling, but also by entering it with keyboard.

By default, the input type of the input field is set to TYPE_CLASS_NUMBER, and therefore user is presented with numerical keyboard. It seems that when you use setDisplayedValues the picker changes the type to TYPE_CLASS_TEXT, however, when you use setFormatter the input type is not changed.

Therefore using formatter in this case may lead to unexpected behavior. Let's say you want the user to be able to pick only the values "0" or "5". You may have code like this:

NumberPicker numberPicker = (NumberPicker) findViewById(R.id.my_number_picker);
numberPicker.setMinValue(0);
numberPicker.setMaxValue(1);
numberPicker.setFormatter(v -> v == 0 ? "0" : "5");

However, in this scenario the user is presented with the numerical keyboard, but is able only to enter only "0" or "1".

If you use instead:

numberPicker.setDisplayedValues(new String[] { "0", "5" });

the user will see the text keyboard, but will be able to enter "0" or "5" as expected.

If you are bothered with the text keyboard you can use reflection to access the private field and set the input type back to number (which is of course not recommended unless really necessary). The field is called "mInputText", or "mText" if you target oldies goldies like Gingerbread.

    try {
        Field inputField = NumberPicker.class.getDeclaredField("mInputText");
        inputField.setAccessible(true);

        EditText inputText = (EditText) inputField.get(numberPicker);
        inputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
    } catch (ClassCastException e) {
        // Just ignore this exception and do nothing.
    } catch (IllegalAccessException e) {
        // Just ignore this exception and do nothing.
    } catch (NoSuchFieldException e) {
        // Just ignore this exception and do nothing.
    }
Marcin Jedynak
  • 3,697
  • 2
  • 20
  • 19
1

This is better approach for ajpolt solution

with any predefined step size, it support for custom value set via keyboard.

 final NumberPicker np = (NumberPicker) dialogView.findViewById(R.id.numberPicker1);
    np.setMaxValue(1000); // max value 1000
    np.setMinValue(0);   // min value 0
    np.setValue(defValue);
    np.setWrapSelectorWheel(false);
    final int m_oldFocus = np.getDescendantFocusability();
    np.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
    np.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            np.setDescendantFocusability(m_oldFocus);
            return false;
        }
    });

    np.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
        @Override
        public void onValueChange(NumberPicker numberPicker, int oldVal, int newVal) {
            int stepSize = 10;
            if(newVal%stepSize !=0){
                if(newVal < oldVal){
                    numberPicker.setValue(((int)(newVal/stepSize)) *stepSize);
                }else{
                    numberPicker.setValue((((int)(newVal/stepSize)) *stepSize ) +stepSize );
                }

            }else{
                numberPicker.setValue(newVal);
            }

        }
    });

*I know this is 5 years old Question, but might be useful for somebody

UdayaLakmal
  • 4,035
  • 4
  • 29
  • 40