Search code examples
androidandroid-timepickerandroid-7.0-nougat

Android N,L and below timepicker with minute interval


I was using a Timepicker with a minute interval from this answer. I ran this on an Andorid N preview 5 emulator and apparently the THEME_HOLO_LIGHT Timepicker has been removed.

How can I have a Timepicker with minute intervals? It does not seem very likely to me that it will be back in the final version of Android N.


Solution

  • Indeed creating my own timepicker was the solution. I'm posting my solution below for others. This creates a timepicker dialog with minute intervals for api 14 and above. It is working for intervals of 1,5 and 15. Other values may also work.

    Usage:

    Dialog intervalTimePickerDialog =  IntervalTimePickerDialogFactory.getIntervalTimePickerDialog(Context context, int positiveTextId, TimePickerDialog.OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView, final int interval);
    intervalTimePickerDialog .show();
    

    I think it won't need much explanation. int positiveTextId can be removed, it was just something I needed.

    timepickerdialog.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TimePicker
            android:timePickerMode="spinner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/timePicker"
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true" />
    
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/timePicker"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true">
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Cancel"
                style="?android:attr/borderlessButtonStyle"
                android:id="@+id/buttonNegative" />
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Inklokken"
                style="?android:attr/borderlessButtonStyle"
                android:id="@+id/buttonPositive" />
        </LinearLayout>
    </RelativeLayout>
    

    Code:

    public class IntervalTimePickerDialogFactory {
        public static Dialog getIntervalTimePickerDialog(Context context, int positiveTextId, TimePickerDialog.OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView, final int interval){
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
                return new LIntervalTimePickerDialog(context, positiveTextId, callBack, hourOfDay, minute, is24HourView, interval);
            }else{
                return new IntervalTimePickerDialog(context, positiveTextId, callBack, hourOfDay, minute, is24HourView, interval);
            }
        }
    
        private static class IntervalTimePickerDialog extends TimePickerDialog {
    
            private TimePicker timePicker;
            private final OnTimeSetListener callback;
            private int interval;
    
            public IntervalTimePickerDialog(Context context, int positiveTextId, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView, final int interval) {
    
                super(context, TimePickerDialog.THEME_HOLO_LIGHT, callBack, hourOfDay, ((int)Math.ceil((double)minute/(double)interval)), is24HourView);
                this.interval = interval;
    
                this.callback = callBack;
    
                this.setButton(BUTTON_POSITIVE, context.getResources().getString(positiveTextId), new OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (callback != null && timePicker != null) {
                            timePicker.clearFocus();
                            callback.onTimeSet(timePicker, timePicker.getCurrentHour(),
                                    timePicker.getCurrentMinute() * interval);
                        }
                    }
                });
                this.setButton(BUTTON_NEGATIVE, context.getResources().getString(R.string.annuleren), new OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dismiss();
                    }
                });
            }
    
    
            @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 / interval) - 1);
                    List<String> displayedValues = new ArrayList<>();
                    for (int i = 0; i < 60; i += interval) {
                        displayedValues.add(String.format("%02d", i));
                    }
                    mMinuteSpinner.setDisplayedValues(displayedValues
                            .toArray(new String[0]));
                } catch (Exception e) {
                    e.printStackTrace();
                }
    
            }
        }
    
        private static class LIntervalTimePickerDialog extends Dialog{
            private static final DecimalFormat FORMATTER = new DecimalFormat("00");
            private final TimePickerDialog.OnTimeSetListener callback;
            private int interval;
            private TimePicker timePicker;
            private NumberPicker minutePicker;
    
            public LIntervalTimePickerDialog(Context context, int positiveTextId, TimePickerDialog.OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView, final int interval) {
                super(context);
                setContentView(R.layout.timepickerdialog);
                timePicker = (TimePicker) findViewById(R.id.timePicker);
                timePicker.setIs24HourView(is24HourView);
                timePicker.setCurrentHour(hourOfDay);
    
                this.callback = callBack;
                this.interval = interval;
    
                Button buttonPositive = (Button) findViewById(R.id.buttonPositive);
                buttonPositive.setText(positiveTextId);
                buttonPositive.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        if (callback != null && timePicker != null) {
                            timePicker.clearFocus();
                            callback.onTimeSet(timePicker, timePicker.getCurrentHour(),
                                    timePicker.getCurrentMinute() * interval);
                        }
                        dismiss();
                    }
                });
    
                Button buttonNegative = (Button) findViewById(R.id.buttonNegative);
                buttonNegative.setText(context.getResources().getString(R.string.annuleren));
                buttonNegative.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        dismiss();
                    }
                });
    
                setMinutePicker();
                timePicker.setCurrentMinute((int) Math.ceil((double) minute / (double) interval));
            }
    
            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 = timePicker.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);
                }
            }
        }
    }