just learning android code here. I'm not sure what I'm missing, but I'm trying to create a custom DialogPreference
that uses a NumberPicker
which will let the user choose the year. I've tried to follow android's Settings Guide , but my chosen value does not save. It saves as long as I don't exit the app, but if I exit then it resets to the default value when I relaunch the app. What am I missing?
SettingsActivity code:
public class SettingsActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
}
public static class MyPreferenceFragment extends PreferenceFragment
{
@Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_preferences);
}
}
}
app_preferences.xml (layout for SettingsActivity)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<EditTextPreference />
<com.NsouthProductions.gradetrackerpro.SemesterPickerPreference
android:key="MY_PICKER"
android:title="@string/year_pref_str"
android:summary="Choose a year here"
android:defaultValue="2005"
/>
</PreferenceScreen>
SemesterPickerPreference (extension of DialogPreference)
public class SemesterPickerPreference extends DialogPreference {
private int year;
private NumberPicker semPick;
private int DEFAULT_VALUE;
public SemesterPickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
DEFAULT_VALUE = 2003;
setDialogLayoutResource(R.layout.semester_picker_dialog);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
setDialogIcon(null);
}
@Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
persistInt(semPick.getValue());
}
}
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
// Restore existing state
year = this.getPersistedInt(DEFAULT_VALUE);
} else {
// Set default state from the XML attribute
year = (Integer) defaultValue;
persistInt(year);
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInteger(index, DEFAULT_VALUE);
}
@Override
protected View onCreateDialogView() {
int max = 2038;
int min = 2001;
LayoutInflater inflater =
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.semester_picker_dialog, null);
semPick = (NumberPicker) view.findViewById(R.id.semester_picker);
// Initialize state
semPick.setMaxValue(max);
semPick.setMinValue(min);
semPick.setValue(this.getPersistedInt(DEFAULT_VALUE));
semPick.setWrapSelectorWheel(false);
return view;
}
// This code copied from android's settings guide.
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];
}
};
}
@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 = year;
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
semPick.setValue(myState.value);
}
}
semester_picker_dialog.xml (layout for my semester/year picker)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_horizontal" >
<NumberPicker
android:id="@+id/semester_picker"
android:layout_centerInParent="true"
android:layout_width="100dip"
android:layout_height="match_parent" />
</LinearLayout>
You're missing the android:persistent="true" XML attribute in your preference XML. That's what tells the Preference whether to actually store its value to the shared preferences. Calling PersistInt()
is apparently not enough.
I had a similar problem. I tried this android:persistent="true"
solution from @scott, but it didn't work. I did a little digging and I have found a solution in EditTextPreference
class.
package android.preference;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.EditText;
/**
* A {@link Preference} that allows for string
* input.
* <p>
* It is a subclass of {@link DialogPreference} and shows the {@link EditText}
* in a dialog. This {@link EditText} can be modified either programmatically
* via {@link #getEditText()}, or through XML by setting any EditText
* attributes on the EditTextPreference.
* <p>
* This preference will store a string into the SharedPreferences.
* <p>
* See {@link android.R.styleable#EditText EditText Attributes}.
*/
public class EditTextPreference extends DialogPreference {
/**
* The edit text shown in the dialog.
*/
private EditText mEditText;
private String mText;
public EditTextPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mEditText = new EditText(context, attrs);
// Give it an ID so it can be saved/restored
mEditText.setId(com.android.internal.R.id.edit);
/*
* The preference framework and view framework both have an 'enabled'
* attribute. Most likely, the 'enabled' specified in this XML is for
* the preference framework, but it was also given to the view framework.
* We reset the enabled state.
*/
mEditText.setEnabled(true);
}
public EditTextPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.editTextPreferenceStyle);
}
public EditTextPreference(Context context) {
this(context, null);
}
/**
* Saves the text to the {@link SharedPreferences}.
*
* @param text The text to save
*/
public void setText(String text) {
final boolean wasBlocking = shouldDisableDependents();
mText = text;
persistString(text);
final boolean isBlocking = shouldDisableDependents();
if (isBlocking != wasBlocking) {
notifyDependencyChange(isBlocking);
}
}
/**
* Gets the text from the {@link SharedPreferences}.
*
* @return The current preference value.
*/
public String getText() {
return mText;
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
EditText editText = mEditText;
editText.setText(getText());
ViewParent oldParent = editText.getParent();
if (oldParent != view) {
if (oldParent != null) {
((ViewGroup) oldParent).removeView(editText);
}
onAddEditTextToDialogView(view, editText);
}
}
/**
* Adds the EditText widget of this preference to the dialog's view.
*
* @param dialogView The dialog view.
*/
protected void onAddEditTextToDialogView(View dialogView, EditText editText) {
ViewGroup container = (ViewGroup) dialogView
.findViewById(com.android.internal.R.id.edittext_container);
if (container != null) {
container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
String value = mEditText.getText().toString();
if (callChangeListener(value)) {
setText(value);
}
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setText(restoreValue ? getPersistedString(mText) : (String) defaultValue);
}
@Override
public boolean shouldDisableDependents() {
return TextUtils.isEmpty(mText) || super.shouldDisableDependents();
}
/**
* Returns the {@link EditText} widget that will be shown in the dialog.
*
* @return The {@link EditText} widget that will be shown in the dialog.
*/
public EditText getEditText() {
return mEditText;
}
/** @hide */
@Override
protected boolean needInputMethod() {
// We want the input method to show, if possible, when dialog is displayed
return true;
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
final SavedState myState = new SavedState(superState);
myState.text = getText();
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
setText(myState.text);
}
private static class SavedState extends BaseSavedState {
String text;
public SavedState(Parcel source) {
super(source);
text = source.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(text);
}
public SavedState(Parcelable superState) {
super(superState);
}
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];
}
};
}
}
First of all, you do not save state in your onRestoreInstanceState
method, you just setting text to your graphic semPick.setValue(myState.value);
. Also, check methods onDialogClosed
and onSetInitialValue
.
Tested and working.
How about you move the following line :
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
out of the oncreate method of SettingsActivity. You want to call that line of code after you have set the preference? You can do that either on exit of the settings activity, or, making use of setOnPreferenceChangeListener (depending on the type of preference). But for the numberpicker control, I would use the former
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.