Custom preferences in Android that use the correct layout - android

For custom preferences, e.g. a time picker preference, I use the following XML in Android:
<com.my.package.TimePreference android:key="notification_time" android:selectable="true" android:title="#string/notification_time" android:enabled="true" android:summary="#string/setTime" android:defaultValue="00:00" />
Where the class "TimePreference" is:
package com.my.package;
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.text.format.DateFormat;
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;
private boolean is_24_hours = true;
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) {
this(ctxt, null);
}
public TimePreference(Context ctxt, AttributeSet attrs) {
this(ctxt, attrs, 0);
}
public TimePreference(Context ctxt, AttributeSet attrs, int defStyle) {
super(ctxt, attrs, defStyle);
setPositiveButtonText(ctxt.getString(R.string.save));
setNegativeButtonText(ctxt.getString(R.string.cancel));
try {
is_24_hours = DateFormat.is24HourFormat(ctxt);
}
catch (Exception e) {
is_24_hours = true;
}
}
#Override
protected View onCreateDialogView() {
picker = new TimePicker(getContext());
if (is_24_hours) {
picker.setIs24HourView(true);
}
else {
picker.setIs24HourView(false);
}
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) {
picker.clearFocus(); // important - otherwise manual input is not saved
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);
}
}
This has always worked great on Android 2.2 and 2.3 - but now that I've switched to Android 4.0 I can see that this preference does not adjust to the preference screen's layout:
How can I solve this problem? Is there a solution better than setting margin/padding manually?

Finally found the problem:
The constructor super() must be called that takes only two arguments (and no int defStyle argument).
In the code from the question, super() is called with a predefined defStyle value of 0 whcih prevents Android from choosing any nice layout. If you call super() without giving a default style argument, super class DialogPreference's constructor chooses the best style on its own.

Related

Android PreferenceManager.setDefaultValues() crashes with class cast exception

I have a frustrating problem, I have created a custom preference for Android, using the support library.
public class CustomTimePreference extends DialogPreference {
public int hour = 0;
public int minute = 0;
public CustomTimePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
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);
}
#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);
}
}
and
public class CustomTimePreferenceDialogFragmentCompat 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);
CustomTimePreference pref = (CustomTimePreference) getPreference();
timePicker.setCurrentHour(pref.hour);
timePicker.setCurrentMinute(pref.minute);
}
#Override
public void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
CustomTimePreference pref = (CustomTimePreference) getPreference();
pref.hour = timePicker.getCurrentHour();
pref.minute = timePicker.getCurrentMinute();
String value = CustomTimePreference.timeToString(pref.hour, pref.minute);
if (pref.callChangeListener(value)) pref.persistStringValue(value);
}
}
#Override
public Preference findPreference(CharSequence charSequence) {
return getPreference();
}
}
For completeness, the xml contained within the preferences.xml is:
<customcontrols.CustomTimePreference
android:key="time_pace_preference"
android:title="#string/title_time_pace_preference"
android:defaultValue="#string/settings_default_pace"
android:summary="Set some time"
/>
However, when I attempt to call
PreferenceManager.setDefaultValues(mContext, preferences, true);
I receive
java.lang.ClassCastException: customcontrols.CustomTimePreference cannot be cast to android.preference.Preference
Why is this happening? as CustomTimePreference extends DialogPreference which itself extends Preference, this should be fine?!
If I don't call the setDefaultValues() I am able to go into my settings fragment and view the custom control?
What am I doing wrong, and how do I fix it!?
If you are extending android.support.v7.preference.DialogPreference that will cause this crash.
If so, you can use android.support.v7.preference.PreferenceManager#setDefaultValues(android.content.Context, int, boolean) instead.

Validating EditTextPreference on Android

