首选框中的时间选择器

我希望创建一个名为 Interval的首选项字段,并希望能够弹出一个 TimePicker并设置一个 mm:ss格式的值,其值最小为 00:30,步骤为30秒。

有可能在 PreferenceScreen中使用 TimePicker吗?

41310 次浏览

Android 没有内置 TimePreferences,但是创建自己的 TimePreferences 相当容易。 这个是我做的:

import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TimePicker;


public class TimePreference extends DialogPreference {
private int lastHour=0;
private int lastMinute=0;
private TimePicker picker=null;


public static int getHour(String time) {
String[] pieces=time.split(":");


return(Integer.parseInt(pieces[0]));
}


public static int getMinute(String time) {
String[] pieces=time.split(":");


return(Integer.parseInt(pieces[1]));
}


public TimePreference(Context ctxt, AttributeSet attrs) {
super(ctxt, attrs);


setPositiveButtonText("Set");
setNegativeButtonText("Cancel");
}


@Override
protected View onCreateDialogView() {
picker=new TimePicker(getContext());


return(picker);
}


@Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);


picker.setCurrentHour(lastHour);
picker.setCurrentMinute(lastMinute);
}


@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);


if (positiveResult) {
lastHour=picker.getCurrentHour();
lastMinute=picker.getCurrentMinute();


String time=String.valueOf(lastHour)+":"+String.valueOf(lastMinute);


if (callChangeListener(time)) {
persistString(time);
}
}
}


@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return(a.getString(index));
}


@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
String time=null;


if (restoreValue) {
if (defaultValue==null) {
time=getPersistedString("00:00");
}
else {
time=getPersistedString(defaultValue.toString());
}
}
else {
time=defaultValue.toString();
}


lastHour=getHour(time);
lastMinute=getMinute(time);
}
}

对于那些自定义首选项的实现不那么明显的人(就像对我来说不是那样) ,你必须把它添加到你的 preferences.xml或任何你称之为它的东西中。

你最终会得到这样的结果:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >


<EditTextPreference
android:key="editTextPref_Key"
android:title="@string/editTextPref_title"/>
<com.example.myapp.TimePreference
android:key="timePrefA_Key"
android:title="@string/timePrefA_title"/>
<com.example.myapp.TimePreference
android:key="timePrefB_Key"
android:title="@string/timePrefB_title"/>


</PreferenceScreen>

假设您将 TimePreferences 添加到您自己的根包中:
(src/com/example/myapp/TimePreference.java)

我已经修改了第一个答案的代码:

  • 它以长形式(毫秒)存储选定的时间,这样比字符串更容易处理(使用日历)
  • 它会以用户格式(12小时或24小时)自动在摘要字段中显示选定的时间

更新代码:

public class TimePreference extends DialogPreference {
private Calendar calendar;
private TimePicker picker = null;


public TimePreference(Context ctxt) {
this(ctxt, null);
}


public TimePreference(Context ctxt, AttributeSet attrs) {
this(ctxt, attrs, android.R.attr.dialogPreferenceStyle);
}


public TimePreference(Context ctxt, AttributeSet attrs, int defStyle) {
super(ctxt, attrs, defStyle);


setPositiveButtonText(R.string.set);
setNegativeButtonText(R.string.cancel);
calendar = new GregorianCalendar();
}


@Override
protected View onCreateDialogView() {
picker = new TimePicker(getContext());
return (picker);
}


@Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
picker.setCurrentHour(calendar.get(Calendar.HOUR_OF_DAY));
picker.setCurrentMinute(calendar.get(Calendar.MINUTE));
}


@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);


if (positiveResult) {
calendar.set(Calendar.HOUR_OF_DAY, picker.getCurrentHour());
calendar.set(Calendar.MINUTE, picker.getCurrentMinute());


setSummary(getSummary());
if (callChangeListener(calendar.getTimeInMillis())) {
persistLong(calendar.getTimeInMillis());
notifyChanged();
}
}
}


@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return (a.getString(index));
}


@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {


if (restoreValue) {
if (defaultValue == null) {
calendar.setTimeInMillis(getPersistedLong(System.currentTimeMillis()));
} else {
calendar.setTimeInMillis(Long.parseLong(getPersistedString((String) defaultValue)));
}
} else {
if (defaultValue == null) {
calendar.setTimeInMillis(System.currentTimeMillis());
} else {
calendar.setTimeInMillis(Long.parseLong((String) defaultValue));
}
}
setSummary(getSummary());
}


@Override
public CharSequence getSummary() {
if (calendar == null) {
return null;
}
return DateFormat.getTimeFormat(getContext()).format(new Date(calendar.getTimeInMillis()));
}
}

和 LEO87一样,我看到的是 ClassCastException 的。问题是由于来自同名的前一个控件的持久化数据过时造成的。可能的解决方案是清除应用程序数据,使用不同的名称(密钥) ,或者如果你必须使用相同的密钥名称,捕获如下异常:

