Android TimePickerDialog set max time
Asked Answered
P

9

28

I'm looking for a way to set the maximun and minimun time pickable on an Android TimePickerDialog, and to change the default minute interval from 1 min to 5 min,

I thought that was an easy one, but I can't find a way !

Passim answered 22/11, 2012 at 16:2 Comment(3)
You should accept Zarokka's answer! It's working fine for me!Herpetology
There is some new answers, I'm not working on Android anymore, so I won't be able to test it, but if the new answer is better, up-vote it, and I will mark it as the new "correct" answerPassim
I prefered this approach #34253879Norland
B
39

THIS ANSWER IS OUTDATED

It will not work on Android 5 or higher.


This is an extension to fiddlers answer. Somehow the onTimeChangedListener is not set by TimePickerDialog in some Android Versions:

ex. http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.2.1_r1.2/android/app/TimePickerDialog.java

The onTimeChanged methode therefor is never called! Best solution for this issue currently, is propably to code a custom TimePickerDialog with the min/max logic from fiddler.

The more ugly work around is this. (Adding the listener using reflections) I also update the title to show the currently selected time.

package com.mysuger.android.dialogs;

import java.lang.reflect.Field;
import java.text.DateFormat;
import java.util.Calendar;

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

/**
 * A time dialog that allows setting a min and max time.
 * 
 */
public class RangeTimePickerDialog extends TimePickerDialog {

private int minHour = -1;
private int minMinute = -1;

private int maxHour = 25;
private int maxMinute = 25;

private int currentHour = 0;
private int currentMinute = 0;

private Calendar calendar = Calendar.getInstance();
private DateFormat dateFormat;


public RangeTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
    super(context, callBack, hourOfDay, minute, is24HourView);
    currentHour = hourOfDay;
    currentMinute = minute;
    dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);

    try {
        Class<?> superclass = getClass().getSuperclass();
        Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
        mTimePickerField.setAccessible(true);
        TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
        mTimePicker.setOnTimeChangedListener(this);
    } catch (NoSuchFieldException e) {
    } catch (IllegalArgumentException e) {
    } catch (IllegalAccessException e) {
    }
}

public void setMin(int hour, int minute) {
    minHour = hour;
    minMinute = minute;
}

public void setMax(int hour, int minute) {
    maxHour = hour;
    maxMinute = minute;
}

@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {

    boolean validTime = true;
    if (hourOfDay < minHour || (hourOfDay == minHour && minute < minMinute)){
        validTime = false;
    }

    if (hourOfDay  > maxHour || (hourOfDay == maxHour && minute > maxMinute)){
        validTime = false;
    }

    if (validTime) {
        currentHour = hourOfDay;
        currentMinute = minute;
    }

    updateTime(currentHour, currentMinute);
    updateDialogTitle(view, currentHour, currentMinute);
}

private void updateDialogTitle(TimePicker timePicker, int hourOfDay, int minute) {
    calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
    calendar.set(Calendar.MINUTE, minute);
    String title = dateFormat.format(calendar.getTime());
    setTitle(title);
}
 }
Bonita answered 5/6, 2013 at 14:29 Comment(5)
Scratching my head how can implement this to Dialog fragment!Herpetology
@Herpetology just replace the RangeTimePickerDialog as the return type in public Dialog onCreateDialog(final Bundle savedInstanceState) following the example in the Android docsConclude
This answer is from 2013. Does this really work ? As it didn't work for me on 8.1Strom
@Killer No, I marked it as outdated directly in the answer.Bonita
Nice solution. I can see one problem that if the date is in future then It should not block the user to select past time . To solve this I have added my logic if ((minCalendar nextDay Calendar.getInstance())) { validTime = true }Haemostat
K
14

For lolipop and higher version of android you can use this modified RangeTimePickerDialog class

(From lolipop, Timepicker uses clock mode (material design) as default so that the old customclass will not work.we can change the mode to spinner for latest version and can reuse the class)

public class RangeTimePickerDialog extends TimePickerDialog {

    private int minHour = -1;
    private int minMinute = -1;

    private int maxHour = 25;
    private int maxMinute = 25;

    private int currentHour = 0;
    private int currentMinute = 0;

    private Calendar calendar = Calendar.getInstance();
    private DateFormat dateFormat;


    public RangeTimePickerDialog(Context context, int dialogTheme, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
        super(context, callBack, hourOfDay, minute, is24HourView);
        currentHour = hourOfDay;
        currentMinute = minute;
        dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);
        fixSpinner(context, hourOfDay, minute, is24HourView);

