How to use DatePickerDialog as a Preference

Thanks to @commonsware. I followed his project and created a date picker preference dialog. So it will help someone.

Follow the steps to open date picker in preference window.

1 . Create a custom dialog preference for date picker.

package com.packagename;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.DatePicker;

public class DatePreference extends DialogPreference {
    private int lastDate = 0;
    private int lastMonth = 0;
    private int lastYear = 0;
    private String dateval;
    private CharSequence mSummary;
    private DatePicker picker = null;
    public static int getYear(String dateval) {
        String[] pieces = dateval.split("-");
        return (Integer.parseInt(pieces[0]));
    }

    public static int getMonth(String dateval) {
        String[] pieces = dateval.split("-");
        return (Integer.parseInt(pieces[1]));
    }

    public static int getDate(String dateval) {
        String[] pieces = dateval.split("-");
        return (Integer.parseInt(pieces[2]));
    }

    public DatePreference(Context ctxt, AttributeSet attrs) {
        super(ctxt, attrs);

        setPositiveButtonText("Set");
        setNegativeButtonText("Cancel");
    }

    @Override
    protected View onCreateDialogView() {
        picker = new DatePicker(getContext());

        // setCalendarViewShown(false) attribute is only available from API level 11
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            picker.setCalendarViewShown(false);
        }

        return (picker);
    }

    @Override
    protected void onBindDialogView(View v) {
        super.onBindDialogView(v);

        picker.updateDate(lastYear, lastMonth + 1, lastDate);
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
        super.onDialogClosed(positiveResult);

        if (positiveResult) {
            lastYear = picker.getYear();
            lastMonth = picker.getMonth();
            lastDate = picker.getDayOfMonth();

            String dateval = String.valueOf(lastYear) + "-"
                    + String.valueOf(lastMonth) + "-"
                    + String.valueOf(lastDate);

            if (callChangeListener(dateval)) {
                persistString(dateval);
            }
        }
    }

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

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        dateval = null;

        if (restoreValue) {
            if (defaultValue == null) {
                Calendar cal = Calendar.getInstance();
                SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
                String formatted = format1.format(cal.getTime());
                dateval = getPersistedString(formatted);
            } else {
                dateval = getPersistedString(defaultValue.toString());
            }
        } else {
            dateval = defaultValue.toString();
        }
        lastYear = getYear(dateval);
        lastMonth = getMonth(dateval);
        lastDate = getDate(dateval);
    }

    public void setText(String text) {
        final boolean wasBlocking = shouldDisableDependents();

        dateval = text;

        persistString(text);

        final boolean isBlocking = shouldDisableDependents();
        if (isBlocking != wasBlocking) {
            notifyDependencyChange(isBlocking);
        }
    }

    public String getText() {
        return dateval;
    }

    public CharSequence getSummary() {
        return mSummary;
    }

    public void setSummary(CharSequence summary) {
        if (summary == null && mSummary != null || summary != null
                && !summary.equals(mSummary)) {
            mSummary = summary;
            notifyChanged();
        }
    }
}

2 . Add the following code in preference xml located in "res/xml/yourpreference.xml"

<com.packagename.DatePreference 
android:key="keyname" 
android:title="Title of the preference" 
android:defaultValue="2014-08-01" 
android:summary="Summary"/>

Note: Change the "keyname","Title of the preference","2014-08-01","summary" as of your requirement

3 . If you want to change the default vaules through Preference Activity use the following code.

package com.packagename;

import android.os.Bundle;
import com.packagename.DatePreference;

public class CustomPreference extends PreferenceActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    addPreferencesFromResource(R.xml.preferences);

    final DatePreference dp= (DatePreference) findPreference("keyname");
    dp.setText("2014-08-02");
    dp.setSummary("2014-08-02");
    dp.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
        @Override
        public boolean onPreferenceChange(Preference preference,Object newValue) {
            //your code to change values.
            dp.setSummary((String) newValue);
            return true;
        }
    });

  }
}

