Android - TimePicker 分钟到 15

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2580216/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 06:24:05  来源:igfitidea点击:

Android - TimePicker minutes to 15

android

提问by teepusink

Can I control the Android TimePicker view to just show the minutes to 15 minutes interval?

我可以控制 Android TimePicker 视图只显示分钟到 15 分钟的间隔吗?

Meaning if it's 12:28 now, show 12:30 and clicking the + and - button will increment and decrement by 15?

意思是如果现在是 12:28,显示 12:30 并单击 + 和 - 按钮将增加和减少 15?

回答by Ali

Create a xml file and name it activity_time_picker.xml

创建一个 xml 文件并将其命名为 activity_time_picker.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="match_parent" >

 <TimePicker
        android:id="@+id/timePicker1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/layoutHourMinute"
        android:layout_centerInParent="true"

        />

</RelativeLayout>

now create activity class like this

现在创建这样的活动类

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

import android.annotation.SuppressLint;

import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.NumberPicker;
import android.widget.TimePicker;

public class TimePickerActivity extends Activity {


    TimePicker timePicker;
    private int TIME_PICKER_INTERVAL = 15;
     NumberPicker minutePicker;
     List<String> displayedValues;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
                 super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        setContentView(R.layout.activity_time_picker);
        super.onCreate(savedInstanceState);
        timePicker = (TimePicker)findViewById(R.id.timePicker1);

        timePicker.setIs24HourView(true);
        timePicker.setCurrentHour(0);
        timePicker.setCurrentMinute(0);

        setTimePickerInterval(timePicker);
    }
@SuppressLint("NewApi")
    private void setTimePickerInterval(TimePicker timePicker) {
         try {
                Class<?> classForid = Class.forName("com.android.internal.R$id");
               // Field timePickerField = classForid.getField("timePicker");  

                Field field = classForid.getField("minute");
                minutePicker = (NumberPicker) timePicker
                        .findViewById(field.getInt(null));

                minutePicker.setMinValue(0);
                minutePicker.setMaxValue(3);
                displayedValues = new ArrayList<String>();
                for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL) {
                    displayedValues.add(String.format("%02d", i));
                }
              //  for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL) {
              //      displayedValues.add(String.format("%02d", i));
              //  }
                minutePicker.setDisplayedValues(displayedValues
                        .toArray(new String[0]));
               minutePicker.setWrapSelectorWheel(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
    }
}

回答by Mark Horgan

Here's my version where you can set the interval:

这是我的版本,您可以在其中设置间隔:

private static final int TIME_PICKER_INTERVAL=15;
private boolean mIgnoreEvent=false;

private TimePicker.OnTimeChangedListener mTimePickerListener=new TimePicker.OnTimeChangedListener(){
    public void onTimeChanged(TimePicker timePicker, int hourOfDay, int minute){
        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;
            timePicker.setCurrentMinute(minute);
            mIgnoreEvent=false;
        }

    }
};

回答by Andy Dyer

The following worked for me.

以下对我有用。

First, in onCreate:

首先,在onCreate中:

pickStartTime = (TimePicker)findViewById(R.id.StartTime);
pickStartTime.setOnTimeChangedListener(mStartTimeChangedListener);

Set the OnTimeChangedListener:

设置 OnTimeChangedListener:

private TimePicker.OnTimeChangedListener mStartTimeChangedListener =
    new TimePicker.OnTimeChangedListener() {

    public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
        updateDisplay(view, startDate, hourOfDay, minute);          
    }
};

Null OnTimeChangedListener (explained in comments in updateDisplay below):

Null OnTimeChangedListener(在下面 updateDisplay 的注释中解释):

private TimePicker.OnTimeChangedListener mNullTimeChangedListener =
    new TimePicker.OnTimeChangedListener() {

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

    }
};

private void updateDisplay(TimePicker timePicker, Date date, int hourOfDay, int minute) { 

    // do calculation of next time 
    int nextMinute = 0;     
    if (minute >= 45 && minute <= 59)
        nextMinute = 45;
    else if(minute >= 30)
        nextMinute = 30;
    else if(minute >= 15)
        nextMinute = 15;
    else if(minute > 0)
        nextMinute = 0;
    else {          
        nextMinute = 45;
    }

    // remove ontimechangedlistener to prevent stackoverflow/infinite loop
    timePicker.setOnTimeChangedListener(mNullTimeChangedListener);

    // set minute
    timePicker.setCurrentMinute(nextMinute);

    // hook up ontimechangedlistener again
    timePicker.setOnTimeChangedListener(mStartTimeChangedListener);

    // update the date variable for use elsewhere in code
    date.setMinutes(nextMinute);  
}

回答by Robby Pond

You have to write your own TimePicker. Not sure if you can extend the existing TimePicker and handle the button events on your own.

您必须编写自己的 TimePicker。不确定您是否可以扩展现有的 TimePicker 并自行处理按钮事件。

回答by teepusink

I think I figured it out. Might be ghetto way but...

我想我想通了。可能是贫民窟的方式但是...

Here is what I did.

这是我所做的。

start_time.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {

            public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
                updateDisplay(hourOfDay, minute);
            }
        });

private void updateDisplay(int hourOfDay, int minute) {
                // do calculation of next time
                // nextTime = calculation of next time;

                // the processed boolean is to prevent infinite loop
        if (!processed) {
            start_time.setCurrentMinute(nextTime);  
                        processed = true;
        } else {
                        processed = false;
                }
    }

回答by Shiv Prakash

private void updateDisplay(TimePicker timePicker, int hourOfDay, int minute) {
    int nextMinute = 0;

    timePicker.setOnTimeChangedListener(mNullTimeChangedListener);

    if (minute >= 45 && minute <= 59) 
        nextMinute = 45;  
    else if (minute  >= 30) 
        nextMinute = 30;
    else if (minute >= 15) 
        nextMinute = 15;
    else if (minute > 0) 
        nextMinute = 0; 
    else { 
        nextMinute = 45; 
    }

    if (minute - nextMinute == 1) {
        if (minute >= 45 && minute <= 59) 
            nextMinute = 00; 
        else if(minute  >= 30) 
            nextMinute = 45;
        else if(minute >= 15) 
            nextMinute = 30;
        else if(minute > 0) 
            nextMinute = 15; 
        else { 
            nextMinute = 15; 
        }
    }

    timePicker.setCurrentMinute(nextMinute);

    timePicker.setOnTimeChangedListener(timePickerChangedListener);

}

Update display method modified for + and - buttons. Rest of the code is same as that from Andrew Dyer.

更新为 + 和 - 按钮修改的显示方法。其余代码与 Andrew Dyer 的代码相同。

回答by BrennanR

I've come up with a solution based on some of the previous answers to this question, and some of my own additions. This solution notably overrides getCurrentMinute and setCurrentMinute, which by default return you the minute index, not the minute value (ie. if your minute interval is 15 and the picker had a displayed minute of 30, it would return 2 without this code).

我根据之前对这个问题的一些答案以及我自己的一些补充提出了一个解决方案。该解决方案特别覆盖了 getCurrentMinute 和 setCurrentMinute,它们默认返回分钟索引,而不是分钟值(即,如果您的分钟间隔为 15 并且选择器显示的分钟为 30,它会在没有此代码的情况下返回 2)。

This solution will also rollover minutes greater than the max interval value (again if your interval is 15, any minutes > 45 will set the minute value to 0 and increment the hour value).

此解决方案还将翻转大于最大间隔值的分钟(同样,如果您的间隔为 15,则任何大于 45 的分钟会将分钟值设置为 0 并增加小时值)。

NOTE: This is tested and working on Android 4.4 and 5.0.

注意:这是在 Android 4.4 和 5.0 上测试和工作的。

public class CustomIntervalTimePicker extends TimePicker {

    private static final int TIME_PICKER_MINUTE_INTERVAL = 15;
    private OnTimeChangedListener timeChangedListener;

    public CustomIntervalTimePicker(Context context, AttributeSet attrs) {
        super(context, attrs);

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

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

    private int maxMinuteIndex() {
        return (60 / TIME_PICKER_MINUTE_INTERVAL) - 1;
    }

    @Override
    public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) {
        super.setOnTimeChangedListener(internalTimeChangedListener);
        this.timeChangedListener = onTimeChangedListener;
    }

    @Override
    public Integer getCurrentMinute() {
        return super.getCurrentMinute() * TIME_PICKER_MINUTE_INTERVAL;
    }

    @Override
    public void setCurrentMinute(Integer currentMinute) {
        int cleanMinute = currentMinute / TIME_PICKER_MINUTE_INTERVAL;
        if(currentMinute % TIME_PICKER_MINUTE_INTERVAL > 0) {
            if(cleanMinute == maxMinuteIndex()) {
                cleanMinute = 0;
                setCurrentHour(getCurrentHour()+1);
            } else {
                cleanMinute++;
            }
        }
        super.setCurrentMinute(cleanMinute);
    }

    // We want to proxy all the calls to our member variable OnTimeChangedListener with our own
    // internal listener in order to make sure our overridden getCurrentMinute is called. Without
    // this some versions of android return the underlying minute index.
    private OnTimeChangedListener internalTimeChangedListener = new OnTimeChangedListener() {
        @Override
        public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
            timeChangedListener.onTimeChanged(view, getCurrentHour(), getCurrentMinute());
        }
    };
}