I have read the many answers on this question but my question is asking where I place the code. I am looking to validate that a number is greater than 100 in the EditTextPreference. This is the code I use to populate the preferences:
public class SettingsFrag extends PreferenceFragment{
//Override onCreate so that the code will run when the activity is started.
#Override
public void onCreate(Bundle savedInstanceState){
//Call to the super class.
super.onCreate(savedInstanceState);
//add the preferences from the XML file.
addPreferencesFromResource(R.xml.preferences);
}
}
Is it in here I add the validation or would I have to create another class?
preferences.xml:
<EditTextPreference
android:key="geofence_range"
android:title="Geofence Size"
android:defaultValue="500"
android:inputType="number"
android:summary="Geofence Size Around User Location"
android:dialogTitle="Enter Size (meters):" />
Add setOnPreferenceChangeListener for EditTextPreference after addPreferencesFromResource to validate data input for User:
EditTextPreference edit_Pref = (EditTextPreference)
getPreferenceScreen().findPreference("geofence_range");
edit_Pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// put validation here..
if(<validation pass>){
return true;
}else{
return false;
}
}
});
Note: This answer is based on the deprecated android.preference.EditTextPreference.
Huh. Another one of those things that should be so darned easy in Android but isn't. Other answers just silently prevent writing back the result to preferences, which seems a bit shoddy. (Showing toast is less shoddy, but is still shoddy).
You'll need a custom preference to do this. Customize onValidate to suit your needs.
package com.two_play.extensions;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
public class ValidatingEditTextPreference extends EditTextPreference {
public ValidatingEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public ValidatingEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ValidatingEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ValidatingEditTextPreference(Context context) {
super(context);
}
#Override
protected void showDialog(Bundle state) {
super.showDialog(state);
AlertDialog dlg = (AlertDialog)getDialog();
View positiveButton = dlg.getButton(DialogInterface.BUTTON_POSITIVE);
getEditText().setError(null);
positiveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onPositiveButtonClicked(v);
}
});
}
private void onPositiveButtonClicked(View v) {
String errorMessage = onValidate(getEditText().getText().toString());
if (errorMessage == null)
{
getEditText().setError(null);
onClick(getDialog(),DialogInterface.BUTTON_POSITIVE);
getDialog().dismiss();
} else {
getEditText().setError(errorMessage);
return; // return WITHOUT dismissing the dialog.
}
}
/***
* Called to validate contents of the edit text.
*
* Return null to indicate success, or return a validation error message to display on the edit text.
*
* #param text The text to validate.
* #return An error message, or null if the value passes validation.
*/
public String onValidate(String text)
{
try {
Double.parseDouble(text);
return null;
} catch (Exception e)
{
return getContext().getString(R.string.error_invalid_number);
}
}
}
It seems more elegant to me to disable the "OK" button instead of allowing the user to press it but then discard their input and show the error. getDialog seems to be gone in androidx.preference, but this seems to work for me instead:
final EditTextPreference p = new EditTextPreference(context);
p.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() {
#Override
public void onBindEditText(#NonNull final EditText editText) {
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable editable) {
String validationError;
try {
// ... insert your validation logic here, throw on failure ...
validationError = null; // All OK!
} catch (Exception e) {
validationError = e.getMessage();
}
editText.setError(validationError);
editText.getRootView().findViewById(android.R.id.button1)
.setEnabled(validationError == null);
}
});
}
});
Here is my Kotlin implementation for androidx.preference.* based on Vladimir Panteleev's answer:
class CustomPreference : EditTextPreference {
// ...
private fun applyValidation() = setOnBindEditTextListener { editText ->
editText.doAfterTextChanged { editable ->
requireNotNull(editable)
// TODO Add validation magic here.
editText.error = if (criteria.isValid()) {
null // Everything is fine.
} else {
if (criteria.getErrorMessage() == null) "Unknown validation error"
else resources.getString(criteria.getErrorMessage())
}
}
}
}
The handy doAfterTextChanged extension is part of the androidx.core:core-ktx library.

Create NumberPicker dialog in preference

