TimePicker в окне предпочтений

Я хотел бы создать поле предпочтений под названием Interval, и я хочу, чтобы вы могли всплывать TimePicker и устанавливать mm:ss с установленным значением с минимальным значением 00:30 и шагом 30 секунд.

Можно ли использовать TimePicker в PreferenceScreen?

Ответ 1

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

Ответ 2

Я изменил код из первого ответа:

  • хранит выбранное время в длинной форме (миллисекунды), с которой легче работать (используя Календарь), а затем строка
  • он автоматически отображает выбранное время в сводном поле в пользовательском формате (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()));
    }
} 

Ответ 3

Для тех, кому реализация пользовательского Preference не так очевидна (например, это было не для меня), вы должны добавить это к своему 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)

Ответ 4

Для Preferences Support Library необходим другой код. Для этого требуется два пользовательских класса TimePreference и TimePreferenceDialogFragmentCompat, а также метод 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"/>

Ответ 5

добавить это для сводки:

@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 и onDialogClosed.

Ответ 6

Решение 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());
    }

}

Ответ 7

Я изменил ответ 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();
    }
}

Также вам нужно добавить пользовательскую настройку, как сказал Сикора.

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

В Android 6 "текущий час" и "текущая минута" устарели. Используйте это, чтобы обеспечить совместимость с Marshmallow:

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

Как и 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());
}