PreferenceScreen中的TimePicker


77

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

是否有可能使用TimePickerPreferenceScreen

Answers:


151

Android中没有内置TimePreference。但是,创建自己的相当容易。这是我做过的

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

1
@Jesse:getCurrentHour()应当返回一个值从0到23
CommonsWare

2
确保键入的值与微调器选择的值一起使用非常方便:stackoverflow.com/questions/3992820/…–
安东尼·诺兰

2
@input:此问题中的实现将时间以字符串HH:MM形式存储为格式。该问题的另一个答案是可以将时间存储很长的替代版本。
CommonsWare,2012年

1
@input:打我 我从没看过那种情况。无论何时提交对话框,都应保存首选项。
CommonsWare,2012年

1
@juned:您不需要这样做TimePreference。只需在DialogFragment周围创建一个TimePickerDialog,如果不使用片段,则使用较旧的托管对话框工具。这是一个DialogFragment示例应用程序(尽管创建了一个示例应用程序,AlertDialog而不是一个TimePickerDialog):github.com/commonsguy/cw-omnibus/tree/master/Dialogs/…–
CommonsWare

72

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

  • 它以长格式(毫秒)存储选择的时间,这很容易使用(使用日历)然后使用字符串
  • 它会以用户的格式(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()));
    }
} 

2
很好,但是如何提供数字格式的默认值?当我使用android:defaultValue =“ 3600000”时,defaultValue是一个字符串,因此首先需要将其解析为Long。
2012年

1
@Destil我在onsetInitialValue中得到类强制转换异常...任何可能的补救
方法..

3
好的答案,除了一个小问题。如果是restoreValue == false这样,您必须persistLong(calendar.getTimeInMillis());作为代码块的最后一行来调用。否则PreferenceManager.setDefaultValues(...)PreferenceManager.getDefaultSharedPreferences(this).getLong(...)除非在首选项中实际选择了默认值,否则它将无法按预期工作,并且不会返回默认值。
zelanix 2014年

4
如果您遇到文本样式与其他首选项不同的问题,请更改两个参数的构造函数,以使用内部Android样式代替0。变更:this(context, attrs, android.R.attr.dialogPreferenceStyle);
Tautvydas 2014年

2
您能否实现此类的getHour,getMinute方法?谢谢
Hoang Trinh 2014年

56

对于那些自定义首选项的实现不是很明显的人(例如,对我而言不是),您必须将其添加到您的名称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>

假设你添加的TimePreference到自己的根包:
src/com/example/myapp/TimePreference.java


如何在另一个类中检索TimePreference的值?我以这种方式编码:hora_inicio = Integer.parseInt(sharedPrefs.getString("hora_inicio", "0"));但是我不确定这是否应该被调用?
输入

1
@input我不确定这是否是您要查找的内容,但是就这样: SharedPreferences prefs; prefs = PreferenceManager.getDefaultSharedPreferences(this); String[] pieces = refs.getString("hora_inicio","08:00").split(":"); 第二个参数是默认值(如果未设置)。
西科拉

3
添加了如上所述的首选项,它就像一个饰物一样工作(感谢)。但是,首选项文本的样式与其余样式不同,也就是说,在标题和摘要概述中,字体看起来更大,并且似乎添加了空白。关于如何改变这一点的任何想法?
加布里·范·李

1
使用它作为第二个构造函数:public TimePreference(Context mContext,AttributeSet mAttributeSet){this(mContext,mAttributeSet,android.R.attr.preferenceStyle); }
John P.

还有其他人吗07-16 15:33:41.689: W/ResourceType(9228): Failure getting entry for 0x010802c9 (t=7 e=713) in package 0 (error -75)
jcaruso

29

对于Preferences Support Library不同的代码是必需的。它需要两个自定义类TimePreferenceTimePreferenceDialogFragmentCompat,以及扩展类中的onDisplayPreferenceDialog方法的覆盖PreferenceFragmentCompat


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

TimePreferenceDialogFragmentCompat.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"/>

如果我添加其中2个,则在设置第一个TimePreference后,将第二个TimePreference的时间设置为相同。如何在2个TimePreferences之间进行区分?
Alex

解决了,忘了换钥匙了。
亚历克斯

像魅力一样工作!
iYonatan '16

1
就像我在回答的第一行所说的那样,“首选项支持库”要求使用不同的代码。其他解决方案适用于常规首选项类。
Dalija Prasnikar'5

1
超过完美!使用它为Xamarin创建日期设置的自定义日期选择器。也可以使用PreferenceManager支持库的来设置默认值,例如PreferenceManager.SetDefaultValues(context, resId, readAgain)
Elisabeth's

5

CommonsWare的解决方案存在一些问题,我已解决:

  • 更改后无法正确更新字段
  • 分钟值仅保留一位数字,例如10:2而不是10:02
  • 如果您使用PreferenceManager.setDefaultPreferences在您的应用程序中设置初始默认偏好设置,则它将无法正常工作,因为onSetInitialValue需要对其进行持久化
  • 结果的格式并非针对用户的语言环境而定制(例如,美国使用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());
    }

}

4

将此添加为摘要:

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

onSetInitialValueonDialogClosed的末尾


1

我已修改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>

1

在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]);
}
}

0

像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());
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.