@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
if (restoreValue) {
long persistedValue;
try {
persistedValue = getPersistedLong(System.currentTimeMillis());
} catch (Exception e) {
//Stale persisted data may be the wrong type
persistedValue = System.currentTimeMillis();
}
calendar.setTimeInMillis(persistedValue);
} else if (defaultValue != null) {
calendar.setTimeInMillis(Long.parseLong((String) defaultValue));
} else {
//!restoreValue, defaultValue == null
calendar.setTimeInMillis(System.currentTimeMillis());
}


setSummary(getSummary());
}

为摘要添加以下内容:

@Override
public CharSequence getSummary() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, lastHour, lastMinute);
return DateFormat.getTimeFormat(getContext()).format(new Date(cal.getTimeInMillis()));
}

然后加

setSummary(getSummary());

OnSetInitialValue关闭的末尾。

我已经修改了 CommonsWare 的答案来使用 JodaTime 库:

import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TimePicker;


import org.joda.time.LocalTime;


public class TimePreference extends DialogPreference {
private int lastHour;
private int lastMinute;
private TimePicker picker;


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


setPositiveButtonText("Set");
setNegativeButtonText("Cancel");
}


@Override
protected View onCreateDialogView() {
picker = new TimePicker(getContext());


return(picker);
}


@Override
protected void onBindDialogView(@NonNull View v) {
super.onBindDialogView(v);


picker.setCurrentHour(lastHour);
picker.setCurrentMinute(lastMinute);
}


@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);


if (positiveResult) {
lastHour = picker.getCurrentHour();
lastMinute = picker.getCurrentMinute();


LocalTime localTime = new LocalTime(lastHour, lastMinute);
String time = localTime.toString();


if (callChangeListener(time)) {
persistString(time);
}
}
}


@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return(a.getString(index));
}


@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
LocalTime time;


if (restoreValue) {
if (defaultValue == null) {
time = LocalTime.parse(getPersistedString("08:00:00.000"));
}
else {
time = LocalTime.parse(getPersistedString(defaultValue.toString()));
}
} else {
time = LocalTime.parse(defaultValue.toString());
}


lastHour = time.getHourOfDay();
lastMinute = time.getMinuteOfHour();
}
}

你还需要像 Sikora 说的那样添加一个自定义首选项。

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >


<EditTextPreference
android:key="editTextPref_Key"
android:title="@string/editTextPref_title"/>
<com.example.myapp.TimePreference
android:key="timePrefA_Key"
android:title="@string/timePrefA_title"/>
<com.example.myapp.TimePreference
android:key="timePrefB_Key"
android:title="@string/timePrefB_title"/>


</PreferenceScreen>

在 Android 6中,“当前时间”和“当前分钟”是不被推荐的。使用这个来确保棉花糖的兼容性:

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TimePicker;


public class TimePreference extends DialogPreference {


private int lastHour;
private int lastMinute;
private TimePicker picker;


public TimePreference(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
setPositiveButtonText(ctx.getString(android.R.string.ok));
setNegativeButtonText(ctx.getString(android.R.string.cancel));
}


@Override
protected View onCreateDialogView() {
picker = new TimePicker(getContext());
picker.setIs24HourView(true);
return picker;
}


@SuppressWarnings("deprecation")
@Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
picker.setCurrentHour(lastHour);
picker.setCurrentMinute(lastMinute);
} else {
picker.setHour(lastHour);
picker.setMinute(lastMinute);
}
}


@SuppressWarnings("deprecation")
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
lastHour = picker.getCurrentHour();
lastMinute = picker.getCurrentMinute();
} else {
lastHour = picker.getHour();
lastMinute = picker.getMinute();
}
String time = String.valueOf(lastHour) + ":" + String.valueOf(lastMinute);
if (callChangeListener(time)) {
persistString(time);
}
}
}


@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}


@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
String time;
if (restoreValue) {
if (defaultValue == null) {
time = getPersistedString("00:00");
} else {
time = getPersistedString(defaultValue.toString());
}
} else {
time = defaultValue.toString();
}
lastHour = getHour(time);
lastMinute = getMinute(time);
}


public static int getHour(String time) {
String[] pieces = time.split(":");
return Integer.parseInt(pieces[0]);
}


public static int getMinute(String time) {
String[] pieces = time.split(":");
return Integer.parseInt(pieces[1]);
}
}

对于 Preferences Support Library,需要不同的代码。它需要两个定制类 TimePreferenceTimePreferenceDialogFragmentCompat,以及在 PreferenceFragmentCompat扩展类中重写 onDisplayPreferenceDialog方法。


TimePreference.java

package com.test;


import android.content.Context;
import android.content.res.TypedArray;
import android.support.v7.preference.DialogPreference;
import android.util.AttributeSet;