回答by Shiv Prakash

First of all I'd like to thank Andrew for overcoming stackoverflow error(infinite loop).My logic to increment time by 15 min is something like this.Declare int previousMinuteSet = 0 as a member variable in the given class.

首先,我要感谢 Andrew 克服了 stackoverflow 错误(无限循环)。我将时间增加 15 分钟的逻辑是这样的。声明 int previousMinuteSet = 0 作为给定类中的成员变量。

// remove ontimechangedlistener to prevent stackoverflow/infinite loop timePicker.setOnTimeChangedListener(mNullTimeChangedListener);

// remove ontimechangedlistener to prevent stackoverflow/infinite loop timePicker.setOnTimeChangedListener(mNullTimeChangedListener);

        if((minute - previousMinuteSet  == 1) || (minute - previousMinuteSet  == 15) || (minute - previousMinuteSet  == -45)) {
            // set hour
            timePicker.setCurrentHour((hourOfDay + minute / 45) % 12);
            // set minute
            timePicker.setCurrentMinute((minute + 14) % 60);
        } else {
            // set hour
            timePicker.setCurrentHour(hourOfDay);
            // set minute
            timePicker.setCurrentMinute(minute);

        }
        previousMinuteSet = minute;
        // hook up ontimechangedlistener again
        timePicker.setOnTimeChangedListener(timePickerChangedListener);
        if((minute - previousMinuteSet  == 1) || (minute - previousMinuteSet  == 15) || (minute - previousMinuteSet  == -45)) {
            // set hour
            timePicker.setCurrentHour((hourOfDay + minute / 45) % 12);
            // set minute
            timePicker.setCurrentMinute((minute + 14) % 60);
        } else {
            // set hour
            timePicker.setCurrentHour(hourOfDay);
            // set minute
            timePicker.setCurrentMinute(minute);

        }
        previousMinuteSet = minute;
        // hook up ontimechangedlistener again
        timePicker.setOnTimeChangedListener(timePickerChangedListener);

Hope this might would be helpful for others.The code I replaced in updateDisplay method of Andrew, apart from that everything was same.

希望这对其他人有帮助。我在 Andrew 的 updateDisplay 方法中替换的代码,除此之外一切都相同。

回答by Varvara Kalinina

Here's the version I wrote. It's C# code because I'm using Xamarin, but I hope it won't be harder for anyone to translate it to Java than for me it is to perform vica versa translation when I need to copy a piece of Java code.

这是我写的版本。它是 C# 代码,因为我使用的是 Xamarin,但我希望任何人将它翻译成 Java 不会比我更难,当我需要复制一段 Java 代码时执行反之亦然的翻译。

It's a bit different from standard TimePicker in that it's top and bottom border is not continuous, but it's a flaw some people will find insignificant.

它与标准的 TimePicker 有点不同,因为它的顶部和底部边框不连续,但这是一个有些人会觉得微不足道的缺陷。

I could have done a better job in making setting custom minutes interval easier and not making you to hardcode the interval values in the implementation, but I believe you can polish this solution if you want to very quickly!

我本可以做得更好,让设置自定义分钟间隔更容易,而不是让您在实现中对间隔值进行硬编码,但我相信如果您想很快就可以完善这个解决方案!

HourPickerMapper.cs

HourPickerMapper.cs

public class HourPickerMapper
{
    public HourPickerMapper() { }

    public int MapValue(int hour)
    {
        return hour;
    }

    public int MapNumber(int number)
    {
        return number;
    }

    public void InitNumberPicker(NumberPicker numberPicker)
    {
        int[] numbers = new int[24];
        for (var i = 0; i < numbers.Length; i++)
        {
            numbers[i] = i;
        }
        var displayed = numbers.Select(_ => $"{_:00}").ToArray();

        numberPicker.SetDisplayedValues(displayed);
        numberPicker.MinValue = 0;
        numberPicker.MaxValue = displayed.Length - 1;
    }
}

MinutePickerMapper.cs

MinutePickerMapper.cs

public class MinutePickerMapper
{
    public MinutePickerMapper() { }

    public int MapValue(int value)
    {
        return (int)Math.Floor(value / 10.0);
    }

    public int MapNumber(int number)
    {
        return number * 10;
    }