        try {
            Class<?> superclass = getClass().getSuperclass();
            Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
            mTimePickerField.setAccessible(true);
            TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
            mTimePicker.setOnTimeChangedListener(this);
        } catch (NoSuchFieldException e) {
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        }
    }

    public void setMin(int hour, int minute) {
        minHour = hour;
        minMinute = minute;
    }

    public void setMax(int hour, int minute) {
        maxHour = hour;
        maxMinute = minute;
    }

    @Override
    public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {

        boolean validTime = true;
        if (hourOfDay < minHour || (hourOfDay == minHour && minute < minMinute)){
            validTime = false;
        }

        if (hourOfDay  > maxHour || (hourOfDay == maxHour && minute > maxMinute)){
            validTime = false;
        }

        if (validTime) {
            currentHour = hourOfDay;
            currentMinute = minute;
        }

        updateTime(currentHour, currentMinute);
        updateDialogTitle(view, currentHour, currentMinute);
    }

    private void updateDialogTitle(TimePicker timePicker, int hourOfDay, int minute) {
        calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
        calendar.set(Calendar.MINUTE, minute);
        String title = dateFormat.format(calendar.getTime());
        setTitle(title);
    }


    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
                //two modes are available clock mode and spinner mode ... selecting spinner mode for latest versions
                final int MODE_SPINNER = 2;
                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 = (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 {

                        spinnerDelegateClass = Class.forName("android.widget.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);
                    }
                }
            } 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;
    }
}
Kirimia answered 19/2, 2018 at 7:33 Comment(4)
Great answer!! Btw. how can we add title in this dialog ? setTitle() method seems to be not working.Denverdeny
how to set dialogTheme?Gelasias
app crashed at E/UncaughtException: java.lang.RuntimeException: java.lang.NoSuchFieldException: TimePicker at here -> styleableClass.getField("TimePicker")Indictable
@apj123 or anyone please share if you've found an answer to this crash or other answer.Inapposite
E
10

You can use this as a starting point.

I extended TimePickerDialog and added 2 methods setMin and setMax.

In the onTimeChanged method I check that the new time is valid with respect to the min/max times.

It still needs some polishing though...

public class BoundTimePickerDialog extends TimePickerDialog {

    private int minHour = -1, minMinute = -1, maxHour = 100, maxMinute = 100;

    private int currentHour, currentMinute;

    public BoundTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
        super(context, callBack, hourOfDay, minute, is24HourView);
    }

    public void setMin(int hour, int minute) {
        minHour = hour;
        minMinute = minute;
    }

    public void setMax(int hour, int minute) {
        maxHour = hour;
        maxMinute = minute;
    }

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

        boolean validTime;
        if(hourOfDay < minHour) {
            validTime = false;
        }
        else if(hourOfDay == minHour) {
            validTime = minute >= minMinute;
        }
        else if(hourOfDay == maxHour) {
            validTime = minute <= maxMinute;
        }
        else {
            validTime = true;
        }

        if(validTime) {
            currentHour = hourOfDay;
            currentMinute = minute;
        }
        else {
            updateTime(currentHour, currentMinute);
        }
    }
}
Eanore answered 22/11, 2012 at 16:15 Comment(3)
How to change the above implementation for public class TimePickerFragment extends DialogFragmentHerpetology
In BoundTimePickerDialog() constructor, you should initialize : currentHour and currentMinute. Then it works perfrctly. Without this, there is one minor issue, set minHour > choose hour minimun than minHour. All the time 12 : 00 AM get set, because currentHour by default set to zero. Nice code. It help me :)Silvas
How exactly it's supposed to fix the issue?Schedule
S
4

After Re-factored code of fiddler :

public class RangeTimePickerDialog extends TimePickerDialog {

    private int mMinHour = -1;
    private int mMinMinute = -1;
    private int mMaxHour = 100;
    private int mMaxMinute = 100;
    private int mCurrentHour;
    private int mCurrentMinute;

    public RangeTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, 
        int minute, boolean is24HourView) {
        super(context, callBack, hourOfDay, minute, is24HourView);
        mCurrentHour = hourOfDay;
        mCurrentMinute = minute;
        // Somehow the onTimeChangedListener is not set by TimePickerDialog
        // in some Android Versions, so, Adding the listener using
        // reflections
        try {
            Class<?> superclass = getClass().getSuperclass();
            Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
            mTimePickerField.setAccessible(true);
            TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
            mTimePicker.setOnTimeChangedListener(this);
        } catch (NoSuchFieldException e) {
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        }
    }

    public void setMin(int hour, int minute) {
        mMinHour = hour;
        mMinMinute = minute;
    }

    public void setMax(int hour, int minute) {
        mMaxHour = hour;
        mMaxMinute = minute;
    }

    @Override
    public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
        super.onTimeChanged(view, hourOfDay, minute);
        boolean validTime;
        if (((hourOfDay < mMinHour ) || (hourOfDay == mMinHour && minute < mMinMinute)) 
                || ((hourOfDay > mMaxHour) || (hourOfDay == mMaxHour && minute > mMaxMinute))) {
            validTime = false;
        } else {
            validTime = true;
        }
        if (validTime) {
            mCurrentHour = hourOfDay;
            mCurrentMinute = minute;
        } else {
            updateTime(mCurrentHour, mCurrentMinute);
        }
    }
}
Silvas answered 13/10, 2014 at 15:9 Comment(0)
A
2

