How do i change the Android TimePicker minute intervals?
Asked Answered
B

6

5

I am writing an application where the user needs to specify a given point in time, but i can't seem to figure out how to set the minute values that the user can choose from, to only use increments of 5 instead of increments of 1.

Simply put, when the user scrolls through the available amounts, he/she must only see 0,5,10,15 etc.

Thank you in advance.

Bayless answered 24/4, 2013 at 8:40 Comment(0)
G
7

To ensure you're compatible with API 21+, make sure your TimePicker has the following attribute:

android:timePickerMode="spinner"

Then here's how you can set an interval programmatically. This method falls back on the standard TimePicker if the minute field cannot be found:

private static final int INTERVAL = 5;
private static final DecimalFormat FORMATTER = new DecimalFormat("00");

private TimePicker picker; // set in onCreate
private NumberPicker minutePicker;

public void setMinutePicker() {
    int numValues = 60 / INTERVAL;
    String[] displayedValues = new String[numValues];
    for (int i = 0; i < numValues; i++) {
        displayedValues[i] = FORMATTER.format(i * INTERVAL);
    }

    View minute = picker.findViewById(Resources.getSystem().getIdentifier("minute", "id", "android"));
    if ((minute != null) && (minute instanceof NumberPicker)) {
        minutePicker = (NumberPicker) minute;
        minutePicker.setMinValue(0);
        minutePicker.setMaxValue(numValues - 1);
        minutePicker.setDisplayedValues(displayedValues);
    }
}

public int getMinute() {
    if (minutePicker != null) {
        return (minutePicker.getValue() * INTERVAL);
    } else {
        return picker.getCurrentMinute();
    }
}
Gaynellegayner answered 28/8, 2015 at 13:32 Comment(1)
I want to keep Clock mode as it is. can we do this?Monopode
T
4

all relative answer need you to set an OnTimeChangedListener. My resolution is that you extends android TimePicker,and modify the constructor of it:

    // minute
    mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
    mMinuteSpinner.setMinValue(0);
    mMinuteSpinner.setMaxValue(3);
    mMinuteSpinner.setDisplayedValues(new String[]{"0", "15", "30", "45"});
    mMinuteSpinner.setOnLongPressUpdateInterval(100);
    mMinuteSpinner.setFormatter(NumberPicker.getTwoDigitFormatter());

so you can have the interval you want.

Trinitrocresol answered 2/7, 2013 at 11:10 Comment(5)
This worked for us but we had a bit of a hacky search for the minute spinner as it didn't know what R.id.minute was. Where does R.id.minute come from?Dorey
@Dorey R.id.minute is the id for the minute spinner used in TimePicker. You can find it in <android-sdk>/res/layout/time_picker.xml.Parra
Sorry for commenting now, but if you are extending TimePicker and want to access the IDs of the NumberPickers, you can do so by using com.android.internal.R.id.minute/hour.Dispossess
The mMinuteSpinner variable is present in the TimePicker class in SDK's versions 4.4.4, but not in 2.0 or 5.1.0, for example.Adverbial
@01-sunlit Do you know how to stop hour changing while we are changing minutes?Pahari
R
3

Thanks @Quadddd, this is the code in Kotlin

private val INTERVAL = 5


private val FORMATTER = DecimalFormat("00")

  private var picker: TimePicker? = null // set in onCreate
  private var minutePicker: NumberPicker? = null

  fun setMinutePicker() {
    val numValues = 60 / INTERVAL
    val displayedValues = arrayOfNulls<String>(numValues)
    for (i in 0 until numValues) {
      displayedValues[i] = FORMATTER.format(i * INTERVAL)
    }

    val minute = picker?.findViewById<NumberPicker>(Resources.getSystem().getIdentifier("minute", "id", "android"))
    if (minute != null) {
      minutePicker = minute
      minutePicker!!.minValue = 0
      minutePicker!!.maxValue = numValues - 1
      minutePicker!!.displayedValues = displayedValues
    }
  }

  fun getMinute(): Int {
    return if (minutePicker != null) {
      minutePicker!!.getValue() * INTERVAL
    } else {
      picker!!.currentMinute
    }
  }
Refutative answered 2/7, 2018 at 10:27 Comment(0)
M
0

