-1

我在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> 

問題: 問題指出的是,onCreateDialogViewOND ialogClosed未在v7支持中找到DialogPreference

編輯: 我發現V7 PreferenceDialogFragmentCompatV7 DialogPreference使用。

回答

0

V7 PreferenceDialogFragmentCompat應配合使用V7 DialogPreference。所以使用這個response代碼可以用這種方式重構。

修改DatePrefence類:

public class DatePreference extends DialogPreference { 

    private final String TAG = getClass().getSimpleName(); 

    public String dateString; 

    public DatePreference(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public static String dateToString(Calendar calendar) { 
     return summaryFormatter().format(calendar.getTime()); 
    } 

    @Override 
    protected Object onGetDefaultValue(TypedArray a, int index) { 
     return a.getString(index); 
    } 

    @Override 
    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { 
     if (restorePersistedValue) { 
      if (defaultValue == null) dateString = getPersistedString(defaultValue()); 
      else dateString = getPersistedString(defaultValue.toString()); 
      setTheDate(dateString); 
     } else { 
      final boolean wasNull = dateString == null; 
      setDate((String) defaultValue); 
      if (!wasNull) { 
       persistStringValue(dateString); 
      } 
     } 
    } 

    @Override 
    protected Parcelable onSaveInstanceState() { 
     if (isPersistent()) { 
      return super.onSaveInstanceState(); 
     } else { 
      return new SavedState(super.onSaveInstanceState()); 
     } 
    } 

    @Override 
    protected void onRestoreInstanceState(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(TAG, "Error casting class to date preference " + Log.getStackTraceString(e)); 
      } 
     } else { 
      SavedState s = (SavedState) state; 
      super.onRestoreInstanceState(s.getSuperState()); 
      setTheDate(s.dateValue); 
     } 
    } 

    private String defaultValue() { 
     if (this.dateString == null) { 
      setDate(defaultCalendarString()); 
     } 
     return this.dateString; 
    } 

    public static String defaultCalendarString() { 
     return formatter().format(defaultCalendar().getTime()); 
    } 

    public static SimpleDateFormat formatter() { 
     return new SimpleDateFormat("yyyy.MM.dd", Locale.getDefault()); 
    } 

    public static SimpleDateFormat summaryFormatter() { 
     return new SimpleDateFormat("MMMM dd, yyyy", Locale.getDefault()); 
    } 

    public static Calendar defaultCalendar() { 
     return new GregorianCalendar(1900, 0, 1); 
    } 

    public void setTheDate(String s) { 
     setDate(s); 
     persistStringValue(s); 
    } 

    public void persistStringValue(String value) { 
     persistString(value); 
     setSummary(summaryFormatter().format(getDate().getTime())); 
    } 

    public void setDate(String dateString) { 
     this.dateString = dateString; 
    } 

    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(); 
     } 
    } 

    private static class SavedState extends Preference.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); 
     } 

    } 

} 

DatePreferenceDialogFragmentCompat類:

public class DatePreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat implements DialogPreference.TargetFragment, DatePicker.OnDateChangedListener { 

    DatePicker datePicker = null; 
    private String changedValueCanBeNull; 
    private Calendar maxCalendar; 
    private int maxYear; 
    private int maxMonth; 
    private int maxDay; 

    @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
    @Override 
    protected View onCreateDialogView(Context context) { 
     datePicker = new DatePicker(context); 

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
      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); 
     } 

     DatePreference pref = (DatePreference) getPreference(); 
     Calendar calendar = pref.getDate(); 
     datePicker.init(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), this); 
     return datePicker; 
    } 

    @Override 
    protected void onBindDialogView(View view) { 
     super.onBindDialogView(view); 
     DatePreference pref = (DatePreference) getPreference(); 
     String value = DatePreference.dateToString(pref.getDate()); 
     if (pref.callChangeListener(value)) pref.persistStringValue(value); 
    } 

    @Override 
    public void onDialogClosed(boolean positiveResult) { 
     if (positiveResult && changedValueCanBeNull != null) { 
      DatePreference pref = (DatePreference) getPreference(); 
      pref.setTheDate(changedValueCanBeNull); 
      changedValueCanBeNull = null; 
     } 
    } 

    @Override 
    public void onClick(DialogInterface dialog, int which) { 
     super.onClick(dialog, which); 
     datePicker.clearFocus(); 
     onDateChanged(datePicker, datePicker.getYear(), datePicker.getMonth(), datePicker.getDayOfMonth()); 
     onDialogClosed(which == DialogInterface.BUTTON_POSITIVE); // OK? 
    } 

    @Override 
    public Preference findPreference(CharSequence charSequence) { 
     return getPreference(); 
    } 

    @Override 
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { 
     Calendar selected = new GregorianCalendar(year, monthOfYear, dayOfMonth); 
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB && selected.after(maxCalendar)) { 
      selected = new GregorianCalendar(maxYear, maxMonth, maxDay); 
     } 
     changedValueCanBeNull = DatePreference.formatter().format(selected.getTime()); 
    } 
} 

我還添加以下代碼到我的SettingsFragment延伸PreferenceFragmentCompat

@Override 
public void onDisplayPreferenceDialog(Preference preference) { 
    DialogFragment dialogFragment = null; 
    if (preference instanceof DatePreference) { 
     dialogFragment = new DatePreferenceDialogFragmentCompat(); 
     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); 
    } 
}