这次同学提出了一个问题,他没搞懂怎么利用DatePicker实现弹出窗口选择日期的逻辑。于是我看了看官方文档,发现官方对使用Picker专门写了一篇指南。有指南当然是最好了。这篇指南里还有弹出窗口选择时间的例子,跟选择日期的例子很相近,此处不赘述了。
之前已经学习过使用AlertDialog
,而此次的DatePickerDialog
就继承于AlertDialog
,本来感觉会与其有很多相似之处,但其实差距还是挺大的。因为指南中推荐使用DialogFragment
来容纳(?原文为host,不知道该怎么翻译)DatePickerDialog
或TimePickerDialog
,因为
DialogFragment
能帮你管理对话框的生命周期,并且允许你在不同的布局配置中显示Picker,如在手机中的基本对话框中或者是在大屏幕里作为布局的一部分嵌入。(说白了就是说Fragment的好处嘛)
然而我看《第一行代码》的时候以为碎片不重要就跳过了。。。没想到现在就要还上一点了。还就还吧~
配置DialogFragment
前面已经说过DatePickerDialog
要在DialogFragment
中容纳,自然要创建一个DialogFragment
。
- 创建
DialogFragment
的子Fragment
,实现DatePickerDialog.OnDateSetListener
接口 - 覆盖
onCreateDialog(Bundle)
方法,因为要选择日期,所以要给一个默认值:- 使用
Calendar.getInstance()
获取一个表示当前时间的Calendar
对象 - 调用
calendar.get(int)
取出对应的年、月、日 return new DatePickerDialog(getActivity(), listener, year, month, day)
来创建DatePickerDialog
(listener是上面实现的接口的实例,当然是this,也就是这个Dialog
。year/month/day是用来设置初始值的)
- 使用
- 实现
OnDateSetListener
接口的onDateSet()
方法,此处是时间设定完成后调用,理所当然地要在这里传回设置好的数据。查阅了很多资料,对于Fragment
给Activity
传递数据都推荐使用实现接口+回调方法来实现,因为DialogFragment
提供了onAttach(Context)
方法来获取父Activity
,不用白不用。这里简单解释一下何为回调,简单地说就是- A持有对B的引用
- 在A中使用B
- B中也持有对A的引用
- 使用完B后在B中回去使用A。
- 最后一步就称为回调。此例中,父
Activity
持有对DialogFragment
的引用,然后在Activity
中创建DialogFragment
,获取了用户设置的日期后再回调父Activity
将日期返回。- 定义传回数据用的接口i.e.
SendDate
和方法
- 定义传回数据用的接口i.e.
- 因为要回调
Activity
的方法,肯定要持有Activity
对象引用,且要在DialogFragment
的生命周期结束时将引用释放,免得Activity
回收时出现错误。- 在
onAttach()
方法中获取父Activity
并判断是否实现SendDate
接口 - 在
onDetach()
方法中释放对付Activity
的引用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
interface SendDate{
public void sendDate(int year, int month, int day);
}
private SendDate listener;
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the current date as the default date in the picker
final Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
// Create a new instance of DatePickerDialog and return it
return new DatePickerDialog(getActivity(), this, year, month, day);
}
public void onDateSet(DatePicker view, int year, int month, int day) {
// Do something with the date chosen by the user
listener.sendDate(year, month, day);
}
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SendDate){
listener = (SendDate)context;
}else{
throw new IllegalArgumentException("Activity must implement SendDate");
}
}
public void onDetach() {
super.onDetach();
listener = null;
}
}
- 在
- 此处再简单介绍一下
DialogFragment
的生命周期:onAttach()
中传入要attach的Activity
实例,使得DialogFragment
中可以调用Activity
中的方法onCreate()
中可以对对话框的样式进行设置onCreateDialog()
中设置对对话框的监听onCreateView()
中初始化View
,并对SavedInstanceState
进行解析onPause()
:用户离开片段的第一个信号。通常应该在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)其他知识点:
instanceof
运算符用以判断某对象是否为某类的实例。
配置MainActivity
- 首先需要一个显示读取到的日期的控件,此处选择最简单的TextView。此处不能忘了初始化Text。为了方便测试起见,需要给
DatePickerDialog
设置一个触发器,这里选择按钮+点击监听。 - 在点击按钮以后,需要做的事是:
- 新建一个上面自己写的
DialogFragment
子类对象; - 构建
DatePickerDialog
并显示; - 实现上面写的回调接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35public class MainActivity extends FragmentActivity implements DatePickerFragment.SendDate{
private TextView dateText;
private Button requireDate;
private DatePickerFragment fragment = new DatePickerFragment();
private int year;
private int month;
private int day;
protected void onCreate(Bundle savedInstanceState) {
final Calendar calendar = Calendar.getInstance();
year = calendar.get(Calendar.YEAR);
month = calendar.get(Calendar.MONTH)+1;
day = calendar.get(Calendar.DAY_OF_MONTH);
dateText.setText(String.format("%d-%d-%d", year, month, day));
requireDate.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
fragment.show(getSupportFragmentManager(), "dataPicker");
}
});
}
public void setDate(int year, int month, int day){
this.year = year;
this.month = month+1;
this.day = day;
dateText.setText(String.format("%d-%d-%d", this.year, this.month, this.day));
}
public void sendDate(int year, int month, int day) {
setDate(year, month, day);
}
}
- 新建一个上面自己写的
其他知识点:
calendar.get(Calendar.MONTH)
方法取得的月份是从0开始的。如果不用一些格式化手段直接用int
显示的话,需要+1。(在初始化DatePickerDialog
的时候并没有+1,但是月份是正常的,推测是用了其他手段,看了源码没有头绪。。)- 如果在自己写的
DialogFragment
的子类里导入的是android.support.v4.app.DialogFragment
的话,在活动中初始化对话框时就要用show(getSupportFragmentManager(), String)
方法;如果导入的是android.app.DialogFragment
的话,就要用show(getFragmentManager(), String)
方法。