You can do this programmatically.

pickStartTime = (TimePicker)findViewById(R.id.StartTime);
pickStartTime.setOnTimeChangedListener(mStartTimeChangedListener);
int nextMinute = 0;

Next, Set the OnTimeChangedListener as shown below

private TimePicker.OnTimeChangedListener mStartTimeChangedListener =
    new TimePicker.OnTimeChangedListener() {
        public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
            updateDisplay(view, startDate, hourOfDay, minute);          
        }
    };

private TimePicker.OnTimeChangedListener mNullTimeChangedListener =
    new TimePicker.OnTimeChangedListener() {
        public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {}
    };

And,

private void updateDisplay(TimePicker timePicker, Date date, int hourOfDay, int minute) 
{ 
    nextMinute = nextMinute+5;

    // remove ontimechangedlistener to prevent stackoverflow/infinite loop
    timePicker.setOnTimeChangedListener(mNullTimeChangedListener);

    // set minute
    timePicker.setCurrentMinute(nextMinute);

    // look up ontimechangedlistener again
    timePicker.setOnTimeChangedListener(mStartTimeChangedListener);

    // update the date variable for use elsewhere in code
    date.setMinutes(nextMinute);  
}
Mydriasis answered 24/4, 2013 at 8:53 Comment(3)
The rusult of this code is that the only amount i can see is 5, maybe i implemented it wrong?Bayless
It does work in the sense that the user can only select increments of 5, but the options at the the top and bottom of the displayed result, are not increments of 5, do you know how i can bind the amounts available in the spinner to only be increments of 5?Bayless
Is there any complete solution for this problem? My application needs to set minimum time tooDithyrambic
B
0

After searching the net, I didn't find the simplest solution.
So I decided to share:
You can use reflections!
The following code use intervals of 10 minutes, but you can choose the minutes you want to use in the DISPLAYED_MINS array.

private final static String[] DISPLAYED_MINS = { "0", "10", "20", "30", "40", "50" };

private NumberPicker getMinuteSpinner(TimePicker t)
{
    try
    {
        Field f = t.getClass().getDeclaredField("mMinuteSpinner"); // NoSuchFieldException
        f.setAccessible(true);
        return (NumberPicker) f.get(t); // IllegalAccessException
    }
    catch (Exception e)
    {
        Log.e("timepicker","field name has been changed, check grepcode");
        return null;
    }
}

when you create your timepicker:

final TimePicker dpStartDate = (TimePicker) view.findViewById(R.id.dpStartDate);
NumberPicker startMinSpiner = getMinuteSpinner(dpStartDate);
if (null != startMinSpiner)
{
    startMinSpiner.setMinValue(0);
    startMinSpiner.setMaxValue(DISPLAYED_MINS.length - 1);
    startMinSpiner.setDisplayedValues(DISPLAYED_MINS);
}

Just don't forget that getCurrentMinute() return the selected minute in the DISPLAY_MINS array. in order to get the minute:

int theTime = 0;
for (String number: DISPLAYED_MINS) {
    int theNumber = Integer.parseInt(number);
    if (theNumber % 5 != 0) {
        theNumber = ((int)(theNumber / 5)) * 5;
    }
    if (theNumber == dpStartDate.getCurrentMinute()) {
        theTime = theNumber;
        break;
    }
}
Barozzi answered 5/10, 2013 at 17:20 Comment(1)
Just an FYI, this no longer works in Android L/Lollipop.Tetra
I
0

As a follow up on @Andrey Kotlin solution, you can make it extensible to specify any given minute interval. Also, using kotlin ranges and step keyword, you can have a bit fancier code:

private fun setupMinutesPicker(minVal: Int, maxVal: Int) {
    minsNumberPicker.displayedValues = null

    minsNumberPicker.minValue = minVal / MINUTE_INTERVAL_STEP
    minsNumberPicker.maxValue = maxVal / MINUTE_INTERVAL_STEP

    minsNumberPicker.displayedValues = (minVal..maxVal step MINUTE_INTERVAL_STEP)
        .toList()
        .map { mins -> FORMATTER.format(mins) }
        .toTypedArray()
}
Intonate answered 3/6, 2020 at 12:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.