Android TimePicker in PreferenceScreen -> read the values - android

I have a custom DialogPreference. The Dialog is called from a PreferenceScreen.
All works fine, the Dialog starts and shows the TimePicker.
But how do I get the selected values?
First of all, I tried to write the selected hours in the summary of the Preference. (therefore the var xxx :)
Later on, I want to save the values in SharedPreferences.
This is what I have for now:
public class Calendar extends DialogPreference implements
TimePicker.OnTimeChangedListener {
TimePicker tp;
int xxx;
public Calendar(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public Calendar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
private void initialize() {
setPersistent(true);
}
#Override
protected View onCreateDialogView() {
tp = new TimePicker(getContext());
tp.setIs24HourView(true);
return tp;
}
#Override
public void onTimeChanged(TimePicker arg0, int arg1, int arg2) {
}
#Override
public void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
// getEditor().
setTitle(getTitle());
setSummary(Integer.toString(xxx));
}
}
private TimePicker.OnTimeChangedListener mTimeSetListener =
new TimePicker.OnTimeChangedListener() {
#Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
xxx=hourOfDay;
}
};
}
Thanks a lot and best regards

Thanks for asking this question, it provided me with an important answer on how to create a DialogPreference.
I hope I might also have an answer for you. I modified your code a little bit and I can now store the time selected from the Dialog:
#Override
protected View onCreateDialogView() {
this.tp = new TimePicker(getContext());
this.tp.setIs24HourView(true);
final String storedValue = getPersistedString("07:00");
final String[] split = storedValue.split(":");
this.tp.setCurrentHour(Integer.parseInt(split[0]));
this.tp.setCurrentMinute(Integer.parseInt(split[1]));
return this.tp;
}
#Override
public void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
final String result = this.tp.getCurrentHour() + ":" + this.tp.getCurrentMinute();
persistString(result);
}
}
When the dialog is shown I retrieve the stored value and simply set the currentHour and currentMinute fields of the TimePicker. The other way round when the dialog is closed. Since I control both the format on the way in as well as on the way out there should not be a problem with illegal values.
Was this what you were looking for?

To store the value in shared pref, implement on preference Change Listener.
note that preference should be default Shared preference
preferences = PreferenceManager.getDefaultSharedPreferences(context);
editor = preferences.edit();
inside onprefchange:
if (preference.getKey().equals(getString(R.string.reminder_end_time_key))){
editor.putString("End_Date", String.valueOf(newValue));
editor.apply();
endTimePickerPreference.setSummary((CharSequence)newValue);
}

Related

SwitchButton (in custom view) value is not checked after rotation

I need to create custom view - TextView and Switch button.
I have custom view:
public class CustomTextWithSwitch extends LinearLayout implements View.OnClickListener {
private CustomTextWithSwitchBinding binding;
private boolean defaultValue;
public CustomTextWithSwitch(Context context) {
this(context, null);
}
public CustomTextWithSwitch(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomTextWithSwitch(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
binding = CustomTextWithSwitchBinding.inflate(LayoutInflater.from(getContext()), this);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTextWithSwitch);
defaultValue = a.getBoolean(R.styleable.CustomTextWithSwitch_defaultValue, false);
a.recycle();
}
#Override
protected void onFinishInflate() {
...
binding.sToggle.setChecked(defaultValue);
super.setOnClickListener(this);
super.onFinishInflate();
}
public void toggle() {
binding.sToggle.toggle();
defaultValue = binding.sToggle.isChecked();
}
#Override
public void onClick(View view) {
toggle();
}
public void setDefaultValue(boolean defaultValue) {
this.defaultValue = defaultValue;
binding.sToggle.setChecked(defaultValue);
}
}
I use that in the activity:
public class MyActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
myCustomView.setDefaultValue(true);
}
}
When I open this activity, everything works as expected - switch button is checked. However, when I rotate the screen, it is checked to false. Can somebody advise what am I doing incorrectly? Note: I use ViewModel and the value is restored correctly. However, the Switch button in this custom view is not toggled.
UPDATE: This issue happens only when I have another CustomTextWithSwich in my activity, so it means they have effect on each other(it seems that default value is set according to second View). Is it possible to separate them, so they are standalone?
I suggest you to use a ViewModdel. During rotation it is normal that you will lost the states. ViewModel will not be destroyed during app rotation so the last state of CustomTextWithSwich will be not lost.
On the other hand you can override onConfigurationChanged method every time app is rotated this method will run, so you can use it to set your data.
#Override
public void onConfigurationChanged(#NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
Please check whether both of your CustomTextWithSwitch and its children on Activity has android:ids. Ids should be different. In this case Android take care about saving instance state for your view.
If it does not save your state, use this code inside your CustomTextWithSwitch
#Override
public Parcelable onSaveInstanceState()
{
Bundle bundle = new Bundle();
bundle.putParcelable("superState", super.onSaveInstanceState());
bundle.putBoolean("isChecked", defaultValue); // ... save check state
return bundle;
}
#Override
public void onRestoreInstanceState(Parcelable state)
{
if (state instanceof Bundle)
{
Bundle bundle = (Bundle) state;
defaultValue = bundle.getBoolean("isChecked"); // ... load state
binding.sToggle.setChecked(defaultValue);
state = bundle.getParcelable("superState");
}
super.onRestoreInstanceState(state);
}
Have faced the same issue. I fixed it by setting setSaveEnabled(false) on the switch view. Just let the viewModel restore it's state.
public CustomTextWithSwitch(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
binding = CustomTextWithSwitchBinding.inflate(LayoutInflater.from(getContext()), this);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTextWithSwitch);
defaultValue = a.getBoolean(R.styleable.CustomTextWithSwitch_defaultValue, false);
a.recycle();
//Add this (assuming your switch view id is 'switch_view')
binding.switchView.setSaveEnabled(false);
}

