Show TimePicker with minutes intervals in android

AndroidIntervalsAndroid Timepicker

Android Problem Overview


My application show a TimePickerDialog to set a time. I want that the timePickerDialog show the minutes with an interval of 5 minutes.

This works fine with this code:

private final int TIME_PICKER_INTERVAL=5;
private boolean mIgnoreEvent=false;
…
	public TimePickerDialogs(Context arg0, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
	super(arg0, callBack, hourOfDay, minute, is24HourView);
			
	formato=Statics.formato;
}

@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
	//super.onTimeChanged(arg0, arg1, arg2);

	if (mIgnoreEvent)
		return;
	if (minute%TIME_PICKER_INTERVAL!=0){
		int minuteFloor=minute-(minute%TIME_PICKER_INTERVAL);
		minute=minuteFloor + (minute==minuteFloor+1 ? TIME_PICKER_INTERVAL : 0);
		if (minute==60)
			minute=0;
		mIgnoreEvent=true;
		view.setCurrentMinute(minute);
		mIgnoreEvent=false;
	}
}

Although only minutes can be selected with an interval of five minutes, the timepickerdialog looks like:

hours and minutes

do not know how the minutes also show the range of 5 minutes, as in this picture:

hours and minutes - 5

I have searched but can not find the solution.

Android Solutions


Solution 1 - Android

Use the following the custom class called CustomTimePickerDialog, which I think solve your problem.

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

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

public class CustomTimePickerDialog extends TimePickerDialog {
	
	private final static int TIME_PICKER_INTERVAL = 5;
	private TimePicker mTimePicker;
	private final OnTimeSetListener mTimeSetListener;

	public CustomTimePickerDialog(Context context, OnTimeSetListener listener,
			int hourOfDay, int minute, boolean is24HourView) {
		super(context, TimePickerDialog.THEME_HOLO_LIGHT, null, hourOfDay,
				minute / TIME_PICKER_INTERVAL, is24HourView);
		mTimeSetListener = listener;
	}

	@Override
	public void updateTime(int hourOfDay, int minuteOfHour) {
		mTimePicker.setCurrentHour(hourOfDay);
		mTimePicker.setCurrentMinute(minuteOfHour / TIME_PICKER_INTERVAL);
	}

	@Override
	public void onClick(DialogInterface dialog, int which) {
		switch (which) {
			case BUTTON_POSITIVE:
				if (mTimeSetListener != null) {
					mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
							mTimePicker.getCurrentMinute() * TIME_PICKER_INTERVAL);
				}
				break;
			case BUTTON_NEGATIVE:
				cancel();
				break;
		}
	}

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

			NumberPicker minuteSpinner = (NumberPicker) mTimePicker
				.findViewById(field.getInt(null));
			minuteSpinner.setMinValue(0);
			minuteSpinner.setMaxValue((60 / TIME_PICKER_INTERVAL) - 1);
			List<String> displayedValues = new ArrayList<>();
			for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL) {
				displayedValues.add(String.format("%02d", i));
			}
			minuteSpinner.setDisplayedValues(displayedValues
					.toArray(new String[displayedValues.size()]));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Here is the demonstrating screenshot.

enter image description here

Solution 2 - Android

There is this awesome library MaterialDateTimePicker that creates beautiful styled material timepickers.

You can use one of the following methods to define your interval:

> - setSelectableTimes(Timepoint[] times) You can pass in an array of Timepoints. These values are the only valid selections in the picker. > setMinTime(Timepoint time) and setMaxTime(Timepoint time) will further > trim this list down. > > - setTimeInterval(int hourInterval, int minuteInterval, int secondInterval) Set the interval for selectable times in the > TimePickerDialog. This is a convenience wrapper around > setSelectableTimes

Solution 3 - Android

/**
 * Set TimePicker interval by adding a custom minutes list
 *
 * @param timePicker
 */
