自从忙完工作变动的事情后好久没写博了, 内心愧疚啊.... 说好的坚持学习呢... TAT
好吧, 回归正题. 用过 Android自带的DatePickerDialog的默认样式是这样的:
只有一个 "完成" 按钮...
如果将完成选择日期的触发事件放在方法onDateSet(), 那么无论是点击返回键或屏幕outSide的地方, 或者点击 "完成"按钮, 都会自动取消这个日期选择弹出框时并每次调用onDateSet(). 就是说, 这时, 放在onDateSet()里面的触发事件无论如何都会被调用啊, 简直不能忍... 要是弹出框的界面除了 "完成" 按钮, 还有 "取消" 按钮, 并且能控制什么时候才去调用完成日期选择的触发事件, 作为社会主义接班人的我们, 该会多开心啊~~~
那么问题来了, 除了自定一个DatePickerDialog, 比如有种做法是继承DatePickerDialog, 然后在onStop() 里面去注释里面的super.onStop() 达到不调用 onDateSet() 的目的. 但自带的DatePickerDialog究竟支不支持我们上面的需求呢? 真相只有一个:
阔以的~
我们翻开DatePickerDialog所继承的AlertDialog类源码, 其实发现里面提供了setButton(...)这样的方法,
/**
* Set a listener to be invoked when the positive button of the dialog is pressed.
*
* @param whichButton Which button to set the listener on, can be one of
* {@link DialogInterface#BUTTON_POSITIVE},
* {@link DialogInterface#BUTTON_NEGATIVE}, or
* {@link DialogInterface#BUTTON_NEUTRAL}
* @param text The text to display in positive button.
* @param listener The {@link DialogInterface.OnClickListener} to use.
*/
public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
mAlert.setButton(whichButton, text, listener, null);
}
貌似只需要传入三个参数 控件ID, 控件显示名称, 和回调的接口方法就行了.
然后, 用代码去见证"奇迹的一刻"吧. 哈哈哈~
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
final DatePickerDialog dpd = new DatePickerDialog(context,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int month,
int day) {
}
}, mYear, mMonth, mDay);
Button btnConfirm = new Button(getActivity());
Button btnCancel = new Button(getActivity());
dpd.setButton(btnConfirm.getId(), "btnConfirm", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getActivity(), "点击\nbtnConfirm", Toast.LENGTH_SHORT).show();
}
});
dpd.setButton(btnCancel.getId(), "btnCancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getActivity(), "点击\nbtnCancel", Toast.LENGTH_SHORT).show();
}
});
dpd.setTitle("选择日期");
dpd.show();
信心满满的运行, 看到弹出框, 还是只有一个按钮, 按钮显示的名称为 "btnCancel" . 纳尼 什么鬼? 这真的是我想要的吗?
这里我们先来看看 setButton() 是如何添加按钮和回调事件的.
点进去 setButton() 方法, 发现调用的是AlertController 的 setButton() 方法:
/**
* Sets a click listener or a message to be sent when the button is clicked.
* You only need to pass one of {@code listener} or {@code msg}.
*
* @param whichButton Which button, can be one of
* {@link DialogInterface#BUTTON_POSITIVE},
* {@link DialogInterface#BUTTON_NEGATIVE}, or
* {@link DialogInterface#BUTTON_NEUTRAL}
* @param text The text to display in positive button.
* @param listener The {@link DialogInterface.OnClickListener} to use.
* @param msg The {@link Message} to be sent when clicked.
*/
public void setButton(int whichButton, CharSequence text,
DialogInterface.OnClickListener listener, Message msg) {
if (msg == null && listener != null) {
msg = mHandler.obtainMessage(whichButton, listener);
}
switch (whichButton) {
case DialogInterface.BUTTON_POSITIVE:
mButtonPositiveText = text;
mButtonPositiveMessage = msg;
break;
case DialogInterface.BUTTON_NEGATIVE:
mButtonNegativeText = text;
mButtonNegativeMessage = msg;
break;
case DialogInterface.BUTTON_NEUTRAL:
mButtonNeutralText = text;
mButtonNeutralMessage = msg;
break;
default:
throw new IllegalArgumentException("Button does not exist");
}
}
看到这个方法的四个参数, 大家都会表示很熟悉吧. 但是这和上面添加 btnConfirm 和 btnCancel 两个按钮只显示最后一个, 有什么关系?
我们回去看Button的构造方法, 从Button -> ... -> 追溯到View类的源码, 就会发现, new出来的控件, 如果没有设置控件ID, 是默认赋予值为 -1 的控件ID:
/**
* The view's identifier.
* {@hide}
*
* @see #setId(int)
* @see #getId()
*/
@ViewDebug.ExportedProperty(resolveId = true)
int mID = NO_ID;
/**
* Used to mark a View that has no ID.
*/
public static final int NO_ID = -1;
这个mID 就是控件的ID值.
再次看上面AlertController 的 setButton() 方法中用于判断的几个参数的值
/**
* The identifier for the positive button.
*/
public static final int BUTTON_POSITIVE = -1;
/**
* The identifier for the negative button.
*/
public static final int BUTTON_NEGATIVE = -2;
/**
* The identifier for the neutral button.
*/
public static final int BUTTON_NEUTRAL = -3;
BUTTON_POSITIVE 的值是 -1 . 而我们新建的两个按钮控件的默认ID也都是 -1. 所以代码中即使两次调用 setButton() 方法去给DatePickerDialog 添加两个按钮, 却只显示后者 btnCancel 按钮. SOGA ~
来到这里, 问题好像还没解决, 却又好像想到怎么解决了.
上面不是提供了三个参数吗? BUTTON_POSITIVE, BUTTON_NEGATIVE, BUTTON_NEUTRAL . 继续试试看!
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
final DatePickerDialog dpd = new DatePickerDialog(context,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int month,
int day) {
}
}, mYear, mMonth, mDay);
dpd.setButton(DatePickerDialog.BUTTON_POSITIVE, "取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
dpd.setButton(DatePickerDialog.BUTTON_NEGATIVE, "确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dpd.setButton(DatePickerDialog.BUTTON_NEUTRAL, "中间按钮", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
dpd.setTitle(R.string.str_select_dob);
dpd.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
dialog.dismiss();
}
});
dpd.setCanceledOnTouchOutside(true);
dpd.show();
POSITIVE 对应默认的确定键, NEGATIVE 是取消. 但是DatePickerDialog 对这个两个按钮的位置是将取消键放在左边, 确定键放在右边.→_→ 为了好看点, 我将它们显示的名称互换了, 反正它们各自有对应回调的接口, 所以毫无影响. 如果将添加中间按钮 setButton(DatePickerDialog.BUTTON_NEUTRAL, ...) 的方法去掉, 顺便把POSITIVE 的传入参数改为"确定", NEGATIVE 改为 "取消", 就会看到我们预期的界面: (果然, 确定键放在右边是有点奇葩吧 == )
到了这里, 显示的问题, 我们搞掂了. 就剩下如何获取日期, 在哪里触发完成选择日期的事件. 你这么聪明, 肯定猜到啦~ 没错, 就这样:
dpd.setButton(DatePickerDialog.BUTTON_NEGATIVE, "确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int year = dpd.getDatePicker().getYear();
int month = dpd.getDatePicker().getMonth();
int day = dpd.getDatePicker().getDayOfMonth();
// 获取日期后,进行处理...
dialog.dismiss();
}
});
OK, onDateSet() 方法里面甚至可以不去写一行代码. 并且只在需要的时候才去获取日期. 这样我们就完成了, 利用DatePickerDialog 自身的方法不但实现了日期选择, 还解决了自动回调 onDateSet() 方法的问题.