Android DatePicker change to only Month and Year
Solution 1:
As i recently stumbled across this problem myself, i tested multiple solutions from this post and similar questions on Stackoverflow.
Unfortunately i found no working solution especially for Android 5+
I ended up with implementing my own simple DialogFragment embedding two NumberPickers. This should be compatible with all versions from 3.0 and upwards.
Here is the code:
public class MonthYearPickerDialog extends DialogFragment {
private static final int MAX_YEAR = 2099;
private DatePickerDialog.OnDateSetListener listener;
public void setListener(DatePickerDialog.OnDateSetListener listener) {
this.listener = listener;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
Calendar cal = Calendar.getInstance();
View dialog = inflater.inflate(R.layout.date_picker_dialog, null);
final NumberPicker monthPicker = (NumberPicker) dialog.findViewById(R.id.picker_month);
final NumberPicker yearPicker = (NumberPicker) dialog.findViewById(R.id.picker_year);
monthPicker.setMinValue(0);
monthPicker.setMaxValue(11);
monthPicker.setValue(cal.get(Calendar.MONTH));
int year = cal.get(Calendar.YEAR);
yearPicker.setMinValue(year);
yearPicker.setMaxValue(MAX_YEAR);
yearPicker.setValue(year);
builder.setView(dialog)
// Add action buttons
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
listener.onDateSet(null, yearPicker.getValue(), monthPicker.getValue(), 0);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MonthYearPickerDialog.this.getDialog().cancel();
}
});
return builder.create();
}
}
And the layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<NumberPicker
android:id="@+id/picker_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp">
</NumberPicker>
<NumberPicker
android:id="@+id/picker_year"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</NumberPicker>
</LinearLayout>
</LinearLayout>
To show the layout use:
MonthYearPickerDialog pd = new MonthYearPickerDialog();
pd.setListener(this);
pd.show(getFragmentManager(), "MonthYearPickerDialog");
Solution 2:
I don't recommend using Reflection to do this kind of thing.
There is a simpler and more pretty way to do so:
((ViewGroup) datePickerDialog.getDatePicker()).findViewById(Resources.getSystem().getIdentifier("day", "id", "android")).setVisibility(View.GONE);
Be aware that .getDatePicker()
method from DatePickerDialog
works on API LEVEL >= 11
.
In addition it does not work on API LEVEL >= 21.
Solution 3:
Try the following code. It will show a DatePicker
with only the year and month (without day)
private DatePickerDialog createDialogWithoutDateField() {
DatePickerDialog dpd = new DatePickerDialog(this, null, 2014, 1, 24);
try {
java.lang.reflect.Field[] datePickerDialogFields = dpd.getClass().getDeclaredFields();
for (java.lang.reflect.Field datePickerDialogField : datePickerDialogFields) {
if (datePickerDialogField.getName().equals("mDatePicker")) {
datePickerDialogField.setAccessible(true);
DatePicker datePicker = (DatePicker) datePickerDialogField.get(dpd);
java.lang.reflect.Field[] datePickerFields = datePickerDialogField.getType().getDeclaredFields();
for (java.lang.reflect.Field datePickerField : datePickerFields) {
Log.i("test", datePickerField.getName());
if ("mDaySpinner".equals(datePickerField.getName())) {
datePickerField.setAccessible(true);
Object dayPicker = datePickerField.get(datePicker);
((View) dayPicker).setVisibility(View.GONE);
}
}
}
}
}
catch (Exception ex) {
}
return dpd;
}
This method returns a date picker dialog. So , in your button's onClick
method add the following code to display your dialog.
createDialogWithoutDateField().show();
Solution 4:
More Advance form of Stephan Klein Answer .
As I have a requirement to make Year optional. And I have also handled date like february 28 and also handle leap year.
MonthYearPickerDialog
public class MonthYearPickerDialog extends DialogFragment {
private DatePickerDialog.OnDateSetListener listener;
private int daysOfMonth = 31;
private NumberPicker monthPicker;
private NumberPicker yearPicker;
private NumberPicker dayPicker;
private Calendar cal = Calendar.getInstance();
public static final String MONTH_KEY = "monthValue";
public static final String DAY_KEY = "dayValue";
public static final String YEAR_KEY = "yearValue";
int monthVal = -1 , dayVal = -1 , yearVal =-1 ;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getArguments();
if(extras != null){
monthVal = extras.getInt(MONTH_KEY , -1);
dayVal = extras.getInt(DAY_KEY , -1);
yearVal = extras.getInt(YEAR_KEY , -1);
}
}
public static MonthYearPickerDialog newInstance(int monthIndex , int daysIndex , int yearIndex) {
MonthYearPickerDialog f = new MonthYearPickerDialog();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt(MONTH_KEY, monthIndex);
args.putInt(DAY_KEY, daysIndex);
args.putInt(YEAR_KEY, yearIndex);
f.setArguments(args);
return f;
}
public void setListener(DatePickerDialog.OnDateSetListener listener) {
this.listener = listener;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
//getDialog().setTitle("Add Birthday");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
View dialog = inflater.inflate(R.layout.month_year_picker, null);
monthPicker = (NumberPicker) dialog.findViewById(R.id.picker_month);
yearPicker = (NumberPicker) dialog.findViewById(R.id.picker_year);
dayPicker = (NumberPicker) dialog.findViewById(R.id.picker_day);
monthPicker.setMinValue(1);
monthPicker.setMaxValue(12);
if(monthVal != -1)// && (monthVal > 0 && monthVal < 13))
monthPicker.setValue(monthVal);
else
monthPicker.setValue(cal.get(Calendar.MONTH) + 1);
monthPicker.setDisplayedValues(new String[]{"Jan","Feb","Mar","Apr","May","June","July",
"Aug","Sep","Oct","Nov","Dec"});
dayPicker.setMinValue(1);
dayPicker.setMaxValue(daysOfMonth);
if(dayVal != -1)
dayPicker.setValue(dayVal);
else
dayPicker.setValue(cal.get(Calendar.DAY_OF_MONTH));
monthPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
switch (newVal){
case 1:case 3:case 5:
case 7:case 8:case 10:
case 12:
daysOfMonth = 31;
dayPicker.setMaxValue(daysOfMonth);
break;
case 2:
daysOfMonth = 28;
dayPicker.setMaxValue(daysOfMonth);
break;
case 4:case 6:
case 9:case 11:
daysOfMonth = 30;
dayPicker.setMaxValue(daysOfMonth);
break;
}
}
});
int maxYear = cal.get(Calendar.YEAR);//2016
final int minYear = 1916;//1997;
int arraySize = maxYear - minYear;
String[] tempArray = new String[arraySize];
tempArray[0] = "---";
int tempYear = minYear+1;
for(int i=0 ; i < arraySize; i++){
if(i != 0){
tempArray[i] = " " + tempYear + "";
}
tempYear++;
}
Log.i("", "onCreateDialog: " + tempArray.length);
yearPicker.setMinValue(minYear+1);
yearPicker.setMaxValue(maxYear);
yearPicker.setDisplayedValues(tempArray);
if(yearVal != -1)
yearPicker.setValue(yearVal);
else
yearPicker.setValue(tempYear -1);
yearPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
try {
if(isLeapYear(picker.getValue())){
daysOfMonth = 29;
dayPicker.setMaxValue(daysOfMonth);
}
}catch (Exception e){
e.printStackTrace();
}
}
});
builder.setView(dialog)
// Add action buttons
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
int year = yearPicker.getValue();
if(year == (minYear+1)){
year = 1904;
}
listener.onDateSet(null, year, monthPicker.getValue(), dayPicker.getValue());
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MonthYearPickerDialog.this.getDialog().cancel();
}
});
return builder.create();
}
public static boolean isLeapYear(int year) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
}
And I call it like
Calendar calendar = Calendar.getInstance();
if(etBirthday.getText().length()> 0 ){
if(checkIsYearAvailable(etBirthday.getText().toString().trim()))
calendar = DateTimeOp.getCalendarFromFormat(etBirthday.getText().toString().trim(), Constants.dateFormat21);
else
calendar = DateTimeOp.getCalendarFromFormat(etBirthday.getText().toString().trim() + ", 1917",Constants.dateFormat21);
}
MonthYearPickerDialog pd = MonthYearPickerDialog.newInstance(calendar.get(Calendar.MONTH) + 1,
calendar.get(Calendar.DAY_OF_MONTH),calendar.get(Calendar.YEAR));
pd.setListener(new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int selectedYear, int selectedMonth, int selectedDay) {
String formatedDate = "";
if(selectedYear == 1904)
{
String currentDateFormat = selectedMonth + "/" + selectedDay;// + "/" + selectedYear; //"MM/dd/yyyy"
formatedDate = DateTimeOp.oneFormatToAnother(currentDateFormat, Constants.dateFormat20, Constants.dateFormat24);
}
else{
String currentDateFormat = selectedMonth + "/" + selectedDay + "/" + selectedYear; //"MM/dd/yyyy"
formatedDate = DateTimeOp.oneFormatToAnother(currentDateFormat, Constants.dateFormat0, Constants.dateFormat21);
}
etBirthday.setText(formatedDate);
}
});
pd.show(getFragmentManager(), "MonthYearPickerDialog");
month_year_picker.xml
<?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="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<NumberPicker
android:id="@+id/picker_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp" />
<NumberPicker
android:id="@+id/picker_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp" />
<NumberPicker
android:id="@+id/picker_year"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
Solution 5:
Late to party. But if someone looking for Kotlin version, I re-write @Stephan Klein 's anwser, change it to Kotlin, with Data-Binding style, month display, and a date input.
So, if you're looking for an Month-Year picker, you can create Class MonthYearPickerDialog
:
class MonthYearPickerDialog(val date: Date = Date()) : DialogFragment() {
companion object {
private const val MAX_YEAR = 2099
}
private lateinit var binding: DialogMonthYearPickerBinding
private var listener: OnDateSetListener? = null
fun setListener(listener: OnDateSetListener?) {
this.listener = listener
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogMonthYearPickerBinding.inflate(requireActivity().layoutInflater)
val cal: Calendar = Calendar.getInstance().apply { time = date }
binding.pickerMonth.run {
minValue = 0
maxValue = 11
value = cal.get(Calendar.MONTH)
displayedValues = arrayOf("Jan","Feb","Mar","Apr","May","June","July",
"Aug","Sep","Oct","Nov","Dec")
}
binding.pickerYear.run {
val year = cal.get(Calendar.YEAR)
minValue = year
maxValue = MAX_YEAR
value = year
}
return AlertDialog.Builder(requireContext())
.setTitle("Please Select View Month")
.setView(binding.root)
.setPositiveButton("Ok") { _, _ -> listener?.onDateSet(null, binding.pickerYear.value, binding.pickerMonth.value, 1) }
.setNegativeButton("Cancel") { _, _ -> dialog?.cancel() }
.create()
}
}
With its xml dialog_month_year_picker
:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<NumberPicker
android:id="@+id/pickerYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp" />
<NumberPicker
android:id="@+id/pickerMonth"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
To use it, call:
MonthYearPickerDialog(date).apply {
setListener { view, year, month, dayOfMonth ->
Toast.makeText(requireContext(), "Set date: $year/$month/$dayOfMonth", Toast.LENGTH_LONG).show()
}
show(supportFragmentManager, "MonthYearPickerDialog")
}
It'll look like this:
CAUTION When Used in Fragment
Since Kotlin apply
will shadow keyword this
, and MonthYearPickerDialog
is also a Fragment
, you must make sure you call the correct FragmentManager
in show()
method. Otherwise a FATAL IllegalStateException: not associated with a fragment manager.
might raise.
For Example, if you want to call this in MyFragment
, use @ to specify which "this" is:
MonthYearPickerDialog(date).apply {
setListener { view, year, month, dayOfMonth ->
Toast.makeText(requireContext(), "Set date: $year/$month/$dayOfMonth", Toast.LENGTH_LONG).show()
}
show([email protected], "MonthYearPickerDialog")
}
Upvote if the answer helps :)