    public void InitNumberPicker(NumberPicker numberPicker)
    {
        int[] numbers = new int[6];
        for (var i = 0; i < numbers.Length; i++)
        {
            numbers[i] = i * 10;
        }
        var displayed = numbers.Select(_ => _.ToString()).ToArray();

        numberPicker.SetDisplayedValues(displayed);
        numberPicker.MinValue = 0;
        numberPicker.MaxValue = displayed.Length - 1;
    }
}

And here's the example of usage, a DialogFragment

这是使用示例,a DialogFragment

public class CustomDateTimePickerDialogFragment : Android.Support.V4.App.DialogFragment
{
    private DateTime _dt;
    private NumberPicker _datePicker, _hourPicker, _minutePicker;
    private DatePickerMapper _datePickerMapper;
    private HourPickerMapper _hourPickerMapper;
    private MinutePickerMapper _minutePickerMapper;

    public event EventHandler<DateTimeSetEventArgs> DateTimeSetEvent;
    public CustomDateTimePickerDialogFragment()
    {
        _dt = DateTime.Now + new TimeSpan(24, 0, 0); // now + 1 day
    }

    public CustomDateTimePickerDialogFragment(DateTime dt)
    {
        _dt = dt;
        if (dt == null)
        {
            _dt = DateTime.Now + new TimeSpan(24, 0, 0); // now + 1 day
        }
    }

    public override Dialog OnCreateDialog(Bundle savedInstanceState)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(Context);
        builder.SetNeutralButton(Resource.String.dialog_datetime_now, (sender, e) => 
        {
            var eventArgs = new DateTimeSetEventArgs();
            DateTimeSetEvent.Invoke(this, eventArgs);
        });
        builder.SetPositiveButton(Resource.String.dialog_ok, (sender, e) =>
        {
            var hour = _hourPickerMapper.MapNumber(_hourPicker.Value);
            var minute = _minutePickerMapper.MapNumber(_minutePicker.Value);
            var date = _datePickerMapper.MapNumber(_datePicker.Value);
            var eventArgs = new DateTimeSetEventArgs(new DateTime(date.Year, date.Month, date.Day, hour, minute, 0));
            DateTimeSetEvent.Invoke(this, eventArgs);
            Dismiss();
        });
        var dialog = builder.Create();
        dialog.SetView(createDateTimeView(dialog.LayoutInflater));

        _datePicker.Value = _datePickerMapper.MapValue(_dt);
        _hourPicker.Value = _hourPickerMapper.MapValue(_dt.Hour);
        _minutePicker.Value = _minutePickerMapper.MapValue(_dt.Minute);

        return dialog;
    }

    private View createDateTimeView(LayoutInflater inflater)
    {
        View view = inflater.Inflate(Resource.Layout.dialog_custom_datetime_picker, null);

        _datePicker = view.FindViewById<NumberPicker>(Resource.Id.datePicker);
        _datePickerMapper = new DatePickerMapper(DateTime.Now, 10);
        _datePickerMapper.InitNumberPicker(_datePicker);

        var now = DateTime.Now;
        _datePicker.Value = _datePickerMapper.MapValue(now);

        _minutePicker = view.FindViewById<NumberPicker>(Resource.Id.minutePicker);
        _minutePickerMapper = new MinutePickerMapper();
        _minutePickerMapper.InitNumberPicker(_minutePicker);
        _minutePicker.Value = _minutePickerMapper.MapValue(now.Minute);

        _hourPicker = view.FindViewById<NumberPicker>(Resource.Id.hourPicker);
        _hourPickerMapper = new HourPickerMapper();
        _hourPickerMapper.InitNumberPicker(_hourPicker);
        _hourPicker.Value = _hourPickerMapper.MapValue(now.Hour);

        return view;
    }

    public class DateTimeSetEventArgs : EventArgs
    {
        public DateTimeSetEventArgs(DateTime dt)
        {
            IsNow = false;
            DT = dt;
        }

        public DateTimeSetEventArgs()
        {
            IsNow = true;
        }

        public DateTime DT { get; }
        public bool IsNow { get; }
    }
}

dialog_custom_datetime_picker.xml

dialog_custom_datetime_picker.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="@dimen/dialog_padding"
    >
  <!-- Layout for the DatePicker -->
  <LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
  >
     <LinearLayout
      android:orientation="horizontal"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:gravity="center_vertical"
      >
      <NumberPicker
        android:id="@+id/datePicker"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:descendantFocusability="blocksDescendants"
        />
      <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" "
        />
      <NumberPicker
        android:id="@+id/hourPicker"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:descendantFocusability="blocksDescendants"
        />
         <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text=":"
          />
      <NumberPicker
        android:id="@+id/minutePicker"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:descendantFocusability="blocksDescendants"
        />       
    </LinearLayout>  
  </LinearLayout>
</LinearLayout>