79

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 ?

hsz
  • 148,279
  • 62
  • 259
  • 315

9 Answers9

151

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);
    }
}
Bryan Herbst
  • 66,602
  • 10
  • 133
  • 120
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Works great, but how do you capture the AM/PM? Is there a property for it? picker.getCurrentHour(); gets the hour, but what about getting AM/PM? – Jesse Apr 13 '12 at 14:28
  • 1
    @Jesse: `getCurrentHour()` should return a value from 0 to 23. – CommonsWare Apr 13 '12 at 14:30
  • 2
    This is handy to make sure that typed values are picked up along with those chosen by the spinner: http://stackoverflow.com/questions/3992820/android-how-to-get-the-time-from-a-timepicker-when-it-is-typed-in – Anthony Nolan May 08 '12 at 12:04
  • How do I retrieve the value of TimePreference in another class? I coded it this way: `hora_inicio = Integer.parseInt(sharedPrefs.getString("hora_inicio", "0"));` but I'm not sure if this is the way it's supposed to be called? – input Aug 11 '12 at 04:07
  • 2
    @input: The implementation in this question stores the time as a string, in `HH:MM` format. Another answer in this question has an alternative version that stores the time as a long. – CommonsWare Aug 11 '12 at 15:24
  • @CommonsWare, thanks. That helped! Another thing I have noticed is that if I manually enter the hours or mins through EditText inTimePreference it does't save the preference. But if I use the arrow buttons to change the numbers, it will save the preference. A user might want to directly enter the numbers rather than navigating through the arrows. How can I save the preference through EditText? – input Aug 13 '12 at 08:35
  • 1
    @input: Beats me; I never looked at that scenario. It should save the preference regardless when the dialog is submitted. – CommonsWare Aug 13 '12 at 10:44
  • @CommonsWare how to store time in long with time containing ":" which gives class cast exception – Snehal Poyrekar Oct 12 '12 at 09:18
  • @CommonsWare thanks for nice code, i have one question how do i use same class to open timePicker dialog on button click within an activity.i mean without preference screen – Juned Apr 02 '13 at 11:33
  • 1
    @juned: You would not need `TimePreference` for that. Just create a `DialogFragment` around the `TimePickerDialog`, or use the older managed-dialog facility if you are not using fragments. Here is a `DialogFragment` sample app (albeit one creating an `AlertDialog` instead of a `TimePickerDialog`): https://github.com/commonsguy/cw-omnibus/tree/master/Dialogs/DialogFragment – CommonsWare Apr 02 '13 at 12:10
  • @CommonsWare Thanks for quick response, but i want use your `TimePreference.java` class to show time picker but on button click like this `showTimePickerDialog()` method of this [link](http://developer.android.com/guide/topics/ui/controls/pickers.html) actually `TimePreference.java` class is perfect for me so i want to reuse it. – Juned Apr 02 '13 at 12:21
  • @juned: You cannot really use a `Preference` outside of the preference framework. You can use a `TimePickerDialog` separately from `TimePreference`, using dialog fragments or managed dialogs. – CommonsWare Apr 02 '13 at 12:52
  • @CommonsWare THanks everything is clear now,i will try to implement separate `TimePickerDialog` – Juned Apr 02 '13 at 13:44
  • 1
    +1 This answer helped me, thanks! However, I need the minutes in 2 characters only, so I replaced `String time=String.valueOf(lastHour)+":"+String.valueOf(lastMinute);` with `String lastMinuteString = String.valueOf(lastMinute); String time = String.valueOf(lastHour) + ":" + (lastMinuteString.length() == 1 ? "0" + lastMinuteString : lastMinuteString);` so the length would always be 2 characters. Still works on Lollipop 5.1 :) – Thomas Vos Jul 23 '15 at 10:48
  • 1
    I updated to androidx and this solution doesn't work anymore, you have to use support library, do not use the new one. (https://developer.android.com/guide/topics/ui/settings/) – Waza_Be Jan 12 '19 at 12:31
  • 2
    Very nice! Is it possible to create one for the new androidx api? – Martin Vysny Apr 21 '19 at 17:13
  • @MartinVysny: Presumably, though I haven't tried it. – CommonsWare Apr 21 '19 at 18:17
  • 1
    @MartinVysny I am also trying to get same one with androidx but it's not showing `onCreateDialogView` method as overrided method. Did you found any solution? – Jaydip Kalkani Aug 23 '19 at 10:12
