Search code examples
javaandroidandroid-studiokotlintimepicker

android studio 30 min interval on time picker(spinner)


I'm using a time picker spinner. I would like to implement 30 min intervals.
The code I'm using outputs this effect as shown in the below image.
enter image description here

Here's the code:

picker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
    @Override
    public void onTimeChanged(TimePicker view, int hour, int min) {

        timeoutput.setText(String.format("%02dh%02d", hour, min));
    }
});

back.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(Schedule.this, choosewalk.class);
        startActivity(intent);
    }
});

@SuppressLint("DefaultLocale")
private void TimeInterval(TimePicker picker) {
    try {
        int Interval = 30;
        NumberPicker minute = (NumberPicker) picker.findViewById(Resources.getSystem().getIdentifier(
            "minute", "id", "android"));
        minute.setMinValue(0);
        minute.setMaxValue((60 / Interval) - 1);
        List<String> displayedValue = new ArrayList<String>();
        for (int i = 0; i < 60; i += Interval) {
            displayedValue.add(String.format("%02d", i));
        }
        minute.setDisplayedValues(displayedValue.toArray(new String[0]));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

I know that that there are similar questions, but for this specific problem, I'm struggling. I know the method works for intervals of 5, 10, 15, etc. It's altering the code for 30 min intervals that's problematic.


Solution

  • Ok, here's your problem. You want a wheel with 00 and 30 on it, and you want to be able to infinitely scroll it, so it ticks over from 30 to 00 and increments the hour wheel each time, right? Which is basically how the TimePicker works by default.

    The issue is this, from NumberPicker:

    Note: If the number of items, i.e. the range ( getMaxValue() - getMinValue()) is less than the number of items shown on the selector wheel, the selector wheel will not wrap.

    the max - min thing is an extremely awkward way of expressing this (the number of items is actually that + 1) - but basically the spinner shows three items on-screen at a time, and you need to have more than three items (so at least one is off-screen) for wrapping to work. That's why is works for your other intervals (15 mins gives you four items, so that wraps)

    Unfortunately I don't think there's a lot you can do about it - the lack of wrapping is hardcoded behaviour in NumberPicker. You could fake it by having four items, 00, 30, 00, 30 but internally that's still values 0 to 3, one rotation of the wheel, not two. And the TimePicker uses those values, comparing to minValue and maxValue to check when the wheel has wrapped and the hour needs to automatically change.

    The listener that handles this (in TimePickerSpinnerDelegate) can't be read, so you can't wrap it in a listener that pretends it's going 0 1 0 1 instead. The listener also refers to a bunch of internal private variables and methods, so you can't just copy it into your own code and make the tweaks. You'd have to reimplement the whole widget and its related classes by the looks of things


    If it works for you, you could just throw two NumberPickers together and remake it yourself. You'll lose things like the AM/PM functionality, probably accessibility, it depends if you care or not. This basically works:

    public class MainActivity extends AppCompatActivity implements NumberPicker.OnValueChangeListener {
    
        private NumberPicker mMinuteSpinner;
        private NumberPicker mHourSpinner;
    
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mMinuteSpinner = findViewById(R.id.minPicker);
            mHourSpinner = findViewById(R.id.hourPicker);
            
            mMinuteSpinner.setMinValue(0);
            mMinuteSpinner.setMaxValue(3);
            mMinuteSpinner.setDisplayedValues(new String[]{"00", "30", "00", "30"});
            mMinuteSpinner.setOnValueChangedListener(this);
    
            mHourSpinner.setMinValue(0);
            mHourSpinner.setMaxValue(23);
            String[] hours = new String[24];
            for (int i = 0; i < 24; i++) {
                hours[i] = Integer.toString(i + 1);
            }
            mHourSpinner.setDisplayedValues(hours);
        }
    
        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            boolean advanced = (newVal == 0 && oldVal == 3) || (newVal == 2 && oldVal == 1);
            boolean regressed = (newVal == 3 && oldVal == 0) || (newVal == 1 && oldVal == 2);
            if (advanced) {
                mHourSpinner.setValue(mHourSpinner.getValue() + 1);
            } else if (regressed) {
                mHourSpinner.setValue(mHourSpinner.getValue() - 1);
            }
        }
    
    }
    

    Haven't touched Java in a while! There might be a better way to do that. But yeah, the listener is just checking to see if it's passing between the two rollover points on the four-item list (where it goes from 30 to 00 scrolling one way, or vice versa scrolling the other way). This is basically how the TimePicker works