I am trying to create a NumberPicker dialog in my preference screen. I have already made one following this:https://stackoverflow.com/a/5533295/2442638
However, for my second dialog, I only want one spinner, so I have adapted the code as follows:
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.NumberPicker;
public class SnoozeTPP extends DialogPreference {
private int Minute = 0;
private NumberPicker np= null;
public static int getMinute(String time) {
String[] pieces = time.split(":");
return (Integer.parseInt(pieces[1]));
}
public SnoozeTPP(Context context, AttributeSet attrs) {
super(context, attrs);
setPositiveButtonText("Set");
setNegativeButtonText("Cancel");
}
#Override
protected View onCreateDialogView() {
np = new NumberPicker(getContext());
return (np);
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
np.setMaxValue(60);
np.setValue(Minute);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
Minute = np.getValue();
String time = 0 + ":" + String.valueOf(Minute);
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("08:00");
} else {
time = getPersistedString(defaultValue.toString());
}
} else {
time = defaultValue.toString();
}
Minute = getMinute(time);
}
}
There are no errors and the dialog pops up correctly, but the layout of it seems to be "messed up" :-). The blue line stretch across the whole dialog instead of just the width of the numbers.
The question is - how to set the layout correctly? (I am sure there are lots of other mistakes as well!)
Thank you
I solved this by using the CyanogenMod number picker.
Java file:
https://github.com/CyanogenMod/android_packages_apps_Trebuchet/blob/cm-10.2/src/com/cyanogenmod/trebuchet/preference/NumberPickerPreference.java
XML file:
https://github.com/CyanogenMod/android_packages_apps_Trebuchet/blob/cm-10.2/res/layout/number_picker_dialog.xml
Attributes:
https://github.com/CyanogenMod/android_packages_apps_Trebuchet/blob/cm-10.2/res/values/attrs.xml#L158
Here is an example of simple, but working NumberPickerPreference, saving integer value between 1 and 100:
NumberPickerPreference.java:
public class NumberPickerPreference extends DialogPreference {
private NumberPicker mPicker;
private Integer mNumber = 0;
public NumberPickerPreference(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NumberPickerPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
}
#Override
protected View onCreateDialogView() {
mPicker = new NumberPicker(getContext());
mPicker.setMinValue(1);
mPicker.setMaxValue(100);
mPicker.setValue(mNumber);
return mPicker;
}
#Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
// needed when user edits the text field and clicks OK
mPicker.clearFocus();
setValue(mPicker.getValue());
}
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedInt(mNumber) : (Integer) defaultValue);
}
public void setValue(int value) {
if (shouldPersist()) {
persistInt(value);
}
if (value != mNumber) {
mNumber = value;
notifyChanged();
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInt(index, 0);
}
}
This is more a workaround than a solution, but i hope it helps. Adding a dummy textView solved the problem. I got exactly the same problem.
My xml File:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/textDummyEmpty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/textDummyEmpty" />
<NumberPicker
android:id="#+id/numberPicker1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
and
android:text="#string/textDummyEmpty"
is an empty String. Maybe its also enough to use just a view instead of a textView.
return a LinearLayout in onCreateDialogView rather than NumberPicker as below:
#Override
protected View onCreateDialogView() {
numberPicker = new NumberPicker(getContext());
numberPicker.setMinValue(1);
numberPicker.setMaxValue(12);
numberPicker.setWrapSelectorWheel(false);
numberPicker.setValue(lastValue);
LinearLayout.LayoutParams pickerParams = new LinearLayout.LayoutParams
(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
pickerParams.gravity = Gravity.CENTER;
numberPicker.setLayoutParams(pickerParams);
LinearLayout layout = new LinearLayout(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setLayoutParams(params);
layout.addView(numberPicker);
return layout;
//return numberPicker;
}

TimePicker in PreferenceScreen

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 ?
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);
}
}
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()));
}
}
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)
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"/>
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());
}
}
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.
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>
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]);
}
}
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());
}