There are no native methods to set the max/min time, but perhaps it works if you extend onTimeChanged or updateTime you can prevent the time from being set to a value outside of your bounds.

Alert answered 22/11, 2012 at 16:12 Comment(0)
O
2

You can just use a custom TimePicker with reflect to set the values.

 @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(5);
        mMinuteSpinner.setMaxValue((60 / timeInterval) - 1);

        List<String> displayedValues = new ArrayList<>();
        for (int i = 0; i < 60; i += timeInterval)
            displayedValues.add(String.format("%02d", i));
        mMinuteSpinner.setDisplayedValues(displayedValues.toArray(new String[0]));

        mMinuteSpinner.setWrapSelectorWheel(true);

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

}

So you can set what values you want

Oakland answered 18/5, 2016 at 3:2 Comment(1)
How you set max hours?Scorch
P
0

Hope this could helpful. I used Joda time library.

public class RangeTimePicker extends TimePicker implements
        TimePicker.OnTimeChangedListener {

    private int mMinHour = -1;

    private int mMinMinute = -1;

    private int mMaxHour = 25;

    private int mMaxMinute = 61;

    private int mCurrentHour;

    private int mCurrentMinute;


    public RangeTimePicker(Context context) {
        super(context);
        setOnTimeChangedListener(this);
    }

    public RangeTimePicker(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOnTimeChangedListener(this);
    }

    public RangeTimePicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setOnTimeChangedListener(this);
    }

    public void setMaxTime(int hourIn24, int minute) {
        mMaxHour = hourIn24;
        mMaxMinute = minute;

        LocalTime currentTime = LocalTime.now();
        LocalTime maxTime = new LocalTime(mMaxHour, mMaxMinute);

        if (currentTime.isAfter(maxTime)) {
            this.setCurrentHour(mCurrentHour = currentTime.getHourOfDay());
            this.setCurrentMinute(
                    mCurrentMinute = currentTime.getMinuteOfHour());
        }

    }

    public void setMinTime(int hourIn24, int minute) {
        mMinHour = hourIn24;
        mMinMinute = minute;

        LocalTime currentTime = LocalTime.now();
        LocalTime minTime = new LocalTime(mMinHour, mMinMinute);

        if (currentTime.isBefore(minTime)) {
            this.setCurrentHour(mCurrentHour = minTime.getHourOfDay());
            this.setCurrentMinute(mCurrentMinute = minTime.getMinuteOfHour());
        }
    }


    @Override
    public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
        boolean validTime = true;
        if (hourOfDay < mMinHour || (hourOfDay == mMinHour
                && minute < mMinMinute)) {
            validTime = false;
        }

        if (hourOfDay > mMaxHour || (hourOfDay == mMaxHour
                && minute > mMaxMinute)) {
            validTime = false;
        }

        if (validTime) {
            mCurrentHour = hourOfDay;
            mCurrentMinute = minute;
        }

        setCurrentHour(mCurrentHour);
        setCurrentMinute(mCurrentMinute);

    }
}
Placidia answered 21/5, 2015 at 7:53 Comment(1)
how do you set if its 24h mode and the minutes or hours?Scorch
R
0

Hope this work, It works perfectly with me...

Set maximum time for time picker.

        timePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
        @Override
        public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {

            if (!mIsOnTimeChanged) {
                mIsOnTimeChanged = true;

                Calendar c = Calendar.getInstance();

                //get the selected day if it is before today allow AM and PM trick
                //else apply rules


                int selected_year = datePicker.getYear();
                int selected_month = datePicker.getMonth();
                int selected_day = datePicker.getDayOfMonth();


                int today = c.get(Calendar.DAY_OF_MONTH);
                int year = c.get(Calendar.YEAR);
                int month = c.get(Calendar.MONTH);


                int hour = c.get(Calendar.HOUR);
                hour += 12;
                int min = c.get(Calendar.MINUTE);

                //Check if user enter hh | mm | more than current reset the timePicker with the current time

                //https://mcmap.net/q/182507/-timepickerdialog-and-am-or-pm

                if ((hourOfDay == hour | hour < hourOfDay | hourOfDay > hour | hour > hourOfDay | minute > min) && (selected_month == month && selected_day == today && selected_year == year)) {

                    int AM_PM_Value = hourOfDay;

                    Calendar datetime = Calendar.getInstance();

                    int hour_without_24 = (hour - 12);
                    if ((datetime.get(Calendar.AM_PM) == Calendar.AM) && (hourOfDay > hour_without_24)) {
                        //set AM
                        timePicker.setCurrentHour(hour - 12);
                    }

                    if (hourOfDay == (hour - 12) && minute > min) {
                        timePicker.setCurrentMinute(min);

                    }

                    if (hourOfDay > hour && datetime.get(Calendar.AM_PM) != Calendar.AM) {
                        timePicker.setCurrentHour(hour);

                        if (minute > min) {
                            timePicker.setCurrentMinute(min);

                        }
                    }


                } else if (selected_month != month | selected_day != today | selected_year != year) {

                } else if (hourOfDay == hour && minute > min) {
                    timePicker.setCurrentMinute(min);
                } else if (hourOfDay > hour) {
                    timePicker.setCurrentHour(hour);

                    if (minute > min) {
                        timePicker.setCurrentMinute(min);

                    }
                }
                //
                isTimePickerChanged = true;
                String AM_PM;
                if (hourOfDay < 12) {
                    AM_PM = "AM";
                } else {
                    AM_PM = "PM";
                }

                //to set as 12 hour
                time_format = (hourOfDay + ":" + minute + ":" + "00");

                if (hourOfDay > 12) {
                    hourOfDay = (hourOfDay - 12);
                }

                st_time = (hourOfDay + ":" + pad(minute) + " " + AM_PM);

                mIsOnTimeChanged = false;

            }


        }
    });
