TimePicker in PreferenceScreen

匿名 (未验证) 提交于 2019-12-03 02:11:02

问题:

I'd like to create a preference field called Interval and I want to be able to popup a TimePicker and set a mm:ss formated value with minimal value 00:30 and step 30 seconds.

Is it possible to use TimePicker in PreferenceScreen ?

回答1:

There is no TimePreference built into Android. However, creating your own is fairly easy. Here's one I did:

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


回答2:

I have modified the code from first answer:

  • it stores selected time in long form (milliseconds) which is easier to work with (using Calendar) then string
  • it automatically shows selected time in summary field in user's format (12 or 24 hour)

Updated code:

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


回答3:

For those whom the implementation of a custom Preference isn't so obvious (like it wasn't for me), you have to add this to your preferences.xml or whatever you're calling it.

You'll end up with something like this:

<?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> 

Assuming you added the TimePreference to your own root package:
(src/com/example/myapp/TimePreference.java)



回答4:

For Preferences Support Library different code is needed. It requires two custom classes TimePreference and TimePreferenceDialogFragmentCompat, as well as overide of onDisplayPreferenceDialog method in PreferenceFragmentCompat extension class.


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

Required modifications in PreferenceFragmentCompat extension class

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

With above code time preference can be used in preferences xml file like this

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


回答5:

add this for Summary:

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

and add

setSummary(getSummary()); 

to end of onSetInitialValue and onDialogClosed.



回答6:

CommonsWare's solution has a few problems, which I fixed:

  • It doesn't update the field properly after it is changed
  • The minutes value only persists a single digit, e.g. 10:2 instead of 10:02
  • If you use PreferenceManager.setDefaultPreferences to set initial default preferences in your app, it won't work because onSetInitialValue needs to persist it
  • The formatting of the result isn't tailored to the user's Locale (e.g. US uses AM/PM)

Here's my code, enjoy.

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


回答7:

I have modified CommonsWare answer to use JodaTime library:

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

Also you will need to add a Custom preference like Sikora said.

<?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> 


回答8:

With Android 6, "current hour" and "current minute" are deprecated. Use this to ensure Marshmallow compatibility:

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


回答9:

Like LEO87, I was seeing ClassCastException's. The problem was due to stale persisted data from a previous control of the same name. Possible solutions are to clear the app data, use a different name (key), or if you must use the same key name, catch the exception as follows:

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


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!