How to implement android Minutes-Seconds picker?

user2704782 picture user2704782 · Oct 23, 2013 · Viewed 9.8k times · Source

I need to implement a dialog in which the user can enter a time in minutes and seconds. For example: 6 minutes 30 seconds. There I also need an upper limit on the number of minutes (i.e., the minutes would range from 0-10, seconds from 0-60.)

But as has been discussed here, the android TimePicker presents only hours and minutes. I've seen a few posts about adding seconds to it, but nothing about removing hours and adding seconds. Probably no way to impose a limit on the minutes, either.

I've seen a few posts recommending creating a custom time picker by using NumberPicker, but have also seen cautions that NumberPicker is intended for internal purposes and shouldn't be used. This would appear to be a Catch-22.

I could try to modify the source for TimePicker or NumberPicker, but I'm not wild about the amount of time it would take to understand how they work and to maintain a custom version going forward.

I guess that leaves me with implementing a custom Minutes-Seconds picker. Using up-down buttons to move a number doesn't look too hard, but I don't know how to allow the user to swipe to scroll through the numbers in each "wheel".

Any suggestions?

Answer

arlomedia picture arlomedia · Jul 27, 2014

After using this SO answer to add a minutes interval to the TimePickerDialog, I made a similar subclass that extends the range of the hours spinner to 0-60 and treats the hours:minutes spinners as minutes:seconds:

import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.widget.NumberPicker;
import android.widget.TimePicker;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class DurationPicker extends TimePickerDialog {

    private TimePicker timePicker;
    private final OnTimeSetListener callback;

    private int initialMinutes;

    public DurationPicker(Context context, OnTimeSetListener callBack, int minutes, int seconds) {
        super(context, callBack, minutes, seconds, true); // the last argument removes the am/pm spinner
        this.callback = callBack;
        this.initialMinutes = minutes; // we'll have to set this again after modifying the "hours" spinner
        this.setTitle("Set duration");
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (callback != null && timePicker != null) {
            timePicker.clearFocus();
            callback.onTimeSet(timePicker, timePicker.getCurrentHour(), timePicker.getCurrentMinute());
        }
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        try {
            Class<?> classForid = Class.forName("com.android.internal.R$id");
            Field timePickerField = classForid.getField("timePicker");
            this.timePicker = (TimePicker) findViewById(timePickerField.getInt(null));
            Field field = classForid.getField("hour");

            // modify the hours spinner to cover the maximum number of minutes we want to support
            int maxMinutes = 60;
            NumberPicker mHourSpinner = (NumberPicker)timePicker.findViewById(field.getInt(null));
            mHourSpinner.setMinValue(0);
            mHourSpinner.setMaxValue(maxMinutes);
            List<String> displayedValues = new ArrayList<String>();
            for (int i = 0; i <= maxMinutes; i++) {
                displayedValues.add(String.format("%d", i));
            }
            mHourSpinner.setDisplayedValues(displayedValues.toArray(new String[0]));
            mHourSpinner.setValue(initialMinutes); // we can set this again now that we've modified the hours spinner
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

In the callback, just treat the value from the second argument as minutes and the value from the third argument as seconds.

It's possible that a future version of Android could return incorrect results when the "minutes" spinner is set to a number above 24, but I read through the current TimePicker code and didn't see any problems with this. Also, it might be possible to modify or replace the am/pm spinner with a seconds spinner, and then the picker could support hours:minutes:seconds as well as minutes:seconds.