How to set a custom minutes interval in TimePickerDialog in Android

Joserra picture Joserra · Oct 10, 2011 · Viewed 17.7k times · Source

I have got a TimePickerDialog working to set time which is set to a TextView in order to display it. Now, I need help to set that TimePicker (inside the TimePickerDialog) minutues interval to 15 minutes. I have seen there is a post with 15 minutes interval issue related to TimePicker, but I don't know how to apply it to the TimePickerDialog because I don't know how to use the TimePicker that it is created inside the TimePickerDialog. I am new to Android and completely lost in this matter. Thanks in advance.

Answer

David O'Meara picture David O'Meara · Nov 14, 2013

Using a combination of this from @Rizwan and this other thread, I came up with a combined solution that allows arbitrary minute increments in a TimePickerDialog. The main issue is that most of the functionality is hidden by the android TimePickerDialog and TimePicker classes and it doesn't appear to be

  1. Extend TimePickerDialog to allow us easier access
  2. Use reflection to reach inside the display and access the required bits (see below)
  3. rewire the minute 'NumberPicker' to display our values
  4. rewire the TimePicker to receive and return values form the NumberPicker honoring our custom increment.
  5. block onStop() so that it doesn't reset the value on close.

Warning

The main issue with reaching inside the UI is that elements are referenced by ids which are likely to change, and even the name of the id is not guaranteed to be the same forever. Having said that, this is working, stable solution and likely to work for the foreseeable future. In my opinion the empty catch block should warn that the UI has changed and should fall back to the default (increment 1 minute) behaviour.

Solution

    private class DurationTimePickDialog extends TimePickerDialog
    {
        final OnTimeSetListener mCallback;
        TimePicker mTimePicker;
        final int increment;

        public DurationTimePickDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView, int increment)
        {
            super(context, callBack, hourOfDay, minute/increment, is24HourView);
            this.mCallback = callBack;
            this.increment = increment;
        }

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

        @Override
        protected void onStop()
        {
            // override and do nothing
        }

        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            try
            {
                Class<?> rClass = Class.forName("com.android.internal.R$id");
                Field timePicker = rClass.getField("timePicker");
                this.mTimePicker = (TimePicker)findViewById(timePicker.getInt(null));
                Field m = rClass.getField("minute");

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

}

constructor accepts the increment value and retains some other references. Note that this omits error checking and we'd prefer 60%increment==0

onCreate uses the name of the UI fields and reflection to discover the current location. Again this omits error checking and should be 'fail-safe' ie revert to default behaviour if something goes wrong.

onClick overridden to return the correct minute value to the callback listener

onStop overridden to prevent the (incorrect) index value being returned a second time, when the dialog closes. Go on, try it yourself.

Most of this comes from digging into the TimePickerDialog source.