我在Android 1.6及更高版本上使用了unofficial PreferenceFragment兼容層。但谷歌已經發布了v7 Preference Support Library。所以我的想法是遷移項目庫。PreferenceFragmentCompat上的DatePicker屏幕
當前實現: 在我創建了一個自定義設置,支持日期項目。
public class DatePreference extends DialogPreference implements DatePicker.OnDateChangedListener {
private String dateString;
private String changedValueCanBeNull;
private DatePicker datePicker;
private Calendar maxCalendar;
private int maxYear;
private int maxMonth;
private int maxDay;
public DatePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected View onCreateDialogView() {
this.datePicker = new DatePicker(getContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
this.datePicker.setMaxDate(Calendar.getInstance().getTimeInMillis());
} else {
maxCalendar = Calendar.getInstance();
maxYear = maxCalendar.get(Calendar.YEAR);
maxMonth = maxCalendar.get(Calendar.MONTH);
maxDay = maxCalendar.get(Calendar.DAY_OF_MONTH);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
this.datePicker.setCalendarViewShown(false);
}
final Calendar calendar = getDate();
datePicker.init(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH),
this);
return datePicker;
}
public Calendar getDate() {
try {
final SimpleDateFormat sfd = formatter();
final Date date = sfd.parse(defaultValue());
final Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal;
} catch (ParseException e) {
return defaultCalendar();
}
}
public void setDate(final String dateString) {
this.dateString = dateString;
}
public static SimpleDateFormat formatter() {
return new SimpleDateFormat("yyyy.MM.dd", Locale.getDefault());
}
public static SimpleDateFormat summaryFormatter() {
return new SimpleDateFormat("MMMM dd, yyyy", Locale.getDefault());
}
@Override
protected Object onGetDefaultValue(final TypedArray a, final int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(final boolean restoreValue, final Object def) {
if (restoreValue) {
this.dateString = getPersistedString(defaultValue());
setTheDate(this.dateString);
} else {
final boolean wasNull = this.dateString == null;
setDate((String) def);
if (!wasNull) {
persistDate(this.dateString);
}
}
}
@Override
protected Parcelable onSaveInstanceState() {
if (isPersistent()) {
return super.onSaveInstanceState();
} else {
return new SavedState(super.onSaveInstanceState());
}
}
@Override
protected void onRestoreInstanceState(final Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
super.onRestoreInstanceState(state);
try {
if (state != null) {
setTheDate(((SavedState) state).dateValue);
}
} catch (ClassCastException e) {
SiempreListoLogger.INSTANCE.error(DatePreference.class.getSimpleName(), "Error casting class to date preference " + Log.getStackTraceString(e));
}
} else {
final SavedState s = (SavedState) state;
super.onRestoreInstanceState(s.getSuperState());
setTheDate(s.dateValue);
}
}
@Override
public void onDateChanged(final DatePicker view, final int year, final int month, final int day) {
Calendar selected = new GregorianCalendar(year, month, day);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB && selected.after(maxCalendar)) {
selected = new GregorianCalendar(maxYear, maxMonth, maxDay);
}
this.changedValueCanBeNull = formatter().format(selected.getTime());
}
@Override
protected void onDialogClosed(final boolean shouldSave) {
if (shouldSave && this.changedValueCanBeNull != null) {
setTheDate(this.changedValueCanBeNull);
this.changedValueCanBeNull = null;
}
}
private void setTheDate(final String s) {
setDate(s);
persistDate(s);
}
private void persistDate(final String s) {
persistString(s);
setSummary(summaryFormatter().format(getDate().getTime()));
}
public static Calendar defaultCalendar() {
return new GregorianCalendar(1900, 0, 1);
}
public static String defaultCalendarString() {
return formatter().format(defaultCalendar().getTime());
}
private String defaultValue() {
if (this.dateString == null) {
setDate(defaultCalendarString());
}
return this.dateString;
}
@Override
public void onClick(final DialogInterface dialog, final int which) {
super.onClick(dialog, which);
datePicker.clearFocus();
onDateChanged(datePicker, datePicker.getYear(), datePicker.getMonth(), datePicker.getDayOfMonth());
onDialogClosed(which == DialogInterface.BUTTON_POSITIVE); // OK?
}
private static class SavedState extends BaseSavedState {
private transient String dateValue;
@SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(final Parcel parcel) {
return new SavedState(parcel);
}
@Override
public SavedState[] newArray(final int size) {
return new SavedState[size];
}
};
public SavedState(final Parcel p) {
super(p);
dateValue = p.readString();
}
public SavedState(final Parcelable p) {
super(p);
}
@Override
public void writeToParcel(final Parcel parcel, final int flags) {
super.writeToParcel(parcel, flags);
parcel.writeString(dateValue);
}
}
}
並支持這種偏好我使用DatePickerFragment允許用戶選擇使用本地的DatePicker的日期。
public class DatePickerFragment extends DialogFragment {
public static final String TAG = DatePickerFragment.class.getName();
private static final String MINIMUM_DATE_TO_SHOW = "minimum_date_to_show";
private static final String DATE_TO_SHOW = "date_to_show";
private DatePickerDialog.OnDateSetListener mListener;
public static DatePickerFragment newInstance(DatePickerDialog.OnDateSetListener listener) {
DatePickerFragment newInstance = new DatePickerFragment();
newInstance.mListener = listener;
return newInstance;
}
public static DatePickerFragment newInstance(DatePickerDialog.OnDateSetListener listener, Date date) {
DatePickerFragment newInstance = new DatePickerFragment();
Bundle args = new Bundle();
args.putSerializable(MINIMUM_DATE_TO_SHOW, date);
args.putSerializable(DATE_TO_SHOW, date);
newInstance.setArguments(args);
newInstance.mListener = listener;
return newInstance;
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the current date as the default date in the picker
final Calendar c = Calendar.getInstance();
int minimumYear = c.get(Calendar.YEAR);
int minimumMonth = c.get(Calendar.MONTH);
int minimumDay = c.get(Calendar.DAY_OF_MONTH);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
if (getArguments() != null && getArguments().containsKey(MINIMUM_DATE_TO_SHOW)) {
Date date = (Date) getArguments().getSerializable(MINIMUM_DATE_TO_SHOW);
if (date != null) {
c.setTime(date);
minimumYear = c.get(Calendar.YEAR);
minimumMonth = c.get(Calendar.MONTH);
minimumDay = c.get(Calendar.DAY_OF_MONTH);
}
}
if (getArguments() != null && getArguments().containsKey(DATE_TO_SHOW)) {
Date date = (Date) getArguments().getSerializable(DATE_TO_SHOW);
if (date != null) {
c.setTime(date);
year = c.get(Calendar.YEAR);
month = c.get(Calendar.MONTH);
day = c.get(Calendar.DAY_OF_MONTH);
}
}
final Date minimumDate = Utils.getDateFromValues(minimumYear, minimumMonth, minimumDay);
// Create a new instance of DatePickerDialog and return it
final DatePickerDialog datePickerDialog = new DatePickerDialog(getActivity(), mListener, year, month, day);
datePickerDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DatePicker datePicker = datePickerDialog.getDatePicker();
datePicker.setMinDate(minimumDate.getTime());
mListener.onDateSet(datePicker, datePicker.getYear(), datePicker.getMonth(), datePicker.getDayOfMonth());
}
});
datePickerDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(android.R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DatePickerFragment.this.dismiss();
}
});
return datePickerDialog;
}
}
而且的settings.xml:
<PreferenceCategory
android:layout="@layout/preference_activity_bg"
android:title="@string/pref_user_private_info">
<com.company.project.util.DatePreference
android:defaultValue="1980.01.01"
android:icon="@drawable/ic_birthdate"
android:key="prefBirthdate"
android:title="@string/pref_user_birthdate"/>
<Preference
android:icon="@drawable/ic_email"
android:key="prefEmail"
android:summary="@string/pref_user_email"
android:title="@string/pref_user_email"/>
<EditTextPreference
android:icon="@drawable/ic_ic_telefono"
android:inputType="phone"
android:key="prefTel"
android:maxLength="10"
android:summary="@string/pref_user_phone"
android:title="@string/pref_user_phone"/>
<Preference
android:icon="@drawable/ic_points"
android:key="prefBalance"
android:title="@string/pref_user_points"/>
</PreferenceCategory>
問題: 問題指出的是,onCreateDialogView和OND ialogClosed未在v7支持中找到DialogPreference。
編輯: 我發現V7 PreferenceDialogFragmentCompat應V7 DialogPreference使用。