【发布时间】:2010-04-05 18:28:54
【问题描述】:
我可以控制 Android TimePicker 视图只显示分钟到 15 分钟的间隔吗?
意思是如果现在是 12:28,则显示 12:30,然后单击 + 和 - 按钮将递增和递减 15?
【问题讨论】:
标签: android
我可以控制 Android TimePicker 视图只显示分钟到 15 分钟的间隔吗?
意思是如果现在是 12:28,则显示 12:30,然后单击 + 和 - 按钮将递增和递减 15?
【问题讨论】:
标签: android
创建一个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>
现在像这样创建活动类
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();
}
}
}
【讨论】:
Class.forName("com.android.internal.R$id") 上抱怨 ClassNotFoundException。
这是我的版本,您可以在其中设置间隔:
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;
}
}
};
【讨论】:
以下对我有用。
首先,在 onCreate 中:
pickStartTime = (TimePicker)findViewById(R.id.StartTime);
pickStartTime.setOnTimeChangedListener(mStartTimeChangedListener);
设置 OnTimeChangedListener:
private TimePicker.OnTimeChangedListener mStartTimeChangedListener =
new TimePicker.OnTimeChangedListener() {
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
updateDisplay(view, startDate, hourOfDay, minute);
}
};
Null OnTimeChangedListener(在下面的 updateDisplay 中用 cmets 解释):
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);
}
【讨论】:
您必须编写自己的 TimePicker。不确定您是否可以扩展现有的 TimePicker 并自行处理按钮事件。
【讨论】:
我想我明白了。可能是贫民区的方式,但...
这就是我所做的。
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;
}
}
【讨论】:
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);
}
更新为 + 和 - 按钮修改的显示方法。其余代码与 Andrew Dyer 相同。
【讨论】:
我根据之前对该问题的一些答案以及我自己的一些补充提出了一个解决方案。此解决方案显着覆盖了 getCurrentMinute 和 setCurrentMinute,默认情况下它们会返回分钟索引,而不是分钟值(即,如果您的分钟间隔为 15 并且选择器显示的分钟为 30,则在没有此代码的情况下将返回 2)。
此解决方案还将翻转大于最大间隔值的分钟数(同样,如果您的间隔为 15,任何分钟数 > 45 都会将分钟值设置为 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());
}
};
}
【讨论】:
首先,我要感谢 Andrew 克服了 stackoverflow 错误(无限循环)。我将时间增加 15 分钟的逻辑是这样的。将 int previousMinuteSet = 0 声明为给定类中的成员变量。
// 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);
希望这对其他人有帮助。我在 Andrew 的 updateDisplay 方法中替换的代码,除此之外一切都一样。
【讨论】:
这是我写的版本。这是 C# 代码,因为我使用的是 Xamarin,但我希望任何人将其翻译成 Java 都不会比我需要复制一段 Java 代码时执行相反的翻译更难。
它与标准的 TimePicker 有点不同,它的顶部和底部边框不是连续的,但这是一些人会觉得微不足道的缺陷。
我本可以做得更好,让设置自定义分钟间隔更容易,而不是让你在实现中硬编码间隔值,但我相信如果你想很快就完善这个解决方案!
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
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;
}
}
这是使用示例,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
<?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>
【讨论】:
TimePicker 基本上由两个NumberPickers 组成,分别代表小时和分钟。使用 Kotlin,您可以使用此函数使用任何分钟间隔:
https://stackoverflow.com/a/62172660/2016262
【讨论】:
这就是我在用户选择小时后成功仅显示 :00、:15、:30 和 :45 分钟选项的方法。
TimePickerDialog tpd = TimePickerDialog.newInstance( 这, DateTime.now().get(Calendar.HOUR_OF_DAY), DateTime.now().get(Calendar.MINUTE), 真的); tpd.setTimeInterval(1, 15);
tpd.setOkText(getString(R.string.lbl_ok));
tpd.setCancelText(getString(R.string.lbl_cancel));
tpd.show(getSupportFragmentManager(), tagName);
【讨论】:
你不能不扩展 TimePicker。试试这个解决方案:
import android.content.Context
import android.util.AttributeSet
import android.widget.NumberPicker
import android.widget.TimePicker
import androidx.databinding.BindingAdapter
import java.lang.reflect.Field
import java.util.*
class TimePickerInterval @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TimePicker(context, attrs, defStyleAttr) {
private var MINUTE_INTERVAL = 1
override fun getCurrentMinute(): Int {
return super.getCurrentMinute() * MINUTE_INTERVAL
}
override fun getMinute(): Int {
return super.getMinute() * MINUTE_INTERVAL
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
setUp()
}
private fun setUp() {
try {
val classForid = Class.forName("com.android.internal.R\$id")
val field: Field = classForid.getField("minute")
val mMinuteSpinner = findViewById<NumberPicker>(field.getInt(null))
mMinuteSpinner.minValue = 0
mMinuteSpinner.maxValue = 60 / MINUTE_INTERVAL - 1
val displayedValues: MutableList<String> = ArrayList()
var i = 0
while (i < 60) {
displayedValues.add(String.format("%02d", i))
i += MINUTE_INTERVAL
}
mMinuteSpinner.displayedValues = displayedValues.toTypedArray()
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun setOnTimeChangedListener(onTimeChangedListener: OnTimeChangedListener) {
super.setOnTimeChangedListener { tp, hour, minute ->
onTimeChangedListener.onTimeChanged(tp, hour, minute * MINUTE_INTERVAL)
}
}
override fun setMinute(minute: Int) {
super.setMinute(minute/MINUTE_INTERVAL)
}
companion object {
@JvmStatic
@BindingAdapter("time_picker_set_minute_interval")
fun setInterval(view: TimePickerInterval, interval: Int?) {
interval?.let {
view.MINUTE_INTERVAL = interval
view.setUp()
}
}
}
}
在您的 XML 中:
<com.gixon.shared.widgets.TimePickerInterval
android:id="@+id/tp_to"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:timePickerMode="spinner"
time_picker_set_minute_interval="@{5}"
/>
【讨论】: