Android Jelly Bean DatePickerDialog --- 有没有办法取消?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11444238/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Jelly Bean DatePickerDialog --- is there a way to cancel?
提问by davidcesarino
--- Note to moderators:Today (July 15), I've noticed that someone already faced this problem here. But I'm not sure if it's appropriate to close this as a duplicate, since i think I provided a much better explanation of the issue. I'm not sure if I should edit the other question and paste this content there, but I'm not comfortable changing someone else's question too much.---
---版主注意:今天(7 月 15 日),我注意到这里已经有人遇到了这个问题。但我不确定将其作为副本关闭是否合适,因为我认为我对这个问题提供了更好的解释。我不确定我是否应该编辑另一个问题并将此内容粘贴到那里,但我不太愿意改变别人的问题太多。---
I have something weirdhere.
我这里有一些奇怪的东西。
I don't think the problem depends on which SDK you build against. The device OS version is what matters.
我不认为问题取决于您针对哪个 SDK 构建。设备操作系统版本很重要。
Problem #1: inconsistency by default
问题#1:默认不一致
DatePickerDialog
was changed (?) in Jelly Bean and now only provides a Donebutton. Previous versions included a Cancelbutton, and this may affect user experience (inconsistency, muscle memory from previous Android versions).
DatePickerDialog
在 Jelly Bean 中被更改 (?),现在只提供一个完成按钮。以前的版本包含一个取消按钮,这可能会影响用户体验(不一致,以前的 Android 版本的肌肉记忆)。
Replicate:Create a basic project. Put this in onCreate
:
复制:创建一个基本项目。把这个放在onCreate
:
DatePickerDialog picker = new DatePickerDialog(
this,
new OnDateSetListener() {
@Override
public void onDateSet(DatePicker v, int y, int m, int d) {
Log.d("Picker", "Set!");
}
},
2012, 6, 15);
picker.show();
Expected:A Cancelbutton to appear in the dialog.
预计:一个取消按钮出现在对话框中。
Current:A Cancelbutton does not appear.
当前:一个取消按钮不会出现。
Screenshots:4.0.3(OK) and 4.1.1(possibly wrong?).
Problem #2: wrong dismiss behavior
问题 2:错误的解雇行为
Dialog calls whichever listener it should call indeed, and then alwayscalls OnDateSetListener
listener. Canceling still calls the set method, and setting it calls the method twice.
Dialog 调用它确实应该调用的任何侦听器,然后始终调用OnDateSetListener
listener。取消仍然调用 set 方法,设置它调用该方法两次。
Replicate:Use #1 code, but add code below (you'll see this solves #1, but only visually/UI):
复制:使用 #1 代码,但在下面添加代码(你会看到这解决了 #1,但只是在视觉上/UI):
picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Cancel!");
}
});
Expected:
预期的:
- Pressing the BACK key or clicking outside the dialog should do nothing.
- Pressing "Cancel" should print Picker Cancel!.
- Pressing "Set" should print Picker Set!.
- 按 BACK 键或单击对话框外应该什么都不做。
- 按“取消”应该打印选择器取消!.
- 按“设置”应该打印选择器设置!.
Current:
当前的:
- Pressing the BACK key or clicking outside the dialog prints Picker Set!.
- Pressing "Cancel" prints Picker Cancel!and then Picker Set!.
- Pressing "Set" prints Picker Set!and then Picker Set!.
- 按 BACK 键或在对话框外单击会打印Picker Set!.
- 按“取消”打印选择器取消!然后选择器设置!.
- 按“设置”打印选择器设置!然后选择器设置!.
Log lines showing the behavior:
显示行为的日志行:
07-15 12:00:13.415: D/Picker(21000): Set!
07-15 12:00:24.860: D/Picker(21000): Cancel!
07-15 12:00:24.876: D/Picker(21000): Set!
07-15 12:00:33.696: D/Picker(21000): Set!
07-15 12:00:33.719: D/Picker(21000): Set!
Other notes and comments
其他注释和评论
- Wrapping it around a
DatePickerFragment
doesn't matter. I simplified the problem for you, but I've tested it.
- 将它包裹在 a 周围
DatePickerFragment
没有关系。我为您简化了问题,但我已经对其进行了测试。
采纳答案by davidcesarino
Note: Fixed as of Lollipop, source here. Automated class for use in clients(compatible with all Android versions) updated as well.
注意:从 Lollipop 开始修复,来源在这里。用于客户端的自动化类(与所有 Android 版本兼容)也更新了。
TL;DR: 1-2-3 dead easy steps for a global solution:
TL;DR:全球解决方案的 1-2-3 个非常简单的步骤:
- Download thisclass.
- Implement
OnDateSetListener
in your activity (or change the class to suit your needs). Trigger the dialog with this code (in this sample, I use it inside a
Fragment
):Bundle b = new Bundle(); b.putInt(DatePickerDialogFragment.YEAR, 2012); b.putInt(DatePickerDialogFragment.MONTH, 6); b.putInt(DatePickerDialogFragment.DATE, 17); DialogFragment picker = new DatePickerDialogFragment(); picker.setArguments(b); picker.show(getActivity().getSupportFragmentManager(), "frag_date_picker");
- 下载这个类。
OnDateSetListener
在您的活动中实施(或更改课程以满足您的需要)。使用此代码触发对话框(在此示例中,我在 a 中使用它
Fragment
):Bundle b = new Bundle(); b.putInt(DatePickerDialogFragment.YEAR, 2012); b.putInt(DatePickerDialogFragment.MONTH, 6); b.putInt(DatePickerDialogFragment.DATE, 17); DialogFragment picker = new DatePickerDialogFragment(); picker.setArguments(b); picker.show(getActivity().getSupportFragmentManager(), "frag_date_picker");
And that's all it takes!The reason I still keep my answer as "accepted" is because I still prefer my solution since it has a very small footprint in client code, it addresses the fundamental issue (the listener being called in the framework class), works fine across config changes and it routes the code logic to the default implementation in previous Android versions not plagued by this bug (see class source).
这就是全部!我仍然将我的答案保留为“已接受”的原因是因为我仍然更喜欢我的解决方案,因为它在客户端代码中占用的空间非常小,它解决了基本问题(在框架类中调用侦听器),在配置更改时工作正常并将代码逻辑路由到未受此错误困扰的以前 Android 版本中的默认实现(请参阅类源)。
Original answer (kept for historical and didactic reasons):
原始答案(出于历史和教学原因保留):
Bug source
错误来源
OK, looks like it's indeed a bug and someone else already filled it. Issue 34833.
好的,看起来它确实是一个错误并且其他人已经填充了它。问题 34833。
I've found that the problem is possibly in DatePickerDialog.java
. Where it reads:
我发现问题可能出在DatePickerDialog.java
. 哪里写着:
private void tryNotifyDateSet() {
if (mCallBack != null) {
mDatePicker.clearFocus();
mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
}
}
@Override
protected void onStop() {
tryNotifyDateSet();
super.onStop();
}
I'd guess it could have been:
我猜它可能是:
@Override
protected void onStop() {
// instead of the full tryNotifyDateSet() call:
if (mCallBack != null) mDatePicker.clearFocus();
super.onStop();
}
Now if someone can tell me how I can propose a patch/bug report to Android, I'd be glad to. Meanwhile, I suggested a possible fix (simple) as an attached version of DatePickerDialog.java
in the Issue there.
现在,如果有人能告诉我如何向 Android 提出补丁/错误报告,我会很高兴。同时,我DatePickerDialog.java
在那里的问题中建议了一个可能的修复(简单)作为附加版本。
Concept to avoid the bug
避免错误的概念
Set the listener to null
in the constructor and create your own BUTTON_POSITIVE
button later on. That's it, details below.
null
在构造函数中将侦听器设置为并BUTTON_POSITIVE
稍后创建您自己的按钮。就是这样,下面详细介绍。
The problem happens because DatePickerDialog.java
, as you can see in the source, calls a global variable (mCallBack
) that stores the listener that was passed in the constructor:
出现问题的原因是DatePickerDialog.java
,正如您在源代码中看到的,调用了一个全局变量 ( mCallBack
),该变量存储在构造函数中传递的侦听器:
/**
* @param context The context the dialog is to run in.
* @param callBack How the parent is notified that the date is set.
* @param year The initial year of the dialog.
* @param monthOfYear The initial month of the dialog.
* @param dayOfMonth The initial day of the dialog.
*/
public DatePickerDialog(Context context,
OnDateSetListener callBack,
int year,
int monthOfYear,
int dayOfMonth) {
this(context, 0, callBack, year, monthOfYear, dayOfMonth);
}
/**
* @param context The context the dialog is to run in.
* @param theme the theme to apply to this dialog
* @param callBack How the parent is notified that the date is set.
* @param year The initial year of the dialog.
* @param monthOfYear The initial month of the dialog.
* @param dayOfMonth The initial day of the dialog.
*/
public DatePickerDialog(Context context,
int theme,
OnDateSetListener callBack,
int year,
int monthOfYear,
int dayOfMonth) {
super(context, theme);
mCallBack = callBack;
// ... rest of the constructor.
}
So, the trick is to provide a null
listener to be stored as the listener, and then roll your own set of buttons (below is the original code from #1, updated):
因此,诀窍是提供一个null
侦听器作为侦听器存储,然后滚动您自己的一组按钮(以下是来自 #1 的原始代码,已更新):
DatePickerDialog picker = new DatePickerDialog(
this,
null, // instead of a listener
2012, 6, 15);
picker.setCancelable(true);
picker.setCanceledOnTouchOutside(true);
picker.setButton(DialogInterface.BUTTON_POSITIVE, "OK",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Correct behavior!");
}
});
picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Cancel!");
}
});
picker.show();
Now it will work because of the possible correction that I posted above.
现在它会起作用,因为我在上面发布了可能的更正。
And since DatePickerDialog.java
checks for a null
whenever it reads mCallback
(since the days of API 3/1.5 it seems--- can't check Honeycomb of course), it won't trigger the exception.Considering Lollipop fixed the issue, I'm not going to look into it: just use the default implementation (covered in the class I provided).
并且由于在读取时DatePickerDialog.java
检查 a (从 API 3/1.5 开始,它似乎无法检查 Honeycomb),因此它不会触发异常。null
mCallback
考虑到 Lollipop 解决了这个问题,我不打算研究它:只需使用默认实现(在我提供的类中涵盖)。
At first I was afraid of not calling the clearFocus()
, but I've tested here and the Log lines were clean. So that line I proposed may not even be necessary after all, but I don't know.
起初我害怕不调用clearFocus()
,但我在这里测试过并且日志行是干净的。所以我提出的那条线可能根本没有必要,但我不知道。
Compatibility with previous API levels (edited)
与之前 API 级别的兼容性(已编辑)
As I pointed in the comment below, that was a concept, and you can download the class I'm using from my Google Drive account. The way I used, the default system implementation is used on versions not affected by the bug.
正如我在下面的评论中指出的那样,这是一个概念,您可以从我的 Google Drive 帐户下载我正在使用的课程。我使用的方式,默认系统实现用于不受错误影响的版本。
I took a few assumptions (button names etc.) that are suitable for my needs because I wanted to reduce boilerplate code in client classes to a minimum. Full usage example:
我做了一些适合我需要的假设(按钮名称等),因为我想将客户端类中的样板代码减少到最低限度。完整使用示例:
class YourActivity extends SherlockFragmentActivity implements OnDateSetListener
// ...
Bundle b = new Bundle();
b.putInt(DatePickerDialogFragment.YEAR, 2012);
b.putInt(DatePickerDialogFragment.MONTH, 6);
b.putInt(DatePickerDialogFragment.DATE, 17);
DialogFragment picker = new DatePickerDialogFragment();
picker.setArguments(b);
picker.show(getActivity().getSupportFragmentManager(), "fragment_date_picker");
回答by dmon
I'm gonna add my own riff on the solution David Cesarino posted, in case you're not using Fragments, and want an easy way to fix it in all versions (2.1 thru 4.1):
我将在 David Cesarino 发布的解决方案上添加我自己的即兴演奏,以防您不使用 Fragments,并且想要一种在所有版本(2.1 到 4.1)中修复它的简单方法:
public class FixedDatePickerDialog extends DatePickerDialog {
//I use a Calendar object to initialize it, but you can revert to Y,M,D easily
public FixedDatePickerDialog(Calendar dateToShow, Context context, OnDateSetListener callBack) {
super(context, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
initializePicker(callBack);
}
public FixedDatePickerDialog(Calendar dateToShow, Context context, int theme,
OnDateSetListener callBack) {
super(context, theme, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
initializePicker(callBack);
}
private void initializePicker(final OnDateSetListener callback) {
try {
//If you're only using Honeycomb+ then you can just call getDatePicker() instead of using reflection
Field pickerField = DatePickerDialog.class.getDeclaredField("mDatePicker");
pickerField.setAccessible(true);
final DatePicker picker = (DatePicker) pickerField.get(this);
this.setCancelable(true);
this.setButton(DialogInterface.BUTTON_NEGATIVE, getContext().getText(android.R.string.cancel), (OnClickListener) null);
this.setButton(DialogInterface.BUTTON_POSITIVE, getContext().getText(android.R.string.ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
picker.clearFocus(); //Focus must be cleared so the value change listener is called
callback.onDateSet(picker, picker.getYear(), picker.getMonth(), picker.getDayOfMonth());
}
});
} catch (Exception e) { /* Reflection probably failed*/ }
}
}
回答by cirit
Until the bug will be fixed I suggest not to use DatePickerDialog or TimePickerDialog. Use custom made AlertDialog with TimePicker/DatePicker widget;
在修复错误之前,我建议不要使用 DatePickerDialog 或 TimePickerDialog。将定制的 AlertDialog 与 TimePicker/DatePicker 小部件一起使用;
Change TimePickerDialog with;
更改 TimePickerDialog 与;
final TimePicker timePicker = new TimePicker(this);
timePicker.setIs24HourView(true);
timePicker.setCurrentHour(20);
timePicker.setCurrentMinute(15);
new AlertDialog.Builder(this)
.setTitle("Test")
.setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", timePicker.getCurrentHour() + ":"
+ timePicker.getCurrentMinute());
}
})
.setNegativeButton(android.R.string.cancel,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
Log.d("Picker", "Cancelled!");
}
}).setView(timePicker).show();
Change DatePickerDialog with;
更改 DatePickerDialog 与;
final DatePicker datePicker = new DatePicker(this);
datePicker.init(2012, 10, 5, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
datePicker.setCalendarViewShown(false);
}
new AlertDialog.Builder(this)
.setTitle("Test")
.setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", datePicker.getYear() + " "
+ (datePicker.getMonth() + 1) + " "
+ datePicker.getDayOfMonth());
}
})
.setNegativeButton(android.R.string.cancel,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
Log.d("Picker", "Cancelled!");
}
}).setView(datePicker).show();
回答by Tejasvi Hegde
The one for TimePicker based on the solution by David Cesarino , "TL;DR: 1-2-3 dead easy steps for a global solution"
TimePicker 基于David Cesarino的解决方案,“TL;DR: 1-2-3 dead easy steps for a global solution”
TimePickerDialog does not provide the functionality like DatePickerDialog.getDatePicker. So, OnTimeSetListenerlistener has to be provided. Just to keep the similarity with DatePicker workaround solution, I have maintained the old mListener concept. You can change it if you need to.
TimePickerDialog 不提供类似 DatePickerDialog.getDatePicker 的功能。因此,必须提供OnTimeSetListener侦听器。为了保持与 DatePicker 解决方案的相似性,我保留了旧的 mListener 概念。如果需要,您可以更改它。
Calling and Listener is same as original solution. Just include
调用和侦听器与原始解决方案相同。只需包括
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
extend parent class,
扩展父类,
... implements OnDateSetListener, OnTimeSetListener
Implement
实施
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
...
}
example calling
示例调用
Calendar cal = Calendar.getInstance();
int hour = cal.get(Calendar.HOUR_OF_DAY);
int minute = cal.get(Calendar.MINUTE);
Bundle b = new Bundle();
b.putInt(TimePickerDialogFragment.HOUR, hour);
b.putInt(TimePickerDialogFragment.MINUTE, minute);
DialogFragment picker = new TimePickerDialogFragment();
picker.setArguments(b);
picker.show(getSupportFragmentManager(), "frag_time_picker");
(Updated to handle cancel)
(更新以处理取消)
public class TimePickerDialogFragment extends DialogFragment {
public static final String HOUR = "Hour";
public static final String MINUTE = "Minute";
private boolean isCancelled = false; //Added to handle cancel
private TimePickerDialog.OnTimeSetListener mListener;
//Added to handle parent listener
private TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
if (!isCancelled)
{
mListener.onTimeSet(view,hourOfDay,minute);
}
}
};
//
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mListener = (TimePickerDialog.OnTimeSetListener) activity;
}
@Override
public void onDetach() {
this.mListener = null;
super.onDetach();
}
@TargetApi(11)
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Bundle b = getArguments();
int h = b.getInt(HOUR);
int m = b.getInt(MINUTE);
final TimePickerDialog picker = new TimePickerDialog(getActivity(), getConstructorListener(), h, m,DateFormat.is24HourFormat(getActivity()));
//final TimePicker timePicker = new TimePicker(getBaseContext());
if (hasJellyBeanAndAbove()) {
picker.setButton(DialogInterface.BUTTON_POSITIVE,
getActivity().getString(android.R.string.ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
isCancelled = false; //Cancel flag, used in mTimeSetListener
}
});
picker.setButton(DialogInterface.BUTTON_NEGATIVE,
getActivity().getString(android.R.string.cancel),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
isCancelled = true; //Cancel flag, used in mTimeSetListener
}
});
}
return picker;
}
private boolean hasJellyBeanAndAbove() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
private TimePickerDialog.OnTimeSetListener getConstructorListener() {
return hasJellyBeanAndAbove() ? mTimeSetListener : mListener; //instead of null, mTimeSetListener is returned.
}
}
回答by AbdelHady
According to Ankur Chaudhary's brilliant answeron the similar TimePickerDialog
issue, if we checked inside onDateSet
if the given view isShown()
or not, it will solve the whole issue with the minimal effort, with no need for extending the picker or checking for some hideous flags going around the code or even checking for the OS version, just do the following:
根据 Ankur Chaudhary对类似问题的精彩回答TimePickerDialog
,如果我们检查内部onDateSet
是否给定视图isShown()
,它将以最小的努力解决整个问题,无需扩展选择器或检查代码周围的一些可怕的标志甚至检查操作系统版本,只需执行以下操作:
public void onDateSet(DatePicker view, int year, int month, int day) {
if (view.isShown()) {
// read the date here :)
}
}
and of course the same can be done for onTimeSet
as per Ankur's answer
当然onTimeSet
,根据 Ankur 的回答也可以这样做
回答by daniel_c05
In case anyone wants a quick workaround, here's the code I used:
如果有人想要快速解决方法,这是我使用的代码:
public void showCustomDatePicker () {
final DatePicker mDatePicker = (DatePicker) getLayoutInflater().
inflate(R.layout.date_picker_view, null);
//Set an initial date for 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);
//Set the date now
mDatePicker.updateDate(year, month, day);
//create the dialog
AlertDialog.Builder mBuilder = new Builder(this);
//set the title
mBuilder.setTitle(getString(R.string.date_picker_title))
//set our date picker
.setView(mDatePicker)
//set the buttons
.setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//whatever method you choose to handle the date changes
//the important thing to know is how to retrieve the data from the picker
handleOnDateSet(mDatePicker.getYear(),
mDatePicker.getMonth(),
mDatePicker.getDayOfMonth());
}
})
.setNegativeButton(android.R.string.cancel, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
//create the dialog and show it.
.create().show();
}
}
Where layout.date_picker_view is a simple layout resource with a DatePicker as it's only element:
其中 layout.date_picker_view 是一个带有 DatePicker 的简单布局资源,因为它是唯一的元素:
<!xml version="1.0" encoding="utf-8">
<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/date_picker"
android:layout_width="fill_parent"
android:spinnersShown="true"
android:calendarViewShown="false"
android:layout_height="fill_parent"/>
Here's the full tutorialin case you are interested.
如果您有兴趣,这里是完整的教程。
回答by Daniel Ryan
My simple solution. When you want to get it firing again just run "resetFired" (say when opening the dialog again).
我的简单解决方案。当您想再次触发它时,只需运行“resetFired”(例如再次打开对话框时)。
private class FixedDatePickerDialogListener implements DatePickerDialog.OnDateSetListener{
private boolean fired;
public void resetFired(){
fired = false;
}
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
if (fired) {
Log.i("DatePicker", "Double fire occurred.");
return;//ignore and return.
}
//put your code here to handle onDateSet
fired = true;//first time fired
}
}
回答by Alvaro
The way I managed this situation was using a flag and overriding the onCancel and onDismiss methods.
我处理这种情况的方法是使用标志并覆盖 onCancel 和 onDismiss 方法。
onCancel gets called only when the user touches outside the dialog or the back button. onDismiss always gets called
只有当用户触摸对话框外部或后退按钮时才会调用 onCancel。onDismiss 总是被调用
Setting a flag in the onCancel method can help filter in the onDismiss method the user's intent: cancel action or done action. Below some code that shows the idea.
在 onCancel 方法中设置一个标志可以帮助在 onDismiss 方法中过滤用户的意图:取消操作或完成操作。下面的一些代码显示了这个想法。
public class DatePickerDialogFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
private boolean cancelDialog = false;
private int year;
private int month;
private int day;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
DatePickerDialog dpd = new DatePickerDialog(getActivity(), this, year, month, day);
return dpd;
}
public void setDatePickerDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
cancelDialog = true;
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (!cancelDialog) {
#put the code you want to execute if the user clicks the done button
}
}
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
setDatePickerDate(year, monthOfYear, dayOfMonth);
}
}
回答by Wojtek Jarosz
There is a very simple workaround, if your application does not use the action bar. Note by the way, that some apps rely on this functionality to work, because cancelling out of the date picker has a special meaning (e.g. it clears the date field to an empty string, which for some apps is a valid and meaningful type of input) and using boolean flags to prevent the date from being set twice on OK will not help you in this case.
如果您的应用程序不使用操作栏,则有一个非常简单的解决方法。顺便说一下,一些应用程序依赖此功能来工作,因为取消日期选择器具有特殊含义(例如,它将日期字段清除为空字符串,这对于某些应用程序来说是有效且有意义的输入类型) 并使用布尔标志来防止在 OK 上设置两次日期在这种情况下对您没有帮助。
Re. the actual fix, you do not have to create new buttons or your own dialog. The point is to be compatible with both, the older versions of Android, the buggy ones (4.) and any future ones, though the latter is impossible to be sure about, of course. Note that in Android 2., the onStop() for android.app.Dialog does nothing at all, and in 4.* it does mActionBar.setShowHideAnimationEnabled(false) which is important only if your app has an action bar. The onStop() in DatePickerDialog, which inherits from Dialog, only contributes mDatePicker.clearFocus() (as of the latest fix to Android sources 4.3), which does not seem essential.
关于。实际修复,您不必创建新按钮或您自己的对话框。关键是要与旧版本的 Android、有问题的版本 (4. ) 和任何未来版本兼容,当然后者无法确定。请注意,在 Android 2. 中, android.app.Dialog 的 onStop() 根本不执行任何操作,而在 4.* 中,它执行 mActionBar.setShowHideAnimationEnabled(false) ,这仅在您的应用程序具有操作栏时才重要。继承自 Dialog 的 DatePickerDialog 中的 onStop() 仅提供 mDatePicker.clearFocus()(截至 Android 源 4.3 的最新修复),这似乎不是必需的。
Therefore replacing onStop() with a method that does nothing should in many instances fix your app and ensure that it will remain so for the foreseeable future. Thus simply extend DatePickerDialog class with your own and override onStop() whit a dummy method. You will also have to provide one or two constructors, as per your requirements. Note also that one should not be tempted to try to overdo this fix by e.g. attempting to do something with the activity bar directly, as this would limit your compatibility to the latest versions of Android only. Also note that it would be nice to be able to call the super for DatePicker's onStop() because the bug is only in the onStop() in DatePickerDialog itself, but not in DatePickerDialog's super class. However, this would require you to call super.super.onStop() from your custom class, which Java will not let you do, as it goes against the encapsulation philosophy :) Below is my little class that I used to verride DatePickerDialog. I hope this comment would be useful for somebody. Wojtek Jarosz
因此,用一个什么都不做的方法替换 onStop() 应该在许多情况下修复您的应用程序并确保它在可预见的未来保持不变。因此,只需使用您自己的扩展 DatePickerDialog 类并用一个虚拟方法覆盖 onStop() 即可。您还必须根据您的要求提供一到两个构造函数。另请注意,不应试图通过尝试直接对活动栏执行某些操作来尝试过度修复此修复程序,因为这会限制您仅对最新版本的 Android 的兼容性。另请注意,能够为 DatePicker 的 onStop() 调用 super 会很好,因为该错误仅存在于 DatePickerDialog 本身的 onStop() 中,而不存在于 DatePickerDialog 的超类中。但是,这需要您从自定义类中调用 super.super.onStop(),Java 不会让你这样做,因为它违背了封装哲学:) 下面是我用来验证 DatePickerDialog 的小类。我希望这个评论对某人有用。沃伊泰克·雅罗斯
public class myDatePickerDialog extends DatePickerDialog {
public myDatePickerDialog(Context context, OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) {
super(context, callBack, year, monthOfYear, dayOfMonth);
}
@Override
protected void onStop() {
// Replacing tryNotifyDateSet() with nothing - this is a workaround for Android bug https://android-review.googlesource.com/#/c/61270/A
// Would also like to clear focus, but we cannot get at the private members, so we do nothing. It seems to do no harm...
// mDatePicker.clearFocus();
// Now we would like to call super on onStop(), but actually what we would mean is super.super, because
// it is super.onStop() that we are trying NOT to run, because it is buggy. However, doing such a thing
// in Java is not allowed, as it goes against the philosophy of encapsulation (the Creators never thought
// that we might have to patch parent classes from the bottom up :)
// However, we do not lose much by doing nothing at all, because in Android 2.* onStop() in androd.app.Dialog //actually
// does nothing and in 4.* it does:
// if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
// which is not essential for us here because we use no action bar... QED
// So we do nothing and we intend to keep this workaround forever because of users with older devices, who might
// run Android 4.1 - 4.3 for some time to come, even if the bug is fixed in later versions of Android.
}
}
}
回答by Loc Phan
Here is my workaround class for DatePickerDialog on cancel button as well as abandoning it by back button. Copy&use in style of DatePickerDialog (Because the listener is stateful, we must create new instance when use, otherwise more code is required to make it it works)
这是我在取消按钮上的 DatePickerDialog 以及通过后退按钮放弃它的解决方法类。复制&使用 DatePickerDialog 样式(因为监听器是有状态的,使用时必须创建新的实例,否则需要更多的代码才能使其工作)
Use:
用:
new FixedDatePickerDialog(this,
new FixedOnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
if (isOkSelected()) {
// when DONE button is clicked
}
}
}, year, month, day).show();
Class:
班级:
public class FixedDatePickerDialog extends DatePickerDialog {
private final FixedOnDateSetListener fixedCallback;
public FixedDatePickerDialog(Context context,
FixedOnDateSetListener callBack, int year, int monthOfYear,
int dayOfMonth) {
super(context, callBack, year, monthOfYear, dayOfMonth);
fixedCallback = callBack;
this.setButton(DialogInterface.BUTTON_NEGATIVE,
context.getString(R.string.cancel), this);
this.setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(R.string.done), this);
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == BUTTON_POSITIVE) {
fixedCallback.setOkSelected(true);
} else {
fixedCallback.setOkSelected(false);
}
super.onClick(dialog, which);
}
public abstract static class FixedOnDateSetListener implements
OnDateSetListener {
private boolean okSelected = false;
@Override
abstract public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth);
public void setOkSelected(boolean okSelected) {
this.okSelected = okSelected;
}
public boolean isOkSelected() {
return okSelected;
}
}
}
}