Rotogravure answered 19/2, 2016 at 23:31 Comment(0)
H
-1

I took @Zarooka 's solution as a starting point. I think I found a bug though. If you had a starting time which was already out-of-range, then you could not move it anymore, because any "onChanged" was being rejected. That's why I extended the setMax and setMin in order to go to a valid range when setting the mins/maxs.

package de.appdream.garooda.view;

import java.util.Calendar;
import java.util.Date;

import android.app.TimePickerDialog;
import android.content.Context;
import android.text.format.DateFormat;
import android.widget.TimePicker;

import java.lang.reflect.Field; 


public class GaroodaTimePickerDialog extends TimePickerDialog {

private int minHour = -1;
private int minMinute = -1;

private int maxHour = 25;
private int maxMinute = 61;

private int currentHour = 0;
private int currentMinute = 0;

    private Calendar calendar = Calendar.getInstance();
    //private DateFormat dateFormat;

public GaroodaTimePickerDialog(Context context, OnTimeSetListener callBack, int hour, int minute,
        boolean is24HourView) {
    super(context, callBack, hour, minute, is24HourView);

    currentHour = hour;
    currentMinute = minute;
    //dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);

    try {
        Class<?> superclass = getClass().getSuperclass();
        Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
        mTimePickerField.setAccessible(true);
        TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
        mTimePicker.setOnTimeChangedListener(this);
    } catch (NoSuchFieldException e) {
    } catch (IllegalArgumentException e) {
    } catch (IllegalAccessException e) {
    }
}







public void setMin(int hour, int minute) {
    minHour = hour;
    minMinute = minute;

    Calendar min = Calendar.getInstance();
    Calendar existing = Calendar.getInstance();

    min.set(Calendar.HOUR_OF_DAY, minHour);
    min.set(Calendar.MINUTE, minMinute);

    existing.set(Calendar.HOUR_OF_DAY, currentHour);
    existing.set(Calendar.MINUTE, currentMinute);

    if (existing.before(min)) {
        currentHour = minHour;
        currentMinute = minMinute;
        updateTime(currentHour, currentMinute);
    }
}

public void setMax(int hour, int minute) {
    maxHour = hour;
    maxMinute = minute;

    Calendar max = Calendar.getInstance();
    Calendar existing = Calendar.getInstance();

    max.set(Calendar.HOUR_OF_DAY, maxHour);
    max.set(Calendar.MINUTE, maxMinute);

    existing.set(Calendar.HOUR_OF_DAY, currentHour);
    existing.set(Calendar.MINUTE, currentMinute);

    if (existing.after(max)) {
        currentHour = maxHour;
        currentMinute = maxMinute;
        updateTime(currentHour, currentMinute);
    }

}

@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {

    boolean validTime = true;
    if (hourOfDay < minHour || (hourOfDay == minHour && minute < minMinute)){
        validTime = false;
    }

    if (hourOfDay  > maxHour || (hourOfDay == maxHour && minute > maxMinute)){
        validTime = false;
    }

    if (validTime) {
        currentHour = hourOfDay;
        currentMinute = minute;
    }

    updateTime(currentHour, currentMinute);
    updateDialogTitle(view, currentHour, currentMinute);
}

private void updateDialogTitle(TimePicker timePicker, int hourOfDay, int minute) {
    calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
    calendar.set(Calendar.MINUTE, minute);
    //String title = dateFormat.format(calendar.getTime());
    //setTitle(title);
}
}
Hardworking answered 17/6, 2014 at 9:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.