I've been recently introduced to PreferenceActivity and would like to change how I handle interaction with an EditTextPreference defined in the xml.
I've put logs, toast and breakpoints over where boilerplate has overridden onListItemClick( but nothing is getting back to me. I've even tried stepping into the super class and was able to set breakpoints on the if and return successfully although they weren't ultimately trapping.
protected void onListItemClick(ListView l, View v, int position, long id) {
if (!isResumed()) {
return;
}
super.onListItemClick(l, v, position, id);
Thanks for looking
EDIT #DanielLe, here is my code:
//This isn't getting called?!
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
String selection = l.getItemAtPosition(position).toString();
Toast.makeText(this, selection, Toast.LENGTH_LONG).show();
Log.d("Activity", "onListItemClick=" + l.getItemAtPosition(position).toString());
super.onListItemClick(l, v, position, id);
}
At the risk of repeating what's gone before, one solution is to extend DialogPreferences as described in the Google guide for Android dev. It's only showing ok and cancel buttons which I believe makes it the minimum for a persisting DialogPreferences implementation:
Android: Creating custom preference
import android.content.Context;
import android.content.DialogInterface;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.DialogPreference;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
import android.widget.Button;
import android.widget.EditText;
public class ClickablePreference extends DialogPreference {
private String mNewValue;
private EditText mEditText;
public ClickablePreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDialogLayoutResource(R.layout.dir_picker_dialog);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
setDialogIcon(null);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
// When the user selects "OK", persist the new value
if (positiveResult) {
persistString(mNewValue);
}
}
private static class SavedState extends BaseSavedState {
// Member that holds the setting's value
// Change this data type to match the type saved by your Preference
String value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
// Get the current preference's value
value = source.readString(); // Change this to read the appropriate data type
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Write the preference's value
dest.writeString(value); // Change this to write the appropriate data type
}
// Standard creator object using an instance of this class
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
// Restore existing state
mNewValue = this.getPersistedString("");
} else {
// Set default state from the XML attribute
mNewValue = (String) defaultValue;
persistString(mNewValue);
}
}
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
// Check whether this Preference is persistent (continually saved)
if (isPersistent()) {
// No need to save instance state since it's persistent,
// use superclass state
return superState;
}
// Create instance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
// Set the state's value with the class member that holds current
// setting value
myState.value = mNewValue;
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
// Check whether we saved the state in onSaveInstanceState
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save the state, so call superclass
super.onRestoreInstanceState(state);
return;
}
// Cast state to custom BaseSavedState and pass to superclass
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
// Set this Preference's widget to reflect the restored state
mEditText.setText(myState.value);
}
#Override
protected void onClick() {
super.onClick();
}
#Override
public void onClick(DialogInterface dialog, int which) {
super.onClick(dialog, which);
}
}
I've yet to look at Concise way of writing new DialogPreference classes? in detail although it seems very similar to what I got from Google.
The problem with this solution, apart from its size, is that mEditText is unused and I couldn't actually grab a reference to the displayed EditText defined in xml.
Roll on Solution 2
With thanks to Android: launch a custom Preference from a PreferenceActivity
just tagged this on the end of onPostCreate from MyPreferenceActivity
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setupSimplePreferencesScreen();
Preference customPref = (Preference) findPreference("pref_do_something");
customPref.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
Log.d("Activity", "onPreferenceClick=" + preference.toString());
return true;
}
});
}
Far better :)
Related
in Settings documentation, there is an implementation of onSaveInstanceState and onRestoreInstanceState with a class called SaveSatet.
how does it work? can anyone explain to me?
private static class SavedState extends BaseSavedState {
// Member that holds the setting's value
// Change this data type to match the type saved by your Preference
int value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
// Get the current preference's value
value = source.readInt(); // Change this to read the appropriate data type
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Write the preference's value
dest.writeInt(value); // Change this to write the appropriate data type
}
// Standard creator object using an instance of this class
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};}
and the implementation:
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
// Check whether this Preference is persistent (continually saved)
if (isPersistent()) {
// No need to save instance state since it's persistent,
// use superclass state
return superState;
}
// Create instance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
// Set the state's value with the class member that holds current
// setting value
myState.value = mNewValue;
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
// Check whether we saved the state in onSaveInstanceState
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save the state, so call superclass
super.onRestoreInstanceState(state);
return;
}
// Cast state to custom BaseSavedState and pass to superclass
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
// Set this Preference's widget to reflect the restored state
mNumberPicker.setValue(myState.value);
}
I wrote my own number picker preference using android's brief guide and some googling. My question is regarding the onSaveInstanceState() method. On google's tutorial, it is suggested that we use the method isPersistent() to determine if the preference is persistent and if it is, then just return the superstate. I didn't do that because with this condition, if I swipe the number picker to a new number and then rotate the screen, the rotated version will return back to the persisted value. If I remove this condition then everything is ok. However, checking the source code of other preferences, like edittextpreference, this condition exists and the state is saved even if I change the value to an unsaved one and then rotate the screen.. Can somebody explain that please?
Here is my code:
public class NumberPreference extends DialogPreference {
private final static int DEFAULT_VALUE=R.integer.timer_def;
private final static int DEFAULT_MIN_VALUE=R.integer.timer_min_def;
private final static int DEFAULT_MAX_VALUE=R.integer.timer_max_def;
private final int min;
private final int max;
private final String time;
private int timer;
private NumberPicker numberPicker;
public NumberPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDialogLayoutResource(R.layout.number_preference);
setNegativeButtonText("Cancel");
setPositiveButtonText("OK");
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.number_preference, 0, 0);
try{
min=a.getInteger(R.styleable.number_preference_min, DEFAULT_MIN_VALUE);
max=a.getInteger(R.styleable.number_preference_max, DEFAULT_MAX_VALUE);
time=a.getString(R.styleable.number_preference_time);
}finally{
a.recycle();
}
setDialogIcon(null);
}
public void setSummary() {
super.setSummary("Every "+getTimer()+' '+time);
}
#Override
protected View onCreateView(ViewGroup parent) {
setSummary();
return super.onCreateView(parent);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
int number = numberPicker.getValue();
if (callChangeListener(number)){
timer=number;
persistInt(timer);
setSummary();
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInt(index,DEFAULT_VALUE);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
timer = getPersistedInt(DEFAULT_VALUE);
}
else{
timer =(Integer) defaultValue;
persistInt(timer);
}
}
#Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
numberPicker=(NumberPicker) view.findViewById(R.id.numpref_picker);
numberPicker.setMinValue(min);
numberPicker.setMaxValue(max);
numberPicker.setValue(timer);
}
public int getTimer() {
return getPersistedInt(DEFAULT_VALUE);
}
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
return superState;
}
final SavedState myState=new SavedState(superState);
if (numberPicker!= null) myState.value=numberPicker.getValue();
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (state==null || !state.getClass().equals(SavedState.class)){
super.onRestoreInstanceState(state);
return;
}
SavedState myState=(SavedState)state;
super.onRestoreInstanceState(myState.getSuperState());
if (numberPicker!=null)numberPicker.setValue(myState.value);
}
private static class SavedState extends BaseSavedState {
// field that holds the setting's value
int value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
// Get the current preference's value
value = source.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Write the preference's value
dest.writeInt(value);
}
// Standard creator object using an instance of this class
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
Thanks:)
without having tested it, would assume you might have to change
private final String time;
to
private String time;
and pass String.valueOf(time) as an argument into the .setSummary() method. of course, the occurrence in .onCreateView() would need to pass the value read from the preferences - or the default value as fallback, if nothing had been returned. try to make in more simple, than more complex.
First of all, sorry for my english.
I'm trying to make a custom 'day picker' dialog on my preference screen. I got the codes from Android Developer site and from Stackoverflow.
The dialog works 'well', it saves and loads the value (the layout contains a NumberPicker and a TextView). The problem is: the dialog disappears upon device rotation.
I have read several posts on this forum and other sites, but no luck. Can you help me ? I've spent hours so far to finding the issue, but I can't see how to solve it. Thanks.
The code:
package hu.test.android.demo.ui;
import hu.test.android.demo.AMdtApplication;
import hu.test.android.demo.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.NumberPicker;
public class DayPreference extends DialogPreference {
private NumberPicker picker = null;
private static int day;
public static int getDay () {
return day;
}
public DayPreference(Context ctxt, AttributeSet attrs) {
super(ctxt, attrs);
setDialogLayoutResource(R.layout.day_preference);
setPositiveButtonText (TestApplication.getContext().getResources().getString( R.string.save ));
setNegativeButtonText (TestApplication.getContext().getResources().getString( R.string.cancel ));
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView (v);
picker = (NumberPicker) v.findViewById (R.id.day_preference_number_picker);
picker.setMinValue (1);
picker.setMaxValue (30);
picker.setValue (day);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
if (callChangeListener (day)) {
day = picker.getValue();
persistInt (day);
}
setSummary(getSummary());
}
}
#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) {
day = getPersistedInt (30);
}
else {
day = getPersistedInt ((Integer) defaultValue);
}
}
else {
day = (Integer) defaultValue;
}
setSummary (getSummary());
}
#Override
public CharSequence getSummary() {
return String.valueOf ( day ) + " " + TestApplication.getContext().getResources().getString( R.string.label_day );
}
// state save & restore
#Override
protected Parcelable onSaveInstanceState () {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
return superState;
}
final SavedState myState = new SavedState (superState);
myState.value = day;
return myState;
}
#Override
protected void onRestoreInstanceState (Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
// set the value to the picker
picker.setValue (myState.value);
}
private static class SavedState extends BaseSavedState {
public int value;
public SavedState (Parcelable superState) {
super(superState);
}
public SavedState (Parcel source) {
super(source);
value = source.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(value);
}
// Standard creator object using an instance of this class
#SuppressWarnings( "unused" )
public static final Parcelable.Creator <SavedState> CREATOR = new Parcelable.Creator <SavedState> () {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray (int size) {
return new SavedState[size];
}
};
}
}
UPDATE:
Please note that the below solution is just a quick hack(and is not a recommended approach), proper solution would be to persist values during onSaveInstanceState and then getting those values after your activity is recreated.
This post is a must read to gracefully handle configuration changes.
ORIGINAL ANSWER:
Maybe this will help, add this line inside the relevant activity tag in your manifest file:
android:configChanges="orientation|screenSize"
This will ensure that onCreate is not called when the screen rotates, you say to the android OS, hey i will be handling the rotation stuff myself for this activity you don't need to start over from onCreate.
Hi im trying to save a 2d array of a custom object in a Bundle inside the method onSaveInstanceState so it saves the array of custom objects when the users rotates the screen
but i dont know how to achieve it.. If possible id like an "easy" solution... Thanks
This is the class of the custom object, i tried to add the implements Parcelable & methods but not sure if that part is correct:
package com.example.game
import android.os.Parcel;
import android.os.Parcelable;
//hacemos el implements parcelable para que se puede guardar en el savedinstance
public class NumeroCarton implements Parcelable {
protected int numero;
protected boolean numacertado;
public NumeroCarton(int numero) {
this.numacertado =false;
this.numero = numero;
}
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
public boolean isNumacertado() {
return numacertado;
}
public void setNumacertado(boolean numacertado) {
this.numacertado = numacertado;
}
////parcelable :
private NumeroCarton(Parcel in) {
numero = in.readInt();
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(numero);
}
public static final Parcelable.Creator<NumeroCarton> CREATOR
= new Parcelable.Creator<NumeroCarton>() {
public NumeroCarton createFromParcel(Parcel in) {
return new NumeroCarton(in);
}
public NumeroCarton[] newArray(int size) {
return new NumeroCarton[size];
}
};
}
Ok, Inside the activity where i have the object i want to save, its micarton, a 2d array
of the class i wrote above :
NumeroCarton[][] micarton=new NumeroCarton[5][3];
And i want to save it inside the bundle of the onSaveInstanceState method:
public void onSaveInstanceState(Bundle outInstance) {
super.onSaveInstanceState(outInstance);
outInstance.putInt(STATE_PUNTOS, puntos);
//This is my problem:
outInstance.putParcelable(STATE_MICARTONN, micarton);
//The method putParcelable(String, Parcelable)
// in the type Bundle is not //applicable for the arguments
//(String, NumeroCarton[][])
//Neither outInstance.putParcelableArray[](STATE_MICARTONN, micarton)
}
I think if you make the custom class serializable it will work.
There is another easy solution, try to convert 2nd array into csv string like this:
data11,data12,data13
data21,data22,data23
....
in this format, you can easily convert 2nd array to a string and backwards
I am trying to implement a DialogPreference with two NumberPicker objects, that restores the last changed NumberPicker values after orientation change:
public class CustomTimePreference extends DialogPreference {
public NumberPicker firstPicker, secondPicker;
private int lastHour = 0;
private int lastMinute = 15;
private int firstMaxValue;
private int tempHour;
private int tempMinute;
private int rotatedHour;
private int rotatedMinute;
private int firstMinValue = 0;
private int secondMinValue=0;
private int secondMaxValue=59;
private String headerText;
private boolean usedForApprox;
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 CustomTimePreference(Context context){
this(context, null);
}
public CustomTimePreference(Context context, AttributeSet attrs){
super(context, attrs);
init(attrs);
setDialogLayoutResource(R.layout.custom_time_preference);
setPositiveButtonText(context.getString(R.string.time_preference_set_text));
setNegativeButtonText(context.getString(R.string.time_preference_cancel_text));
}
private void init(AttributeSet attrs){
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTimePreference);
firstMaxValue = a.getInteger(R.styleable.CustomTimePreference_firstMaxValue,10);
usedForApprox = a.getBoolean(R.styleable.CustomTimePreference_usedForApproximate, false);
headerText = a.getString(R.styleable.CustomTimePreference_customTimeDialogTopText);
a.recycle();
}
public void setFirstPickerValue(int value){
firstPicker.setValue(value);
}
public void setSecondPickerValue(int value){
secondPicker.setValue(value);
}
#Override
protected View onCreateDialogView(){
Log.d("OnCreateDialogView","nanana");
View root = super.onCreateDialogView();
TextView tv = (TextView)root.findViewById(R.id.custom_time_preference_title);
tv.setText(headerText);
firstPicker = (NumberPicker)root.findViewById(R.id.time_preference_first_picker);
firstPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// TODO Auto-generated method stub
tempHour = newVal;
}
});
secondPicker = (NumberPicker)root.findViewById(R.id.time_preference_second_picker);
secondPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// TODO Auto-generated method stub
tempMinute = newVal;
}
});
if(usedForApprox){
int smallestValue = MainActivity.getShortestPeriodLength(getContext());
int second = smallestValue % 60;
second-=1;
firstPicker.setMaxValue(second);
secondPicker.setMaxValue(59);
} else {
firstPicker.setMaxValue(firstMaxValue);
secondPicker.setMaxValue(secondMaxValue);
}
firstPicker.setMinValue(firstMinValue);
secondPicker.setMinValue(secondMinValue);
return root;
}
#Override
protected void onBindDialogView(View v){
super.onBindDialogView(v);
firstPicker.setValue(lastHour);
secondPicker.setValue(lastMinute);
}
#Override
protected void onDialogClosed(boolean positiveResult){
super.onDialogClosed(positiveResult);
if(positiveResult){
lastHour = firstPicker.getValue();
lastMinute = secondPicker.getValue();
if (lastHour ==0 && lastMinute == 0){
lastMinute =1;
}
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 = tempHour = getHour(time);
lastMinute = tempMinute = getMinute(time);
}
private static class SavedState extends BaseSavedState {
// Member that holds the setting's value
// Change this data type to match the type saved by your Preference
String value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
// Get the current preference's value
value = source.readString(); // Change this to read the appropriate data type
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Write the preference's value
dest.writeString(value); // Change this to write the appropriate data type
}
// Standard creator object using an instance of this class
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
// Check whether this Preference is persistent (continually saved)
/*
if (isPersistent()) {
// No need to save instance state since it's persistent, use superclass state
return superState;
}
*/
// Create instance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
// Set the state's value with the class member that holds current setting value
myState.value = String.valueOf(tempHour) + ":" + String.valueOf(tempMinute);
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
// Check whether we saved the state in onSaveInstanceState
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save the state, so call superclass
super.onRestoreInstanceState(state);
return;
}
// Cast state to custom BaseSavedState and pass to superclass
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
// Set this Preference's widget to reflect the restored state
rotatedHour = getHour(myState.value);
rotatedMinute = getMinute(myState.value);
firstPicker.setValue(rotatedHour);
secondPicker.setValue(rotatedMinute);
}
}
There are two problems:
The app crashes, when i open my custom preference, change values on one of the pickers, and then rotate the phone from portrait to landscape. The error is NuLLpointerException and it points to the line where i try to assing the restored value to one of my NumberPicker objects.
This is more a question than a problem. I copied the BaseSavedState inner class and both onSaveInstanceState(), onRestoreInstanceState() functions from Android Developer homepage, but when i tried the app to restore values on orientation change, the phone showed the persisted values, not the latest values before orientation change. When i tried to examine the code with log messages, i discovered that my phone on SaveInstanceState isPersisted check exits the function and doesn`t operate with BaseSavedState object att all. I comented out that isPersisted check so now i can save and retrieve values from BaseSavedState object, but after that the problem nr. 1 appears. So my question is, what is the reasoning to skip the BaseSavedState object creation, if the preference is persistent. And is my decision to skip the persistent check and force the app to create BaseSavedState object a bad one?
Take a look at this implementation with 3 NumberPicker objects:
package com.bom.dom;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.NumberPicker;
import android.widget.NumberPicker.OnValueChangeListener;
public class TimePreference extends DialogPreference {
NumberPicker hoursNumberPicker;
NumberPicker minutesNumberPicker;
NumberPicker secondsNumberPicker;
int time;
int currentTime;
public TimePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onBindDialogView(View view) {
hoursNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_hours);
hoursNumberPicker.setMaxValue(24);
hoursNumberPicker.setMinValue(0);
hoursNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
updateCurrentTimeFromUI();
}
});
minutesNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_minutes);
minutesNumberPicker.setMaxValue(59);
minutesNumberPicker.setMinValue(0);
minutesNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
updateCurrentTimeFromUI();
}
});
secondsNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_seconds);
secondsNumberPicker.setMaxValue(59);
secondsNumberPicker.setMinValue(0);
secondsNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
updateCurrentTimeFromUI();
}
});
updateUI();
super.onBindDialogView(view);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
time = currentTime;
persistInt(time);
return;
}
currentTime = time;
}
private void updateCurrentTimeFromUI() {
int hours = hoursNumberPicker.getValue();
int minutes = minutesNumberPicker.getValue();
int seconds = secondsNumberPicker.getValue();
currentTime = hours * 3600 + minutes * 60 + seconds;
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
time = getPersistedInt(1);
} else {
time = (Integer) defaultValue;
persistInt(time);
}
currentTime = time;
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
Integer defaultValue = a.getInteger(index, 1);
return defaultValue;
}
private void updateUI() {
int hours = (int) (currentTime / 3600);
int minutes = ((int) (currentTime / 60)) % 60;
int seconds = currentTime % 60;
hoursNumberPicker.setValue(hours);
minutesNumberPicker.setValue(minutes);
secondsNumberPicker.setValue(seconds);
}
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
final SavedState myState = new SavedState(superState);
myState.value = currentTime;
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
currentTime = myState.value;
super.onRestoreInstanceState(myState.getSuperState());
}
private static class SavedState extends BaseSavedState {
int value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
value = source.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(value);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
The layout file is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<NumberPicker
android:id="#+id/numberpicker_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<NumberPicker
android:id="#+id/numberpicker_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<NumberPicker
android:id="#+id/numberpicker_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
I found the solution on first problem:
To avoid the NullPointerException, i had to enclose the functions, that access NumberPicker objects, with a check, that determines if those pickers are initiated. I had this problem because on my app i have multiple my custom preference instances, and when i tried to follow the data path for saving/restoring functions, i discovered, that in logcat i had twice the amount of my own messages (not only for my onscreen preference, but also for the other prefrerence that uses my custom DialogPreference class). And because, i opened only one preference, the initialisation of NumberPicker objects in other preference didn`t happened, so accessing those pickers led (if i understood correctly) to NullPointerException.
But i still would like to hear someone more experienced, that could explain the default behaviour with save/restore functions.