时间选择器中onDateSet/onTimeSet执行两次

一 编写目的

虽然目前有很多的时间选择控件可以给开发者使用,但有的时候由于使用行业,手机页面布局,手机屏幕等因素,我们还是需要使用android自带的时间选择控件。我最近使用系统时间选择控件的时候,发现了一个问题,就是高版本的系统中onDateSet方法会调用两次,这就意味着低版本中在onDateSet中实现的逻辑在高版本中会执行两次,这样就会给我们软件的兼容问题带来一些麻烦,因此这个地方把解决方法记录下,方便大家学习。

二 问题原因

我手中有几台不同系统版本的手机,发现在4.3系统后的(下面就是4.1版本,4.2没测试过)版本中运行测试代码的时候,onDateSet中打印日志执行两次,而低版本的只执行了一次,这就说明很有可能是高版本系统在这个地方做了修改,因此我去查阅了下系统源码,对比一下,发现了其中问题。
低版本的源码:

1
2
3
4
5
6
public void onClick(DialogInterface dialog, int which) {  
if (mCallBack != null) {
mDatePicker.clearFocus();
mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
} }

就是说点击确定或取消按钮的时候会执行onDateSet方法。
高版本的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void onClick(DialogInterface dialog, int which) {  
tryNotifyDateSet();
}

private void tryNotifyDateSet() {
if (mCallBack != null) {
mDatePicker.clearFocus();
mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
}
}

@Override
protected void onStop() {
tryNotifyDateSet();
super.onStop();
}

从此源码中可以看出,相较于低版本的源码,高版本的源码中对应对话框的停止(onStop)也做了调用onDateSet方法,从而使点击确定或取消按钮时,该方法被执行了两次。

三 解决

比较两份源码,我们很容易得出一个解决方法,就是把高版本的onStop干掉。对头,我就是这么处理的,重载onStop方法,将里面的super.onStop()去掉。对于无其他特殊要求的情况下,这种貌似是没有副作用的,而且,我这样用后到目前为止也没有副作用体现出来。以下是重载方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 public static class MyDatePickDialog extends DatePickerDialog {  

public MyDatePickDialog(Context context, OnDateSetListener callBack,
int year, int monthOfYear, int dayOfMonth) {
super(context, callBack, year, monthOfYear, dayOfMonth);
// TODO Auto-generated constructor stub
}

public MyDatePickDialog(Context context, int theme,
OnDateSetListener callBack, int year, int monthOfYear,
int dayOfMonth) {
super(context, theme, callBack, year, monthOfYear, dayOfMonth);
// TODO Auto-generated constructor stub
}

@Override
protected void onStop() {
// TODO Auto-generated method stub
}

}

同理,TimePickerDialog的解决方式和这个一样,就不累述了。

四 一个自定义的时间选择示例

功能:通过继承EditText,达到点击输入框(或者调用里面的触发时间),然后弹出日期供选择,选完日期后,接着弹出时间来供选择,最后将选择的时间日期填写到输入框中。以下是代码:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package com.urovo.stocity.view.custom;  

import java.util.Calendar;
import java.util.Date;

import utils.DateUtil;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.TimePicker;

/**
* input method will need to run in a limited "generate key events" mode.
*
*
* @author admin
*
*/
public class DateTimeEditText extends EditText implements OnDateSetListener,
OnTimeSetListener {
public MyDatePickDialog Dlg;
public Dialog timeDialog;
private Context context;
private String timeStr;

public DateTimeEditText(Context context) {
super(context);
this.context = context;
setListener();
}

public DateTimeEditText(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
setListener();
}

private void setListener() {
this.setFocusable(false);
this.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
initCalendar();
Dlg.show();
}
});
}

public void dateSelect() {// 此方法为弹出时间选择对话框,可在外部调用
initCalendar();
Dlg.show();
}

private void initCalendar() {
String dateStr = this.getText().toString().trim();
Date date = DateUtil.parseCompleteDate(dateStr);
if (date == null) {// 如果输入框内初始为空,则将当前时间初始为日期时间
date = new Date();
}

Calendar d = Calendar.getInstance();
d.setTimeInMillis(date.getTime());

// 初始化时间选择器
int year = d.get(Calendar.YEAR);
int month = d.get(Calendar.MONTH);
int day = d.get(Calendar.DAY_OF_MONTH);
Dlg = new MyDatePickDialog(context, this, year, month, day);

Dlg.setCanceledOnTouchOutside(false);

timeDialog = new MyTimePickerDialog(context, this,
d.get(Calendar.HOUR_OF_DAY), d.get(Calendar.MINUTE), true);
timeDialog.setTitle("请选择时间");
}

@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
// TODO Auto-generated method stub
timeStr = year + "-"
+ (monthOfYear < 9 ? "0" + (monthOfYear + 1) : monthOfYear + 1)
+ "-" + (dayOfMonth < 10 ? "0" + dayOfMonth : dayOfMonth) + " ";
timeDialog.show();
Log.e("aa", "onDateSet");
}

@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
// TODO Auto-generated method stub
timeStr += (hourOfDay < 10 ? "0" + hourOfDay : hourOfDay) + ":"
+ (minute < 10 ? "0" + minute : minute) + ":00";
DateTimeEditText.this.setText(timeStr);
Log.e("aa", "onTimeSet");
}

// 重写DatePickerDialog以避免两次执行onDateSet方法
public static class MyDatePickDialog extends DatePickerDialog {

public MyDatePickDialog(Context context, OnDateSetListener callBack,
int year, int monthOfYear, int dayOfMonth) {
super(context, callBack, year, monthOfYear, dayOfMonth);
// TODO Auto-generated constructor stub
}

public MyDatePickDialog(Context context, int theme,
OnDateSetListener callBack, int year, int monthOfYear,
int dayOfMonth) {
super(context, theme, callBack, year, monthOfYear, dayOfMonth);
// TODO Auto-generated constructor stub
}

@Override
protected void onStop() {
// TODO Auto-generated method stub
}

}

public static class MyTimePickerDialog extends TimePickerDialog {

public MyTimePickerDialog(Context context, OnTimeSetListener callBack,
int hourOfDay, int minute, boolean is24HourView) {
super(context, callBack, hourOfDay, minute, is24HourView);
// TODO Auto-generated constructor stub
}

public MyTimePickerDialog(Context context, int theme,
OnTimeSetListener callBack, int hourOfDay, int minute,
boolean is24HourView) {
super(context, theme, callBack, hourOfDay, minute, is24HourView);
// TODO Auto-generated constructor stub
}

@Override
protected void onStop() {
// TODO Auto-generated method stub
}

}
}

五 结束

一个小小的总结,希望能帮到大家。