4

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.

JeanBrand
  • 73
  • 1
  • 1
  • 6

6 Answers6

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();
    }
}
Alex Wang
  • 998
  • 1
  • 9
  • 13
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.

01.sunlit
  • 305
  • 1
  • 2
  • 8
  • 1
    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? – Dan2552 Feb 03 '14 at 15:40
  • @Dan2552 `R.id.minute` is the id for the minute spinner used in TimePicker. You can find it in `/res/layout/time_picker.xml`. – phse Mar 24 '14 at 15:15
  • 1
    Sorry for commenting now, but if you are extending `TimePicker` and want to access the IDs of the `NumberPicker`s, you can do so by using `com.android.internal.R.id.minute/hour`. – Asaf Jul 17 '14 at 20:23
  • 3
    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. – minipif Jun 29 '15 at 02:00
  • @01-sunlit Do you know how to stop hour changing while we are changing minutes? – zkvarz Jan 05 '17 at 20:29
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
    }
  }
Andrey
  • 722
  • 7
  • 11
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);  
}
Andrew Prock
  • 6,900
  • 6
  • 40
  • 60
Linga
  • 10,379
  • 10
  • 52
  • 104
  • 1
    The rusult of this code is that the only amount i can see is 5, maybe i implemented it wrong? – JeanBrand Apr 24 '13 at 09:33
  • 2
    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? – JeanBrand Apr 29 '13 at 12:23
  • Is there any complete solution for this problem? My application needs to set minimum time too – JAPS Jul 25 '14 at 11:36
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;
    }
}
Logi24
  • 528
  • 6
  • 17
Dror
  • 1
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()
}
Haris Ribic
  • 359
  • 3
  • 10