Now Enjoy...


Here's an implementation ready to use in your project as a lib.

To quote the source:

Use it just like any other preference in your PreferenceScreen XML:

<org.bostonandroid.datepreference.DatePreference
      android:key="dob" android:title="@string/dob"
      android:defaultValue="1991.01.01" />

You would need to create a custom DialogPreference incorporating a DatePicker.


In androidx DialogPreference class implementation is split into DialogPreference that handles data persistence, and PreferenceDialogFragmentCompat that handles UI. Building on top of Mahendran Sakkarai's answer, this one and on EditTextPreference class as an example, it can be done like this.

1 . DatePreference class.

package com.example.util.timereminder.ui.prefs.custom;

import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;

import com.example.util.timereminder.R;

import androidx.preference.DialogPreference;

/**
 * A dialog preference that shown calendar in the dialog.
 *
 * Saves a string value.
 */
public class DatePreference extends DialogPreference {

    private String mDateValue;

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

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

    @Override
    protected void onSetInitialValue(Object defaultValue) {
        setDate(getPersistedString((String) defaultValue));
    }

    /**
     * Gets the date as a string from the current data storage.
     *
     * @return string representation of the date.
     */
    public String getDate() {
        return mDateValue;
    }

    /**
     * Saves the date as a string in the current data storage.
     *
     * @param text string representation of the date to save.
     */
    public void setDate(String text) {
        final boolean wasBlocking = shouldDisableDependents();

        mDateValue = text;

        persistString(text);

        final boolean isBlocking = shouldDisableDependents();
        if (isBlocking != wasBlocking) {
            notifyDependencyChange(isBlocking);
        }

        notifyChanged();
    }

    /**
     * A simple {@link androidx.preference.Preference.SummaryProvider} implementation for an
     * {@link DatePreference}. If no value has been set, the summary displayed will be 'Not
     * set', otherwise the summary displayed will be the value set for this preference.
     */
    public static final class SimpleSummaryProvider implements SummaryProvider<DatePreference> {

        private static SimpleSummaryProvider sSimpleSummaryProvider;

        private SimpleSummaryProvider() {}

        /**
         * Retrieve a singleton instance of this simple
         * {@link androidx.preference.Preference.SummaryProvider} implementation.
         *
         * @return a singleton instance of this simple
         * {@link androidx.preference.Preference.SummaryProvider} implementation
         */
        public static SimpleSummaryProvider getInstance() {
            if (sSimpleSummaryProvider == null) {
                sSimpleSummaryProvider = new SimpleSummaryProvider();
            }
            return sSimpleSummaryProvider;
        }

        @Override
        public CharSequence provideSummary(DatePreference preference) {
            if (TextUtils.isEmpty(preference.getDate())) {
                return (preference.getContext().getString(R.string.not_set));
            } else {
                return preference.getDate();
            }
        }
    }
}

2 . DatePreferenceDialogFragment class.

package com.example.util.timereminder.ui.prefs.custom;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.DatePicker;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import androidx.preference.PreferenceDialogFragmentCompat;

public class DatePreferenceDialogFragment extends PreferenceDialogFragmentCompat {

    private int mLastYear;
    private int mLastMonth;
    private int mLastDay;
    private DatePicker mDatePicker;

    public static DatePreferenceDialogFragment newInstance(String key) {
        final DatePreferenceDialogFragment
                fragment = new DatePreferenceDialogFragment();
        final Bundle b = new Bundle(1);
        b.putString(ARG_KEY, key);
        fragment.setArguments(b);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        String dateValue = getDatePreference().getDate();

        if (dateValue == null || dateValue.isEmpty()) {
            Calendar calendar = Calendar.getInstance();
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            dateValue = df.format(calendar.getTime());
        }

        mLastYear = getYear(dateValue);
        mLastMonth = getMonth(dateValue);
        mLastDay = getDay(dateValue);
    }