Appcompatactivity with custom native (not compatibility) dialogpreference containing a TimePicker

I am building a preferences / settings screen inside an Android AppCompatActivity. One requirement is a custom [DialogPreference][1] with a TimePicker.
The DialogPreference must be 'native', meaning not the compatibility version like described here and here.
The code for the AppCompatActivity:
...
public class SettingsActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preferences);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_settings);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
The layout of activity_preferences.xml:
...
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<fragment
android:name="nl.waywayway.broodkruimels.SettingsFragment"
android:id="#+id/settings_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.NestedScrollView>
The SettingsFragment class:
...
public class SettingsFragment extends PreferenceFragment
{
Context mContext;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
The preferences.xml file:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SwitchPreference
android:key="pref_notify"
android:title="#string/pref_notify"
android:summary="#string/pref_notify_summ"
android:defaultValue="false" />
<nl.waywayway.broodkruimels.TimePreference
android:dependency="pref_notify"
android:key="pref_notify_time"
android:title="#string/pref_notify_time"
android:summary="#string/pref_notify_time_summ"
android:defaultValue="390" />
</PreferenceScreen>
And the custom TimePreference class:
public class TimePreference extends DialogPreference
{
private TimePicker mTimePicker = null;
private int mTime;
private int mDialogLayoutResId = R.layout.preferences_timepicker_dialog;
// 4 constructors for the API levels,
// calling each other
public TimePreference(Context context)
{
this(context, null);
}
public TimePreference(Context context, AttributeSet attrs)
{
this(context, attrs, R.attr.preferenceStyle);
}
public TimePreference(Context context, AttributeSet attrs, int defStyleAttr)
{
this(context, attrs, defStyleAttr, defStyleAttr);
}
public TimePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
}
public int getTime()
{
return mTime;
}
public void setTime(int time)
{
mTime = time;
// Save to Shared Preferences
persistInt(time);
}
#Override
public int getDialogLayoutResource()
{
return mDialogLayoutResId;
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index)
{
// Default value from attribute. Fallback value is set to 0.
return a.getInt(index, 0);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
{
// Read the value. Use the default value if it is not possible.
setTime(restorePersistedValue ?
getPersistedInt(mTime) : (int) defaultValue);
}
#Override
protected void onBindDialogView(View view)
{
super.onBindDialogView(view);
mTimePicker = (TimePicker) view.findViewById(R.id.preferences_timepicker);
if (mTimePicker == null)
{
throw new IllegalStateException("Dialog view must contain a TimePicker with id 'preferences_timepicker'");
}
// Get the time from the related Preference
Integer minutesAfterMidnight = null;
TimePreference preference = (TimePreference) findPreferenceInHierarchy("pref_notify_time");
minutesAfterMidnight = preference.getTime();
// Set the time to the TimePicker
if (minutesAfterMidnight != null)
{
int hours = minutesAfterMidnight / 60;
int minutes = minutesAfterMidnight % 60;
boolean is24hour = DateFormat.is24HourFormat(getContext());
mTimePicker.setIs24HourView(is24hour);
if (Build.VERSION.SDK_INT >= 23)
{
mTimePicker.setHour(hours);
mTimePicker.setMinute(minutes);
}
else
{
mTimePicker.setCurrentHour(hours);
mTimePicker.setCurrentMinute(minutes);
}
}
}
#Override
protected void onDialogClosed(boolean positiveResult)
{
if (positiveResult)
{
// Get the current values from the TimePicker
int hours;
int minutes;
if (Build.VERSION.SDK_INT >= 23)
{
hours = mTimePicker.getHour();
minutes = mTimePicker.getMinute();
}
else
{
hours = mTimePicker.getCurrentHour();
minutes = mTimePicker.getCurrentMinute();
}
// Generate value to save
int minutesAfterMidnight = (hours * 60) + minutes;
// Save the value
TimePreference timePreference = (TimePreference) findPreferenceInHierarchy("pref_notify_time");
// This allows the client to ignore the user value.
if (timePreference.callChangeListener(minutesAfterMidnight))
{
// Save the value
timePreference.setTime(minutesAfterMidnight);
}
}
}
}
The preferences_timepicker_dialog.xml file is as follows:
...
<TimePicker
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/preferences_timepicker"
android:layout_width="match_parent"
android:layout_height="match_parent" />
The result is like the screenshot below. On a Moto G5 plus phone with Android 7.
Question:
There should be two preferences. However, the custom DialogPreference is not showing in the settings list.
What is going wrong here?
Does the AppCompatActivity actually work with the 'native' DialogPreference?
The TimePreference class is actually instantiated from the preferences xml, that could be logged from the constructor. Also no compile time errors, no runtime errors.
Finally I found a different approach that looks clean, tested and works on real devices from Android 4 until 7. The Preference is showing in the Preference screen.
Also, the TimePicker dialog is properly showing in landscape orientation. This is a problem on some devices. See
Android TimePicker not displayed well on landscape mode
TimePickerDialog widget in landscape mode (PreferenceScreen)
Steps are:
Include a general Preference item in the preferences xml file
Set a click listener on this Preference using onPreferenceTreeClick()
When this Preference is clicked, show a regular (not compatibility library) TimePickerDialog
like described in the 'Pickers' guide
(https://developer.android.com/reference/android/app/TimePickerDialog.html)
Save the time set on the TimePicker manually in the SharedPreferences
The preferences Activity:
...
public class SettingsActivity extends AppCompatActivity
{
public static final String KEY_PREF_NOTIFY = "pref_notify";
public static final String KEY_PREF_NOTIFY_TIME = "pref_notify_time";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preferences);
}
}
The layout file activity_preferences.xml:
...
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<fragment
android:name="nl.waywayway.broodkruimels.SettingsFragment"
android:id="#+id/settings_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.NestedScrollView>
The SettingsFragment class:
...
public class SettingsFragment extends PreferenceFragmentCompat
{
Context mContext;
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
{
setPreferencesFromResource(R.xml.preferences, rootKey);
}
// The Context object of this fragment is only available when this fragment is 'attached', so set the Context object inside the onAttach() method
#Override
public void onAttach(Context context)
{
super.onAttach(context);
mContext = context;
}
// This method sets the action of clicking the Preference
#Override
public boolean onPreferenceTreeClick(Preference preference)
{
switch (preference.getKey())
{
case SettingsActivity.KEY_PREF_NOTIFY_TIME:
showTimePickerDialog(preference);
break;
}
return super.onPreferenceTreeClick(preference);
}
private void showTimePickerDialog(Preference preference)
{
DialogFragment newFragment = new TimePickerFragment();
newFragment.show(getFragmentManager(), "timePicker");
}
}
The preferences.xml file:
...
<android.support.v7.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.preference.SwitchPreferenceCompat
android:key="pref_notify"
android:title="#string/pref_notify"
android:summary="#string/pref_notify_summ"
android:defaultValue="false" />
<android.support.v7.preference.Preference
android:dependency="pref_notify"
android:key="pref_notify_time"
android:title="#string/pref_notify_time"
android:summary="#string/pref_notify_time_summ"
android:defaultValue="390" />
</android.support.v7.preference.PreferenceScreen>
The TimePickerFragment class, see the Android 'Pickers' guide (https://developer.android.com/guide/topics/ui/controls/pickers.html) for an explanation:
...
public class TimePickerFragment extends DialogFragment
implements TimePickerDialog.OnTimeSetListener
{
private Context mContext;
private int mTime; // The time in minutes after midnight
// The Context object for this fragment is only available when this fragment is 'attached', so set the Context object inside the onAttach() method
#Override
public void onAttach(Context context)
{
super.onAttach(context);
mContext = context;
}
// Getter and setter for the time
public int getTime()
{
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
int prefDefault = mContext.getResources().getInteger(R.integer.preferences_time_default);
mTime = sharedPref.getInt(SettingsActivity.KEY_PREF_NOTIFY_TIME, prefDefault);
return mTime;
}
public void setTime(int time)
{
mTime = time;
// Save to Shared Preferences
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(SettingsActivity.KEY_PREF_NOTIFY_TIME, time);
editor.commit();
}
public Dialog onCreateDialog(Bundle savedInstanceState)
{
int minutesAfterMidnight = getTime();
int hour = minutesAfterMidnight / 60;
int minute = minutesAfterMidnight % 60;
Log.i("HermLog", "onCreateDialog(), tijd: " + hour + ":" + minute);
// Create a new instance of TimePickerDialog and return it
return new TimePickerDialog(
mContext,
this,
hour,
minute,
DateFormat.is24HourFormat(mContext)
);
}
#Override
public void onTimeSet(TimePicker view, int hour, int minute)
{
int minutesAfterMidnight = (hour * 60) + minute;
setTime(minutesAfterMidnight);
}
}

PersistInt slowing down button response in custom preference

I've created a custom preference that embeds two buttons (here I have subclassed Button as FastButton). The problem is executing the persistInt to store the preference drastically slows down the response of the button.
I had the notion of only executing the persistInt when the preference's lifecycle is ended, but could not find an appropriate method to override (i.e. there is nothing like onPause() for the Preference class).
I was also unsuccessful at trying to use AsyncTask to move the persistInt off of the UI thread.
Any suggestions about how I should go about mitigating the effect of persistInt on my UI response?
public final class StepperPreference extends Preference {
public int mCurrentValue = 1;
public int maxValue = Integer.MAX_VALUE;
public int minValue = Integer.MIN_VALUE;
private TextView mText;
private FastButton plusButton;
private FastButton minusButton;
public StepperPreference(Context context) {
super(context);
}
public StepperPreference(Context context, AttributeSet attrs) {
super(context, attrs);
parseCustomAttributes(attrs);
}
public StepperPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
parseCustomAttributes(attrs);
}
public void setmCurrentValue(int value) {
if (mCurrentValue != value) {
mCurrentValue = value;
persistInt(mCurrentValue);
}
}
private void parseCustomAttributes(AttributeSet attrs) {
int maxValueAttrInt=Integer.MAX_VALUE;
int minValueAttrInt=Integer.MIN_VALUE;
if (attrs!=null) {
TypedArray a=getContext()
.obtainStyledAttributes(attrs,
R.styleable.StepperPreference,
0, 0);
maxValueAttrInt = a.getInt(R.styleable.StepperPreference_maxValue, Integer.MAX_VALUE);
minValueAttrInt = a.getInt(R.styleable.StepperPreference_minValue, Integer.MIN_VALUE);
a.recycle();
}
if (maxValueAttrInt > minValueAttrInt) {
maxValue = maxValueAttrInt;
minValue = minValueAttrInt;
}
}
#Override
protected View onCreateView(ViewGroup parent) {
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = (View) li.inflate(R.layout.stepper_preference, parent, false);
mText = (TextView) view.findViewById(R.id.text_view);
Context context = getContext();
int localDefaultValue = 0;
mCurrentValue = getPersistedInt(localDefaultValue);
mText.setText(Integer.toString(mCurrentValue));
plusButton = (FastButton) view.findViewById(R.id.plus_button);
plusButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mCurrentValue < maxValue) {
mCurrentValue++;
mText.setText(Integer.toString(mCurrentValue));
persistInt(mCurrentValue);
}
}
});
minusButton = (FastButton) view.findViewById(R.id.minus_button);
minusButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mCurrentValue > minValue) {
mCurrentValue--;
mText.setText(Integer.toString(mCurrentValue));
persistInt(mCurrentValue);
}
}
});
return view;
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
int localDefaultValue = 0;
Object result = a.getInt(index, localDefaultValue);
return result;
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
int localDefaultValue = 0;
setmCurrentValue(restoreValue ? this.getPersistedInt(localDefaultValue) : (int) defaultValue);
}
}
CheckBoxPreference, via its TwoStatePreference superclass, uses persistBoolean() for saving the preference value, much as you are using persistInt(). I do not perceive significant latency in the processing of the CheckBox. This means one of two things:
I'm a troglodyte and am incapable of seeing obvious delays in animations and such
CheckBoxPreference does not exhibit the problems that you are seeing in your StepperPreference
note: these two possibilities are not mutually exclusive
If we assume #2 to be correct, then there's something else afoot. Method tracing, to see where you are spending time "downstream" from persistInt(), may prove useful for determining what is different about StepperPreference.
From your comment, you had a listener responding to preference changes, and that was what was causing the sluggish response. "Inline" preferences, like CheckBoxPreference and StepperPreference, will be somewhat more "twitchy" than DialogPreference subclasses like ListPreference, simply because it takes less work to change the preference state (e.g., one screen tap versus 2+). As a result, listeners need to be cheaper. For example, you might hold off on doing significant work until the user has left the PreferenceFragment and so you know that the preference values are likely to be stable for at least a second or so.

