NumberPicker.getValue() on SCROLL_STATE_IDLE, might not be the last updated value

3.7k views Asked by At

I'm using the below function to detect the last final value when user complete their scroll select on NumberPicker. The getValue() would then get the latest value updated.

    numberPicker.setOnScrollListener(new NumberPicker.OnScrollListener() {

        @Override
        public void onScrollStateChange(NumberPicker numberPicker, int scrollState) {
            if (scrollState == NumberPicker.OnScrollListener.SCROLL_STATE_IDLE) {
                int value = numberPicker.getValue();
            }
        }
    });

However, then I found out that, in the event the scroll-touch end not on a definite position of a value, but in between 2 values (e.g. between 1 and 2, but slightly closer towards 2), after letting go the scroll, the function trigger captured the getValue as 1, but the scroll will auto-complete it's scroll to centralize at 2. Therefore the last updated value of the NumberPicker is then set to 2 (not 1 i.e. as was captured in my function above).

How could I get the last updated value of the NumberPicker? Or perhaps a way to detect the final auto scroll of the NumberPicker (when it is centralizing to a specific value)?

FYI. One option is to use setOnValueChangedListener. However this is not ideal for my case, as it capture every single value change even the NumberPicker scrolling is in progress.

Thanks!

3

There are 3 answers

0
Sean On

After so many years, this seems still a known challenge? I feel the last value should be what users intend to use in most of the cases. I wish the Android ADK could make it easier ...

Anyway, I found the solutions above are acceptable but I use the following evolved from all the above:

myPicker.setOnScrollListener({ picker, state ->
            if(state == SCROLL_STATE_IDLE) {
                picker.postDelayed( { // to make sure value is in picker regardless the order of scroll or value change events
                    val newVal = picker.value
                    // use the value and  hidePicker()
                }, 500)
            }
        })
2
Denis Ilatovskiy On

To solve this, I add both OnValueChange and OnScrollListener to NumberPicker. I made private field to keep current scroll state. Both listener interfaces are implemented by same class. onValueChangeListener method checks scroll state, and if it's idle - execute some code. This works for me. Looks something like that:

public class MainActivity extends AppCompatActivity {

    private NumberPicker np;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        np = (NumberPicker) findViewById(R.id.picker);
        PickerListener listener = new PickerListener();
        np.setOnScrollListener(listener);
        np.setOnValueChangedListener(listener);
    }

    private void update(){
        //your code here
    }

    private class PickerListener implements NumberPicker.OnScrollListener, NumberPicker.OnValueChangeListener {
        private int scrollState=0;
        @Override
        public void onScrollStateChange(NumberPicker view, int scrollState) {
            this.scrollState=scrollState;
            if (scrollState==SCROLL_STATE_IDLE){
                update();
            }
        }
        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            if (scrollState==0){
                update();
            }
        }
    }
0
UdaraWanasinghe On

I little bit improved @denis-ilatovskiy 's answer. This one doesn't trigger updates for small changes in the scroll position.

abstract class NumberPickerListener : NumberPicker.OnValueChangeListener, NumberPicker.OnScrollListener {

    private var isValueChanged: Boolean = false
    private var newValue: Int = 0

    override fun onValueChange(picker: NumberPicker?, oldVal: Int, newVal: Int) {
        isValueChanged = true
        newValue = newVal
        onValueChanged(newVal, false)
    }

    override fun onScrollStateChange(view: NumberPicker?, scrollState: Int) {
        if (isValueChanged && scrollState == NumberPicker.OnScrollListener.SCROLL_STATE_IDLE) {
            onValueChanged(newValue, true)
            isValueChanged = false
        }
    }

    abstract fun onValueChanged(value: Int, completed: Boolean)

}