在android中以分钟间隔显示时间选择器

在android中以分钟间隔显示时间选择器,android,intervals,android-timepicker,Android,Intervals,Android Timepicker,我的应用程序显示一个TimePickerDialog来设置时间。 我希望timePickerDialog以5分钟的间隔显示分钟数 这适用于以下代码: private final int TIME_PICKER_INTERVAL=5; private boolean mIgnoreEvent=false; … public TimePickerDialogs(Context arg0, OnTimeSetListener callBack, int hourOfDay, int minut

我的应用程序显示一个TimePickerDialog来设置时间。 我希望timePickerDialog以5分钟的间隔显示分钟数

这适用于以下代码:

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;
    }
}
虽然间隔为五分钟时只能选择分钟,但timepickerdialog如下所示:

不知道分钟数如何显示5分钟的范围,如下图所示:


我已经搜索了,但找不到解决方案。

查看
TimePickerDialog
的代码,它似乎没有给您提供一种直接修改
TimePicker
的方法,然后将其显示给用户,您会看到图片中显示的“错误分钟数”。看

我的建议是创建自己的TimePickerDialog,基于Android的设计。一开始听起来可能让人望而生畏,但是

  • 通过这种方式,您可以根据需要自定义实现,包括初始化
    时间选择器
    值以显示分钟数(如上所述),以及
  • 实际上这并不难,Android TimePickerDialog.java本身只有160行,非常容易管理

  • 正如@anddev84所说,您可以使用自己定制的TimePickerDialog来完成

    但是,请查看您可以找到的源代码:

  • TimePickerDialog是一种帧布局,请参见此。它只包含一个时间选择器
  • TimePicker小部件只是一个线性布局,请参见。您可以看到分钟微调器是一个数字微调器
  • 参考的代码,您会发现它有一个公共方法setDisplayedValues(String[]displayedValues)


  • 因此,有了这些信息,我想您可以简单地自定义自己的时间选择器对话框,并显示有限的分钟。

    使用以下名为
    CustomTimePickerDialog的自定义类,我认为这可以解决您的问题

    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();
            }
        }
    }
    
    import java.lang.reflect.Field;
    导入java.util.ArrayList;
    导入java.util.List;
    导入android.app.TimePickerDialog;
    导入android.content.Context;
    导入android.content.DialogInterface;
    导入android.widget.NumberPicker;
    导入android.widget.TimePicker;
    公共类CustomTimePickerDialog扩展了TimePickerDialog{
    专用最终静态整数时间选择器间隔=5;
    私人时间选择器;
    私有最终OnTimeSetListener MTTimeSetListener;
    公共CustomTimePickerDialog(上下文上下文、OnTimeSetListener侦听器、,
    int hourOfDay,int minute,布尔值为24小时视图){
    super(上下文,TimePickerDialog.THEME\u HOLO\u LIGHT,null,hourOfDay,
    分钟/时间\u选择器\u间隔,IS24小时视图);
    mTimeSetListener=监听器;
    }
    @凌驾
    public void updateTime(inthourofday,intminuteofhour){
    mTimePicker.setCurrentHour(hourOfDay);
    mTimePicker.setCurrentMinute(小时分钟/时间\u选择器\u间隔);
    }
    @凌驾
    public void onClick(DialogInterface dialog,int which){
    开关(哪个){
    外壳按钮_正极:
    如果(mTimeSetListener!=null){
    mTimeSetListener.onTimeSet(mTimePicker,mTimePicker.getCurrentHour(),
    mTimePicker.getCurrentMinute()*时间\u选取器\u间隔);
    }
    打破
    外壳按钮\u负片:
    取消();
    打破
    }
    }
    @凌驾
    公共空间和附加的Towindow(){
    super.onAttachedToWindow();
    试一试{
    Class classForid=Class.forName(“com.android.internal.R$id”);
    Field timePickerField=classForid.getField(“timePicker”);
    mTimePicker=(TimePicker)findviewbyd(timePickerField.getInt(null));
    Field=classForid.getField(“分钟”);
    NumberPicker分钟数Pinner=(NumberPicker)mTimePicker
    .findviewbyd(field.getInt(null));
    minuteSpinner.setMinValue(0);
    minuteSpinner.setMaxValue((60次/选择器间隔)-1);
    List displayedValues=new ArrayList();
    对于(int i=0;i<60;i+=时间\u选择器\u间隔){
    displayedValues.add(String.format(“%02d”,i));
    }
    minuteSpinner.setDisplayedValues(displayedValues
    .toArray(新字符串[displayedValues.size()]);
    }捕获(例外e){
    e、 printStackTrace();
    }
    }
    }
    
    这是演示屏幕截图


    基于上述Tanmay Mandal的回答,我为TimePicker小部件(而不是TimePickerDialog)实现了一个解决方案。这里的技巧是用活动布局中的原始TimePicker元素替换LinearLayout元素,以便以编程方式添加自定义TimePicker。见下例:

    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();
        }
    
       }
    
    }
    

    如果你不习惯在android 5+中使用相同的时间选择器(而不是时钟)对话框,只需更改主题即可。您可以直接在构造函数中更改:

     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;
        }
    

    我对上面的优秀答案做了补充。它的问题是,至少在某些版本的Android中,当您更改所选时间时,它会用错误的时间值替换对话框标题。您不能重写有问题的方法updateTitle(),但可以在onTimeChanged()处截获调用,而不调用super或使用固定参数调用它

    我键入了setTitle()方法是否是用非零字符串ID调用的,但是您可以为布尔值本身或其他任何东西使用getter/setter

    @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;
    

    挣扎了30分钟的时间间隔超过了一个小时。如果看不到@Lee的评论,可能会更加挣扎。因此,只需发布间隔30分钟的完整对话代码:

    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;
            }
        }
    
    公共类IntervalTimePickerDialog扩展了TimePickerDialog{
    私有静态最终字符串标记=“IntervalPickerDialog”;
    专用最终静态整数时间选择器间隔=30;
    私人时间选择器;
    普里瓦特
    
    @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;
    
    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;
            }
        }
    
    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();
        }
    
    }
    
    /**
     * 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);
        }
    }