Handling long values on custom preferences

I created a custom Preference for my Android App, a number-picker preference to be exact. It's really a shame that Android don't provide already a built-in preference for that, but we need to deal with it so I created one and since I could re-use that code in future projects I decided to make it using long values, so it could handle very large numbers, but I found something very curious.
You can store long values on the SharedPreferences but there isn't any getLong() on a TypedArray which is used to access attribute values on Android. So the work-around that I made was to get the values from the TypedArray as Strings and convert them to long. But I'm wondering if there is a better approach
Here I leave you the code snippet, feel free to use it in your projects, it's not using the NumberPicker Widget, it's built with a TextView and two buttons so you can use it on old devices.
public class NumberPickerPreference extends DialogPreference {
private long max;
private long min;
private long value;
private TextView picker;
private Button btnUp;
private Button btnDown;
private int step;
private long defValue;
public NumberPickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setPersistent(false);
setDialogLayoutResource(R.layout.number_picker);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(R.string.cancel);
TypedArray numberPickerType=context.obtainStyledAttributes(attrs,
R.styleable.NumberPickerPreference, 0, 0);
String maxStr = numberPickerType.getString(R.styleable.NumberPickerPreference_max);
if(maxStr==null) {
maxStr="50";
}
String minStr=numberPickerType.getString(R.styleable.NumberPickerPreference_min);
if(minStr==null) {
minStr="5";
}
step=numberPickerType.getInt(R.styleable.NumberPickerPreference_step, 1);
max=Long.parseLong(maxStr);
min=Long.parseLong(minStr);
numberPickerType.recycle();
}
#Override
protected void onBindDialogView(View v) {
picker=(TextView)v.findViewById(R.id.tvNumUpDown);
btnUp=(Button)v.findViewById(R.id.btnUp);
btnDown=(Button)v.findViewById(R.id.btnDown);
btnUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
add();
}
});
btnDown.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
subs();
}
});
value=getSharedPreferences().getLong(getKey(), defValue);
picker.setText(value+"");
super.onBindDialogView(v);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue,
Object defaultValue) {
long aux=min;
if(defaultValue!=null && !defaultValue.toString().isEmpty()) {
aux=Long.parseLong(defaultValue.toString());
}
defValue=(restorePersistedValue?getPersistedLong(min):aux);
}
/**
* You can provide a default value with the android:defaultValue attribute here
*/
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
protected void subs() {
if(value>min) {
value-=step;
picker.setText(value+"");
}
}
protected void add() {
if(value<max) {
value+=step;
picker.setText(value+"");
}
}
#Override
protected void onDialogClosed(boolean positiveResult) {
if(positiveResult) {
getEditor().putLong(getKey(), value).commit();
}
}
}
This is quite an old question, but I will give an answer for the benefit of future readers.
Converting from a String as you have done here is a fine approach. Ultimately that is what DialogPreference would be doing anyway. The attribute in XML is a String, so whether you convert it, or it is converted for you, the data will be coming from a parsed String. Just ensure when you are making the conversion you handle invalid data / NumberFormatExceptions properly.

Custom preferences in Android that use the correct layout

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.

Categories

Resources