How to use DatePickerDialog as a Preference

I have preferences working and I am using a combination of CheckBoxPreference and EditTextPreference. I would like to replace one of them with a DatePickerDialog.
When my settings screen is showing, if you click on one of the preferences, I would like the date picker dialog to pop up for the user to select a date, and save the picked date in preferences. I have seen this work in other apps, but I cannot see how to do this.
I have the date picker dialog working from a regular view (as per tutorial), but I would to like to use it from a preference.
Thanks to #commonsware. I followed his project and created a date picker preference dialog. So it will help someone.
Follow the steps to open date picker in preference window.
1 . Create a custom dialog preference for date picker.
package com.packagename;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.DatePicker;
public class DatePreference extends DialogPreference {
private int lastDate = 0;
private int lastMonth = 0;
private int lastYear = 0;
private String dateval;
private CharSequence mSummary;
private DatePicker picker = null;
public static int getYear(String dateval) {
String[] pieces = dateval.split("-");
return (Integer.parseInt(pieces[0]));
}
public static int getMonth(String dateval) {
String[] pieces = dateval.split("-");
return (Integer.parseInt(pieces[1]));
}
public static int getDate(String dateval) {
String[] pieces = dateval.split("-");
return (Integer.parseInt(pieces[2]));
}
public DatePreference(Context ctxt, AttributeSet attrs) {
super(ctxt, attrs);
setPositiveButtonText("Set");
setNegativeButtonText("Cancel");
}
#Override
protected View onCreateDialogView() {
picker = new DatePicker(getContext());
// setCalendarViewShown(false) attribute is only available from API level 11
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
picker.setCalendarViewShown(false);
}
return (picker);
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
picker.updateDate(lastYear, lastMonth + 1, lastDate);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
lastYear = picker.getYear();
lastMonth = picker.getMonth();
lastDate = picker.getDayOfMonth();
String dateval = String.valueOf(lastYear) + "-"
+ String.valueOf(lastMonth) + "-"
+ String.valueOf(lastDate);
if (callChangeListener(dateval)) {
persistString(dateval);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return (a.getString(index));
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
dateval = null;
if (restoreValue) {
if (defaultValue == null) {
Calendar cal = Calendar.getInstance();
SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
String formatted = format1.format(cal.getTime());
dateval = getPersistedString(formatted);
} else {
dateval = getPersistedString(defaultValue.toString());
}
} else {
dateval = defaultValue.toString();
}
lastYear = getYear(dateval);
lastMonth = getMonth(dateval);
lastDate = getDate(dateval);
}
public void setText(String text) {
final boolean wasBlocking = shouldDisableDependents();
dateval = text;
persistString(text);
final boolean isBlocking = shouldDisableDependents();
if (isBlocking != wasBlocking) {
notifyDependencyChange(isBlocking);
}
}
public String getText() {
return dateval;
}
public CharSequence getSummary() {
return mSummary;
}
public void setSummary(CharSequence summary) {
if (summary == null && mSummary != null || summary != null
&& !summary.equals(mSummary)) {
mSummary = summary;
notifyChanged();
}
}
}
2 . Add the following code in preference xml located in "res/xml/yourpreference.xml"
<com.packagename.DatePreference
android:key="keyname"
android:title="Title of the preference"
android:defaultValue="2014-08-01"
android:summary="Summary"/>
Note: Change the "keyname","Title of the preference","2014-08-01","summary" as of your requirement
3 . If you want to change the default vaules through Preference Activity use the following code.
package com.packagename;
import android.os.Bundle;
import com.packagename.DatePreference;
public class CustomPreference extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
final DatePreference dp= (DatePreference) findPreference("keyname");
dp.setText("2014-08-02");
dp.setSummary("2014-08-02");
dp.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference,Object newValue) {
//your code to change values.
dp.setSummary((String) newValue);
return true;
}
});
}
}
Now Enjoy...
Here's an implementation ready to use in your project as a lib.
To quote the source:
Use it just like any other preference in your PreferenceScreen XML:
<org.bostonandroid.datepreference.DatePreference
android:key="dob" android:title="#string/dob"
android:defaultValue="1991.01.01" />
You would need to create a custom DialogPreference incorporating a DatePicker.
In androidx DialogPreference class implementation is split into DialogPreference that handles data persistence, and PreferenceDialogFragmentCompat that handles UI. Building on top of Mahendran Sakkarai's answer, this one and on EditTextPreference class as an example, it can be done like this.
1 . DatePreference class.
package com.example.util.timereminder.ui.prefs.custom;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import com.example.util.timereminder.R;
import androidx.preference.DialogPreference;
/**
* A dialog preference that shown calendar in the dialog.
*
* Saves a string value.
*/
public class DatePreference extends DialogPreference {
private String mDateValue;
public DatePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
#Override
protected void onSetInitialValue(Object defaultValue) {
setDate(getPersistedString((String) defaultValue));
}
/**
* Gets the date as a string from the current data storage.
*
* #return string representation of the date.
*/
public String getDate() {
return mDateValue;
}
/**
* Saves the date as a string in the current data storage.
*
* #param text string representation of the date to save.
*/
public void setDate(String text) {
final boolean wasBlocking = shouldDisableDependents();
mDateValue = text;
persistString(text);
final boolean isBlocking = shouldDisableDependents();
if (isBlocking != wasBlocking) {
notifyDependencyChange(isBlocking);
}
notifyChanged();
}
/**
* A simple {#link androidx.preference.Preference.SummaryProvider} implementation for an
* {#link DatePreference}. If no value has been set, the summary displayed will be 'Not
* set', otherwise the summary displayed will be the value set for this preference.
*/
public static final class SimpleSummaryProvider implements SummaryProvider<DatePreference> {
private static SimpleSummaryProvider sSimpleSummaryProvider;
private SimpleSummaryProvider() {}
/**
* Retrieve a singleton instance of this simple
* {#link androidx.preference.Preference.SummaryProvider} implementation.
*
* #return a singleton instance of this simple
* {#link androidx.preference.Preference.SummaryProvider} implementation
*/
public static SimpleSummaryProvider getInstance() {
if (sSimpleSummaryProvider == null) {
sSimpleSummaryProvider = new SimpleSummaryProvider();
}
return sSimpleSummaryProvider;
}
#Override
public CharSequence provideSummary(DatePreference preference) {
if (TextUtils.isEmpty(preference.getDate())) {
return (preference.getContext().getString(R.string.not_set));
} else {
return preference.getDate();
}
}
}
}
2 . DatePreferenceDialogFragment class.
package com.example.util.timereminder.ui.prefs.custom;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.DatePicker;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import androidx.preference.PreferenceDialogFragmentCompat;
public class DatePreferenceDialogFragment extends PreferenceDialogFragmentCompat {
private int mLastYear;
private int mLastMonth;
private int mLastDay;
private DatePicker mDatePicker;
public static DatePreferenceDialogFragment newInstance(String key) {
final DatePreferenceDialogFragment
fragment = new DatePreferenceDialogFragment();
final Bundle b = new Bundle(1);
b.putString(ARG_KEY, key);
fragment.setArguments(b);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String dateValue = getDatePreference().getDate();
if (dateValue == null || dateValue.isEmpty()) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
dateValue = df.format(calendar.getTime());
}
mLastYear = getYear(dateValue);
mLastMonth = getMonth(dateValue);
mLastDay = getDay(dateValue);
}
#Override
protected View onCreateDialogView(Context context) {
mDatePicker = new DatePicker(getContext());
// Show spinner dialog for old APIs.
mDatePicker.setCalendarViewShown(false);
return mDatePicker;
}
#Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
mDatePicker.updateDate(mLastYear, mLastMonth - 1, mLastDay);
}
#Override
public void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
mLastYear = mDatePicker.getYear();
mLastMonth = mDatePicker.getMonth() + 1;
mLastDay = mDatePicker.getDayOfMonth();
String dateVal = String.valueOf(mLastYear) + "-"
+ String.valueOf(mLastMonth) + "-"
+ String.valueOf(mLastDay);
final DatePreference preference = getDatePreference();
if (preference.callChangeListener(dateVal)) {
preference.setDate(dateVal);
}
}
}
private DatePreference getDatePreference() {
return (DatePreference) getPreference();
}
private int getYear(String dateString) {
String[] datePieces = dateString.split("-");
return (Integer.parseInt(datePieces[0]));
}
private int getMonth(String dateString) {
String[] datePieces = dateString.split("-");
return (Integer.parseInt(datePieces[1]));
}
private int getDay(String dateString) {
String[] datePieces = dateString.split("-");
return (Integer.parseInt(datePieces[2]));
}
}
3 . In PreferenceFragment.
package com.example.util.timereminder.ui.prefs;
import android.os.Bundle;
import com.example.util.timereminder.R;
import com.example.util.timereminder.ui.prefs.custom.DatePreferenceDialogFragment;
import com.example.util.timereminder.ui.prefs.custom.DatePreference;
import androidx.fragment.app.DialogFragment;
import androidx.preference.EditTextPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
/**
* Displays different preferences.
*/
public class PrefsFragment extends PreferenceFragmentCompat {
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
initSummary(getPreferenceScreen());
}
#Override
public void onDisplayPreferenceDialog(Preference preference) {
if (preference instanceof DatePreference) {
final DialogFragment f;
f = DatePreferenceDialogFragment.newInstance(preference.getKey());
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), null);
} else {
super.onDisplayPreferenceDialog(preference);
}
}
/**
* Walks through all preferences.
*
* #param p The starting preference to search from.
*/
private void initSummary(Preference p) {
if (p instanceof PreferenceGroup) {
PreferenceGroup pGrp = (PreferenceGroup) p;
for (int i = 0; i < pGrp.getPreferenceCount(); i++) {
initSummary(pGrp.getPreference(i));
}
} else {
setPreferenceSummary(p);
}
}
/**
* Sets up summary providers for the preferences.
*
* #param p The preference to set up summary provider.
*/
private void setPreferenceSummary(Preference p) {
// No need to set up preference summaries for checkbox preferences because
// they can be set up in xml using summaryOff and summary On
if (p instanceof DatePreference) {
p.setSummaryProvider(DatePreference.SimpleSummaryProvider.getInstance());
} else if (p instanceof EditTextPreference) {
p.setSummaryProvider(EditTextPreference.SimpleSummaryProvider.getInstance());
}
}
}
4 . And in preference.xml. If default value left out, calendar opens on the current date.
<com.example.util.timereminder.ui.prefs.custom.DatePreference
android:title="#string/prefs_date_of_birth_title"
android:key="#string/prefs_date_of_birth_key"
android:defaultValue="2014-08-01"
app:iconSpaceReserved="false"/>
Just a simple way to use TimePickerFragment in the settings, it doesn't really answer your question, but it can help some guys.
Please read this before: https://developer.android.com/guide/topics/ui/settings
From "Overview" to "Preference... attributes" of course... Oo
controller.fragments
public class TimePickerFragment extends DialogFragment {
private TimePickerDialog.OnTimeSetListener onTimeSetListener;
private int hours;
private int minutes;
TimePickerFragment(TimePickerDialog.OnTimeSetListener onTimeSetListener, int hours, int minutes) {
this.onTimeSetListener = onTimeSetListener;
this.hours = hours;
this.minutes = minutes;
}
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
return new TimePickerDialog(getActivity(), R.style.dateTimePicker,
onTimeSetListener, hours, minutes, DateFormat.is24HourFormat(getActivity()));
}
}
res.values.style
<style name="dateTimePicker" parent="ThemeOverlay.MaterialComponents.Dialog">
<item name="colorAccent">#color/colorPrimary</item>
</style>
res.xml.root_preferences.xml create the xml folder in res & the file root_preferences of course Oo
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory app:title="Set time">
<Preference
app:key="set_time"
app:title="Set time"
app:summary="bla bla bla"/>
</PreferenceCategory>
</PreferenceScreen>
controller.activities.SettingsActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_activity);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.settings, new SettingsFragment())
.commit();
}
controller.fragments.SettingsFragment
public class SettingsFragment extends PreferenceFragmentCompat implements TimePickerDialog.OnTimeSetListener {
private Preference setTime;
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
getPreference();
configListener();
}
private void getPreference() {
setTime = findPreference("set_time");
}
private void configListener() {
if (setTime != null){
setTime.setOnPreferenceClickListener(preference -> {
showTimeDialog(preference);
return true;
});
}
}
private void showTimeDialog(Preference preference) {
String value = preference.getSharedPreferences().getString("set_time", "12:00");
String[] time = value.split(":");
int hours = Integer.parseInt(time[0]);
int minutes = Integer.parseInt(time[1]);
if (getFragmentManager() != null) {
new TimePickerFragment(this, hours, minutes)
.show(getFragmentManager(), getString(R.string.tag_time_picker));
}
}
#Override
public void onTimeSet(TimePicker timePicker, int h, int m) {
String time = format(Locale.getDefault(),"%02d", h) + ":" + format(Locale.getDefault(), "%02d", m);
SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().putString("set_time", time).apply();
// if you use setOnPreferenceChangeListener on it, use setTime.callChangeListener(time);
}
}
I didn't document anything because after reading the guide, you should all understand ^^
While looking for a TimePicker to use in preferences I found this thread.
I'd like to point out that there is also a TimePicker project in that repo (link).
The only problem is that the default.jardesc is not available, but can easily be made from the corresponding one in the DatePicker project.
I will show my code to select and save the date from SettingsActivity.java, applied in the Preferences fragment class. Works on every devices, from API level 16, to API level 29.
First, put this tag between <PreferenceScreen>, or between <PreferenceCategory> tag (if you have) in your "root_preferences.xml" file:
<Preference
app:key="date_pref"
app:title="Set date"
app:useSimpleSummaryProvider="true" />
And final, put this code in the SettingsActivity.java file, in the sub class named SettingsFragment:
public static class SettingsFragment extends PreferenceFragmentCompat {
SharedPreferences settings;
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
settings = requireContext().getSharedPreferences(requireContext().getPackageName() + "_preferences", Context.MODE_PRIVATE);
setPreferencesFromResource(R.xml.root_preferences, rootKey);
Preference date_pref = getPreferenceManager().findPreference("date_pref");
assert date_pref != null;
date_pref.setSummary(settings.getString("date_pref", ""));
date_pref.setOnPreferenceClickListener(datePicker);
}
Preference.OnPreferenceClickListener datePicker = new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
Calendar cal = Calendar.getInstance();
int cal_month = cal.get(Calendar.MONTH);
int cal_day = cal.get(Calendar.DAY_OF_MONTH);
int cal_year = cal.get(Calendar.YEAR);
DatePickerDialog datePickerDialog = new DatePickerDialog(requireActivity(), new DatePickerDialog.OnDateSetListener() {
#Override
public void onDateSet(DatePicker view, int year, int month, int day) {
String datePicked = day + "." + month + "." + year;
settings.edit().putString("date_pref", datePicked).apply();
preference.setSummary(datePicked);
}
}, cal_year, cal_month, cal_day);
datePickerDialog.show();
return false;
}
};
}
Don't forget this imports:
import android.app.DatePickerDialog;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.Calendar;
And don't forget to Vote Up my post :)

Categories

Resources