72

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()));
    }
} 
David Vávra
  • 18,446
  • 7
  • 48
  • 56
  • 2
    Great, but how do you supply a default value in a numeric format? When I use android:defaultValue="3600000" the defaultValue is a String, so it first needs to be parsed to Long. – Mark Sep 02 '12 at 10:01
  • 1
    @Destil i get class cast exception in onsetInitialValue...any possible remedy..thanks – Snehal Poyrekar Oct 12 '12 at 09:14
  • Your onSetInitialValue breaks the contract defined in the API documentation. You are allowed to use the defaultValue parameter only if restoreValue is false. Making it work as it is expected expected simplifies the if-else statements considerably. – Torben Nov 22 '12 at 11:30
  • Does anyone know how to compare the stored value to the current time. I have to two `TimePreference`, a start and end time, and I'd like to tell if the current time is between the selected start and end time. I thought I could use `System.currentTimeMillis()` as a compare value, but that doesn't seem to work. – Kris B Mar 26 '13 at 02:07
  • +1 Because this is simply a lot better than the highest voted answer. – Kezz Mar 31 '13 at 19:43
  • Is anyone else getting `07-16 15:33:41.689: W/ResourceType(9228): Failure getting entry for 0x010802c9 (t=7 e=713) in package 0 (error -75)` – jcaruso Jul 16 '13 at 20:35
  • And why can i not set the `android:defaultValue="3600000"` – jcaruso Jul 16 '13 at 20:40
  • Caution: You cannot use the defaultValue as the default value in the getPersisted*() method, because its value is always null when restorePersistedValue is true. .....So there is no need of extra if else conditions... otherwise u had done a great job. – Pranav Jadav Aug 22 '13 at 10:53
  • 3
    Great answer save for one minor issue. If `restoreValue == false` then you must call `persistLong(calendar.getTimeInMillis());` as the last line in the code block. Otherwise `PreferenceManager.setDefaultValues(...)` does not work as expected and `PreferenceManager.getDefaultSharedPreferences(this).getLong(...)` will not return the default value unless it has actually been selected in the preferences. – zelanix Feb 11 '14 at 00:43
  • 4
    If you are having issues with text style being different from other preferences then change the two argument constructor to use internal Android style rather that `0`. Change: `this(context, attrs, android.R.attr.dialogPreferenceStyle);` – Tautvydas Feb 19 '14 at 22:34
  • 2
    Could you please implement how to getHour, getMinute of this class? Thank you – 0xh8h Jul 02 '14 at 16:57
  • How would I set a default value to 8am? I'm not sure how to sort that out when using setTimeMillis. – Ryan Quinn Dec 30 '16 at 22:49
  • I've set the default time in XML, like this: android:defaultValue="148417253000" But that gives a NumberFormatException. (because for some reason this is converted to scientific notation, e.g. 1.48417253E12) The solution for this is: long timeLong = Double.valueOf(defaultValue.toString()).longValue(); calendar.setTimeInMillis(timeLong); – FrankkieNL Jan 12 '17 at 14:00
56

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)

indivisible
  • 4,892
  • 4
  • 31
  • 50
Sikora
  • 766
  • 6
  • 6
  • How do I retrieve the value of TimePreference in another class? I coded it this way: `hora_inicio = Integer.parseInt(sharedPrefs.getString("hora_inicio", "0"));` but I'm not sure if this is the way it's supposed to be called? – input Aug 11 '12 at 04:07
  • 1
    @input I'm not sure if this is what you're looking for but, here it goes: `SharedPreferences prefs; prefs = PreferenceManager.getDefaultSharedPreferences(this); String[] pieces = refs.getString("hora_inicio","08:00").split(":");` the second argument is the default value if it's not set. – Sikora Aug 20 '12 at 17:47
  • 3
    Added the preference as stated which works like a charm (thanks for that). However, the style of the preference texts is different from the rest, that is, in the overview of titles and summaries, the font looks bigger and a margin seems to be added. Any ideas on how to change that? – Gabri van Lee Nov 16 '12 at 15:48
  • 1
    Use this as the second constructor: public TimePreference(Context mContext, AttributeSet mAttributeSet) { this(mContext, mAttributeSet, android.R.attr.preferenceStyle); } – John P. Jul 06 '13 at 19:51
  • Is anyone else getting `07-16 15:33:41.689: W/ResourceType(9228): Failure getting entry for 0x010802c9 (t=7 e=713) in package 0 (error -75)` – jcaruso Jul 16 '13 at 20:35
  • How to access the data set in the preference ? – 0xtuytuy Jan 08 '17 at 18:51
31

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"/>
Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
  • If I add 2 of these, after setting the 1st TimePreference, the 2nd TimePreference's time is set to the same. How can I make a difference between 2 TimePreferences? – Alex Mar 21 '16 at 16:25
  • Solved, forgot to change the key. – Alex Mar 21 '16 at 16:27
  • Works like charm! – iYonatan Dec 19 '16 at 14:28
  • Could you explain *why* this is needed, as opposed to the solutions above? – Julian Delphiki May 05 '17 at 15:56
  • 1
    @JulianDelphiki Like I said in first line of my answer [Preferences Support Library](https://developer.android.com/topic/libraries/support-library/features.html) requires different code. Other solutions work for regular Preference classes. – Dalija Prasnikar May 05 '17 at 16:25
  • 1
    More than perfect!! Used it to create a custom date picker for a date setting with Xamarin. Use also the `PreferenceManager` of the support library to set default values for example, in `PreferenceManager.SetDefaultValues(context, resId, readAgain)` – Eli Jul 01 '17 at 17:47
  • What do you mean by _`PreferenceFragmentCompat` extension class_? How can you modify a built in class? You didn't use it in your code, only implemented it. How do I implement abstract `onCreatePreferences` in `PreferencesFragment` class? – Eido95 Nov 16 '17 at 15:13
  • @Eido95 You don't modify built in class, you extend it - hence term extension, though it is a bit imprecise and ambiguous. You create descendant class (subclass) to implement different behavior. – Dalija Prasnikar Nov 16 '17 at 20:25
  • Thank you @DalijaPrasnikar, I just read [How to use the v7/v14 Preference Support library?](https://stackoverflow.com/questions/32070186/how-to-use-the-v7-v14-preference-support-library) and understood how to use `PreferenceFragmentCompat` extending class. – Eido95 Nov 20 '17 at 16:38
5

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

}
Matt Koala
  • 2,171
  • 2
  • 18
  • 14
4

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.

Omid Omidi
  • 1,670
  • 2
  • 16
  • 23
1

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>
Binoy Babu
  • 16,699
  • 17
  • 91
  • 134
1

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]);
}
}
Awais
  • 222
  • 3
  • 14
0101100101
  • 5,786
  • 6
  • 31
  • 55
0

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());
}
pmont
  • 2,083
  • 22
  • 43