private void setTimePickerInterval(TimePicker timePicker) {
    try {

        NumberPicker minutePicker = (NumberPicker) timePicker.findViewById(Resources.getSystem().getIdentifier(
                "minute", "id", "android"));
        minutePicker.setMinValue(0);
        minutePicker.setMaxValue((60 / TIME_PICKER_INTERVAL) - 1);
        List<String> displayedValues = new ArrayList<String>();
        for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL) {
            displayedValues.add(String.format("%02d", i));
        }
        minutePicker.setDisplayedValues(displayedValues.toArray(new String[0]));
    } catch (Exception e) {
        Log.e(TAG, "Exception: " + e);
    }
}

Solution 4 - Android

As @anddev84 said, you can do it with your own customized TimePickerDialog.

However, take a look of the source code you can find:

  1. TimePickerDialog is a frame layout, see this file. And it just contains a TimePicker

  2. The TimePicker widget is just a linear layout, see this file. And you can see the minute spinner is a NumberPicker

  3. Refer to the code of NumberPicker, you will find it has a public method setDisplayedValues(String[] displayedValues)

     /**
      * Sets the values to be displayed.
      *
      * @param displayedValues The displayed values.
      */
     public void setDisplayedValues(String[] displayedValues)
    

So, with these information, I suppose you can simply customize your own time picker dialog with limited minutes displayed.

Solution 5 - Android

Struggled with 30 min interval more then hour. Might struggle more if wouldn't see the comment of @Lee. So just post a full code of dialog with 30 min interval:

public class IntervalTimePickerDialog extends TimePickerDialog {
    private static final String TAG = "IntervalPickerDialog";

    private final static int TIME_PICKER_INTERVAL = 30;
    private TimePicker timePicker;
    private final OnTimeSetListener callback;

    private int lastHour = -1;
    private int lastMinute = -1;

    public IntervalTimePickerDialog(Context context, int themeId, OnTimeSetListener callBack,
                                    int hourOfDay, int minute, boolean is24HourView) {
        super(context, themeId, callBack, hourOfDay, minute / TIME_PICKER_INTERVAL,
                is24HourView);
        lastHour = hourOfDay;
        lastMinute = minute;
        this.callback = callBack;
    }

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

    @Override
    protected void onStop() {
    }

    @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("minute");

            NumberPicker mMinuteSpinner = (NumberPicker) timePicker.findViewById(field.getInt(null));
            mMinuteSpinner.setMinValue(0);
            mMinuteSpinner.setMaxValue((60 / TIME_PICKER_INTERVAL) - 1);
            List<String> displayedValues = new ArrayList<>();
            for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL) {
                displayedValues.add(String.format("%02d", i));
            }
            mMinuteSpinner.setDisplayedValues(displayedValues.toArray(new String[0]));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
        super.onTimeChanged(view, hourOfDay, minute);
        if (lastHour != hourOfDay && lastMinute != minute) {
            view.setCurrentHour(lastHour);
            lastMinute = minute;
        } else {
            lastHour = hourOfDay;
            lastMinute = minute;
        }
    }

Solution 6 - Android

Looking at the code for TimePickerDialog, it doesn't appear they give you a way to directly modify the TimePicker before it gets shown to the user and you see the "wrong minutes" are you show in the picture. See TimePickerDialog.java

My recommendation is to create your own TimePickerDialog, basing the design off the Android one. It might sound daunting at first, but

  1. that way you can customize the implementation however you want, including initializing the TimePicker values to display for the minutes (as mentioned here), and also
  2. it actually shouldn't be very hard, the Android TimePickerDialog.java itself is only 160 lines, very manageable.

Solution 7 - Android

I've made an addition to the excellent accepted answer above. The problem with it is that, in at least some versions of Android, when you change the selected time it will replace the dialog title with an incorrect time value. You can't override the offending method, updateTitle(), but you can intercept the call at onTimeChanged() and either not call super or call it with fixed arguments.

I keyed mine off of whether the setTitle() method had been called with a non-zero string ID, but you could just have a getter/setter for the boolean itself or whatever.

@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute)
{
    if (autoTitle)
    {
        // Super will call a private updateTitle() method, so lets make
        // sure it has the right minute value.
        super.onTimeChanged(view, hourOfDay, minute * TIME_PICKER_INTERVAL);
    }
    else
    {
        // do nothing
    }
}

@Override
public void setTitle(int id)
{
    super.setTitle(id);
    autoTitle = (id == 0);
}