public class TimePreference extends DialogPreference
{
public int hour = 0;
public int minute = 0;


public static int parseHour(String value)
{
try
{
String[] time = value.split(":");
return (Integer.parseInt(time[0]));
}
catch (Exception e)
{
return 0;
}
}


public static int parseMinute(String value)
{
try
{
String[] time = value.split(":");
return (Integer.parseInt(time[1]));
}
catch (Exception e)
{
return 0;
}
}


public static String timeToString(int h, int m)
{
return String.format("%02d", h) + ":" + String.format("%02d", m);
}


public TimePreference(Context context, AttributeSet attrs)
{
super(context, attrs);
}


@Override
protected Object onGetDefaultValue(TypedArray a, int index)
{
return a.getString(index);
}


@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue)
{
String value;
if (restoreValue)
{
if (defaultValue == null) value = getPersistedString("00:00");
else value = getPersistedString(defaultValue.toString());
}
else
{
value = defaultValue.toString();
}


hour = parseHour(value);
minute = parseMinute(value);
}


public void persistStringValue(String value)
{
persistString(value);
}
}

Java

package com.test;


import android.content.Context;
import android.support.v7.preference.DialogPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceDialogFragmentCompat;
import android.view.View;
import android.widget.TimePicker;


public class TimePreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat implements DialogPreference.TargetFragment
{
TimePicker timePicker = null;


@Override
protected View onCreateDialogView(Context context)
{
timePicker = new TimePicker(context);
return (timePicker);
}


@Override
protected void onBindDialogView(View v)
{
super.onBindDialogView(v);
timePicker.setIs24HourView(true);
TimePreference pref = (TimePreference) getPreference();
timePicker.setCurrentHour(pref.hour);
timePicker.setCurrentMinute(pref.minute);
}


@Override
public void onDialogClosed(boolean positiveResult)
{
if (positiveResult)
{
TimePreference pref = (TimePreference) getPreference();
pref.hour = timePicker.getCurrentHour();
pref.minute = timePicker.getCurrentMinute();


String value = TimePreference.timeToString(pref.hour, pref.minute);
if (pref.callChangeListener(value)) pref.persistStringValue(value);
}
}


@Override
public Preference findPreference(CharSequence charSequence)
{
return getPreference();
}
}

PreferenceFragmentCompat扩展类中所需的修改

    public static class PreferencesFragment extends PreferenceFragmentCompat
{
....


@Override
public void onDisplayPreferenceDialog(Preference preference)
{
DialogFragment dialogFragment = null;
if (preference instanceof TimePreference)
{
dialogFragment = new TimePreferenceDialogFragmentCompat();
Bundle bundle = new Bundle(1);
bundle.putString("key", preference.getKey());
dialogFragment.setArguments(bundle);
}


if (dialogFragment != null)
{
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(this.getFragmentManager(), "android.support.v7.preference.PreferenceFragment.DIALOG");
}
else
{
super.onDisplayPreferenceDialog(preference);
}
}
}

使用以上代码时首选项可以在首选项 xml 文件中使用,如下所示

<com.test.TimePreference
android:key="some_time"
android:title="Set some time"
android:defaultValue="12:00"
android:summary="Set some time"/>

Commons Ware 的解决方案有几个问题,我已经解决了:

  • 在更改字段后,它不会正确地更新字段
  • 分钟值只保留一个数字,例如10:2而不是10:02
  • 如果您使用 PreferenceManager.setDefaultPreferences 来设置应用程序中的初始默认首选项,那么它将无法工作,因为 onSetInitialValue 需要持久化它
  • 结果的格式不适合用户的语言环境(例如 US 使用 AM/PM)

这是我的密码,好好享受吧。

public class TimePreference extends DialogPreference {
private int lastHour=0;
private int lastMinute=0;
private TimePicker picker=null;


public static int getHour(String time) {
String[] pieces=time.split(":");


return(Integer.parseInt(pieces[0]));
}


public static int getMinute(String time) {
String[] pieces=time.split(":");


return(Integer.parseInt(pieces[1]));
}


public TimePreference(Context ctxt, AttributeSet attrs) {
super(ctxt, attrs);


setPositiveButtonText("Set");
setNegativeButtonText("Cancel");
}


@Override
protected View onCreateDialogView() {
picker=new TimePicker(getContext());


return(picker);
}


@Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);


picker.setCurrentHour(lastHour);
picker.setCurrentMinute(lastMinute);
}


@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);


if (positiveResult) {
lastHour=picker.getCurrentHour();
lastMinute=picker.getCurrentMinute();


setSummary(getSummary());


String lastMinuteString = String.valueOf(lastMinute);
String time = String.valueOf(lastHour) + ":" + (lastMinuteString.length() == 1 ? "0" + lastMinuteString : lastMinuteString);


if (callChangeListener(time)) {
persistString(time);
}
}
}


@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return(a.getString(index));
}


@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {


String time;
String defaultValueStr = (defaultValue != null) ? defaultValue.toString() : "00:00";
if (restoreValue)
time = getPersistedString(defaultValueStr);
else {
time = defaultValueStr;
if (shouldPersist())
persistString(defaultValueStr);
}


lastHour=getHour(time);
lastMinute=getMinute(time);


setSummary(getSummary());
}


@Override
public CharSequence getSummary() {


Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, lastHour);
cal.set(Calendar.MINUTE, lastMinute);
DateFormat sdf = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT);


return sdf.format(cal.getTime());
}


}