    @Override
    protected View onCreateDialogView(Context context) {
        mDatePicker = new DatePicker(getContext());
        // Show spinner dialog for old APIs.
        mDatePicker.setCalendarViewShown(false);

        return mDatePicker;
    }

    @Override
    protected void onBindDialogView(View view) {
        super.onBindDialogView(view);

        mDatePicker.updateDate(mLastYear, mLastMonth - 1, mLastDay);
    }

    @Override
    public void onDialogClosed(boolean positiveResult) {
        if (positiveResult) {
            mLastYear = mDatePicker.getYear();
            mLastMonth = mDatePicker.getMonth() + 1;
            mLastDay = mDatePicker.getDayOfMonth();

            String dateVal = String.valueOf(mLastYear) + "-"
                    + String.valueOf(mLastMonth) + "-"
                    + String.valueOf(mLastDay);

            final DatePreference preference = getDatePreference();
            if (preference.callChangeListener(dateVal)) {
                preference.setDate(dateVal);
            }
        }
    }

    private DatePreference getDatePreference() {
        return (DatePreference) getPreference();
    }

    private int getYear(String dateString) {
        String[] datePieces = dateString.split("-");
        return (Integer.parseInt(datePieces[0]));
    }

    private int getMonth(String dateString) {
        String[] datePieces = dateString.split("-");
        return (Integer.parseInt(datePieces[1]));
    }

    private int getDay(String dateString) {
        String[] datePieces = dateString.split("-");
        return (Integer.parseInt(datePieces[2]));
    }
}

3 . In PreferenceFragment.

package com.example.util.timereminder.ui.prefs;

import android.os.Bundle;

import com.example.util.timereminder.R;
import com.example.util.timereminder.ui.prefs.custom.DatePreferenceDialogFragment;
import com.example.util.timereminder.ui.prefs.custom.DatePreference;

import androidx.fragment.app.DialogFragment;
import androidx.preference.EditTextPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;

/**
 * Displays different preferences.
 */
public class PrefsFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        addPreferencesFromResource(R.xml.preferences);

        initSummary(getPreferenceScreen());
    }

    @Override
    public void onDisplayPreferenceDialog(Preference preference) {
        if (preference instanceof DatePreference) {
            final DialogFragment f;
            f = DatePreferenceDialogFragment.newInstance(preference.getKey());
            f.setTargetFragment(this, 0);
            f.show(getFragmentManager(), null);
        } else {
            super.onDisplayPreferenceDialog(preference);
        }
    }

    /**
     * Walks through all preferences.
     *
     * @param p The starting preference to search from.
     */
    private void initSummary(Preference p) {
        if (p instanceof PreferenceGroup) {
            PreferenceGroup pGrp = (PreferenceGroup) p;
            for (int i = 0; i < pGrp.getPreferenceCount(); i++) {
                initSummary(pGrp.getPreference(i));
            }
        } else {
            setPreferenceSummary(p);
        }
    }

    /**
     * Sets up summary providers for the preferences.
     *
     * @param p The preference to set up summary provider.
     */
    private void setPreferenceSummary(Preference p) {
        // No need to set up preference summaries for checkbox preferences because
        // they can be set up in xml using summaryOff and summary On
        if (p instanceof DatePreference) {
            p.setSummaryProvider(DatePreference.SimpleSummaryProvider.getInstance());
        } else if (p instanceof EditTextPreference) {
            p.setSummaryProvider(EditTextPreference.SimpleSummaryProvider.getInstance());
        }
    }
}

4 . And in preference.xml. If default value left out, calendar opens on the current date.

<com.example.util.timereminder.ui.prefs.custom.DatePreference
    android:title="@string/prefs_date_of_birth_title"
    android:key="@string/prefs_date_of_birth_key"
    android:defaultValue="2014-08-01"
    app:iconSpaceReserved="false"/>

Tags:

Android