...

private boolean autoTitle = true;

Solution 8 - Android

Solutions so far don't work with listeners and when setting time programmatically. This solutions is fully usable and stable. Also Kotlin...

import android.content.Context
import android.util.AttributeSet
import android.widget.NumberPicker
import android.widget.TimePicker
import androidx.databinding.BindingAdapter
import java.lang.reflect.Field
import java.util.*


class TimePickerInterval @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TimePicker(context, attrs, defStyleAttr) {

    private var MINUTE_INTERVAL = 1
    override fun getCurrentMinute(): Int {
        return super.getCurrentMinute() * MINUTE_INTERVAL
    }

    override fun getMinute(): Int {
        return super.getMinute() * MINUTE_INTERVAL
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        setUp()
    }

    private fun setUp() {
        try {
            val classForid = Class.forName("com.android.internal.R\$id")
            val field: Field = classForid.getField("minute")
            val mMinuteSpinner = findViewById<NumberPicker>(field.getInt(null))
            mMinuteSpinner.minValue = 0
            mMinuteSpinner.maxValue = 60 / MINUTE_INTERVAL - 1
            val displayedValues: MutableList<String> = ArrayList()
            var i = 0
            while (i < 60) {
                displayedValues.add(String.format("%02d", i))
                i += MINUTE_INTERVAL
            }
            mMinuteSpinner.displayedValues = displayedValues.toTypedArray()

        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun setOnTimeChangedListener(onTimeChangedListener: OnTimeChangedListener) {
        super.setOnTimeChangedListener { tp, hour, minute ->
            onTimeChangedListener.onTimeChanged(tp, hour, minute * MINUTE_INTERVAL)
        }
    }

    override fun setMinute(minute: Int) {
        super.setMinute(minute/MINUTE_INTERVAL)
    }

    companion object {
        @JvmStatic
        @BindingAdapter("time_picker_set_minute_interval")
        fun setInterval(view: TimePickerInterval, interval: Int?) {
            interval?.let {
                view.MINUTE_INTERVAL = interval
                view.setUp()
            }
        }
    }
}

In your XML:

        <your.path.TimePickerInterval
            android:id="@+id/tp_to"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:timePickerMode="spinner"
            time_picker_set_minute_interval="@{5}"
 />

Solution 9 - Android

The answer above works really fine for older version of Android, but it didn't work for me in Android 7 Nougat, basically the date picker is changed completely to use the Clock mode... I kind of merge the code proposed here with https://gist.github.com/jeffdgr8/6bc5f990bf0c13a7334ce385d482af9f, then it set by default the mode of the TimePickerDialog to show the Spinner controls, and it runs the code to show only 15minuts intervals in my case.

The code is posted here https://gist.github.com/smaugho/14dae83f3284fa05455ee0a9e4f13099

public class CustomTimePickerDialog extends TimePickerDialog {

private final static int TIME_PICKER_INTERVAL = 15;
private TimePicker timePicker;
private final OnTimeSetListener callback;

  public HorekoTimePicker(Context context,
		OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
	  super(context, callBack, hourOfDay, minute/TIME_PICKER_INTERVAL, is24HourView);
	  this.callback = callBack;
    fixSpinner(context, hourOfDay, minute, is24HourView);
  }

/**
 * Workaround for this bug: https://code.google.com/p/android/issues/detail?id=222208
 * In Android 7.0 Nougat, spinner mode for the TimePicker in TimePickerDialog is
 * incorrectly displayed as clock, even when the theme specifies otherwise, such as:
 *
 *  <resources>
 *      <style name="Theme.MyApp" parent="Theme.AppCompat.Light.NoActionBar">
 *          <item name="android:timePickerStyle">@style/Widget.MyApp.TimePicker</item>
 *      </style>
 *
 *      <style name="Widget.MyApp.TimePicker" parent="android:Widget.Material.TimePicker">
 *          <item name="android:timePickerMode">spinner</item>
 *      </style>
 *  </resources>
 *
 * May also pass TimePickerDialog.THEME_HOLO_LIGHT as an argument to the constructor,
 * as this theme has the TimePickerMode set to spinner.
 *
 * Taken from: https://gist.github.com/jeffdgr8/6bc5f990bf0c13a7334ce385d482af9f
 */
private void fixSpinner(Context context, int hourOfDay, int minute, boolean is24HourView) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // android:timePickerMode spinner and clock began in Lollipop
        try {

            // Get the theme's android:timePickerMode
            final int MODE_SPINNER = 1;
            Class<?> styleableClass = Class.forName("com.android.internal.R$styleable");
            Field timePickerStyleableField = styleableClass.getField("TimePicker");
            int[] timePickerStyleable = (int[]) timePickerStyleableField.get(null);
            final TypedArray a = context.obtainStyledAttributes(null, timePickerStyleable, android.R.attr.timePickerStyle, 0);
            Field timePickerModeStyleableField = styleableClass.getField("TimePicker_timePickerMode");
            int timePickerModeStyleable = timePickerModeStyleableField.getInt(null);
            final int mode = a.getInt(timePickerModeStyleable, MODE_SPINNER);
            a.recycle();

            if (mode == MODE_SPINNER) {

                timePicker = (TimePicker) findField(TimePickerDialog.class, TimePicker.class, "mTimePicker").get(this);
                Class<?> delegateClass = Class.forName("android.widget.TimePicker$TimePickerDelegate");
                Field delegateField = findField(TimePicker.class, delegateClass, "mDelegate");
                Object delegate = delegateField.get(timePicker);
                Class<?> spinnerDelegateClass;
                if (Build.VERSION.SDK_INT != Build.VERSION_CODES.LOLLIPOP) {
                    spinnerDelegateClass = Class.forName("android.widget.TimePickerSpinnerDelegate");
                } else {
                    // TimePickerSpinnerDelegate was initially misnamed TimePickerClockDelegate in API 21!
                    spinnerDelegateClass = Class.forName("android.widget.TimePickerClockDelegate");
                }

                // In 7.0 Nougat for some reason the timePickerMode is ignored and the delegate is TimePickerClockDelegate
                if (delegate.getClass() != spinnerDelegateClass) {
                    delegateField.set(timePicker, null); // throw out the TimePickerClockDelegate!
                    timePicker.removeAllViews(); // remove the TimePickerClockDelegate views
                    Constructor spinnerDelegateConstructor = spinnerDelegateClass.getConstructor(TimePicker.class, Context.class, AttributeSet.class, int.class, int.class);
                    spinnerDelegateConstructor.setAccessible(true);
                    // Instantiate a TimePickerSpinnerDelegate
                    delegate = spinnerDelegateConstructor.newInstance(timePicker, context, null, android.R.attr.timePickerStyle, 0);
                    delegateField.set(timePicker, delegate); // set the TimePicker.mDelegate to the spinner delegate
                    // Set up the TimePicker again, with the TimePickerSpinnerDelegate
                    timePicker.setIs24HourView(is24HourView);
                    timePicker.setCurrentHour(hourOfDay);
                    timePicker.setCurrentMinute(minute);
                    timePicker.setOnTimeChangedListener(this);
                }

                setTimeIntervals();
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

private static Field findField(Class objectClass, Class fieldClass, String expectedName) {
    try {
        Field field = objectClass.getDeclaredField(expectedName);
        field.setAccessible(true);
        return field;
    } catch (NoSuchFieldException e) {} // ignore
    // search for it if it wasn't found under the expected ivar name
    for (Field searchField : objectClass.getDeclaredFields()) {
        if (searchField.getType() == fieldClass) {
            searchField.setAccessible(true);
            return searchField;
        }
    }
    return null;
}

  @Override
protected void onStop() { }

/*
 * Feature #363: (Un)availability times in 15min interval
 * https://abix.webhop.net/redmine/issues/363
 * Solution extracted from
 * http://stackoverflow.com/questions/20214547/show-timepicker-with-minutes-intervals-in-android
 */

@Override
public void onClick(DialogInterface dialog, int which) {
	super.onClick(dialog, which);

    if (callback != null && timePicker != null) {
        timePicker.clearFocus();
        callback.onTimeSet(timePicker, timePicker.getCurrentHour(),
            timePicker.getCurrentMinute()*TIME_PICKER_INTERVAL);
    }
}

private void setTimeIntervals() {
    try {
        Class<?> classForid = Class.forName("com.android.internal.R$id");
        Field field = classForid.getField("minute");

        NumberPicker mMinuteSpinner = (NumberPicker) timePicker.findViewById(field.getInt(null));
        mMinuteSpinner.setMinValue(0);
        mMinuteSpinner.setMaxValue((60 / TIME_PICKER_INTERVAL) - 1);
        List<String> displayedValues = new ArrayList<String>();
        for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL) {
            displayedValues.add(String.format("%02d", i));
        }
        mMinuteSpinner.setDisplayedValues(displayedValues.toArray(new String[0]));

    } catch (Exception e) {
        e.printStackTrace();
    }

}

}

Solution 10 - Android

If you wont to use the same time picker (not clock) dialog in android 5+ just change the theme. You can change directly in the constructor:

 public CustomTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView, int time_interval) {
        super(context, TimePickerDialog.THEME_HOLO_LIGHT, callBack, hourOfDay, minute / time_interval, is24HourView);
        this.TIME_PICKER_INTERVAL = time_interval;
        this.callback = callBack;
    }

Solution 11 - Android

Based on Tanmay Mandal's answer shown above, I have implemented a solution for the TimePicker Widget (not the TimePickerDialog). The trick here is to substitute the original TimePicker element in the layout of the activity for a LinearLayout element, in order to add our custom TimePicker programmatically. See example below:

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

import android.content.Context;
import android.widget.NumberPicker;
import android.widget.TimePicker;

public class MyTimePicker extends TimePicker {

  private static final int TIME_PICKER_INTERVAL = 5;

	public MyTimePicker(Context context) {
		super(context);
	}

	@Override
	public Integer getCurrentMinute() {
		return super.getCurrentMinute()*TIME_PICKER_INTERVAL;
	}
	
	@Override
	protected void onAttachedToWindow() {
		super.onAttachedToWindow();
		
        try {
            Class<?> classForid = Class.forName("com.android.internal.R$id");
            Field field = classForid.getField("minute");

            NumberPicker mMinuteSpinner = (NumberPicker) findViewById(field.getInt(null));
            mMinuteSpinner.setMinValue(0);
            mMinuteSpinner.setMaxValue((60 / TIME_PICKER_INTERVAL) - 1);
            List<String> displayedValues = new ArrayList<String>();
            for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL)
                displayedValues.add(String.format("%02d", i));
             mMinuteSpinner.setDisplayedValues(displayedValues.toArray(new String[0]));		
        } catch (Exception e) {
		e.printStackTrace();
	}
        
   }
	
}

And for our activity:

	      @Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

    		LinearLayout ll = (LinearLayout) findViewById(R.id.myLinearLayout);
    		ll.removeAllViews();
    		MyTimePicker tp = new MyTimePicker(this);
    		tp.setIs24HourView(true);
     		Calendar cal = Calendar.getInstance(); // Current time
   		tp.setCurrentHour(cal.get(Calendar.HOUR_OF_DAY));
    		tp.setCurrentMinute(cal.get(Calendar.MINUTE)/5); // Aprox current minute
    		ll.addView(tp);

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSergio76View Question on Stackoverflow
Solution 1 - AndroidTanmay MandalView Answer on Stackoverflow
Solution 2 - AndroidLukas LechnerView Answer on Stackoverflow
Solution 3 - AndroidTharik ahamedView Answer on Stackoverflow
Solution 4 - AndroidRobinView Answer on Stackoverflow
Solution 5 - AndroidYev KanivetsView Answer on Stackoverflow
Solution 6 - Androidanddev84View Answer on Stackoverflow
Solution 7 - AndroidbenkcView Answer on Stackoverflow
Solution 8 - AndroidDarko MartinovićView Answer on Stackoverflow
Solution 9 - AndroidAdrián RiveroView Answer on Stackoverflow
Solution 10 - AndroidgellezzzView Answer on Stackoverflow
Solution 11 - AndroidMJimView Answer on Stackoverflow