果冻豆DatePickerDialog ---有没有办法取消?没有办法、果冻、DatePickerDialog

2023-09-11 11:52:47 作者:骚年先疯队队长

--- 注意给版主:今天(7月15日),我注意到,有人已经面临这个问题的此处。但我不知道这是否是合适的关闭这是一个重复的,因为我认为我提供了一个更好的解释这个问题。我不知道我是否应该修改的其他问题,并粘贴此内容存在,但我不舒服改变别人的问题太多了。的---

--- 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. ---

我有一些的怪异的位置。

我不认为这个问题取决于它的SDK,构建抵御。该设备的操作系统版本是最重要的。

I don't think the problem depends on which SDK you build against. The device OS version is what matters.

DatePickerDialog 变更(?)的果冻豆,目前只提供了一个的完成的按钮。 previous版本包括的取消的按钮,这可能会影响用户(由previous的Andr​​oid版本不一致,肌肉记忆)的经验。

DatePickerDialog was changed (?) in Jelly Bean and now only provides a Done button. Previous versions included a Cancel button, and this may affect user experience (inconsistency, muscle memory from previous Android versions).

复制:的创建的基础工程。把这个的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();

预计:的 A 取消的按钮出现在对话框中

Expected: A Cancel button to appear in the dialog.

电流:的 A 取消的按钮没有出现

截图:的 4.0.3 (OK)和 4.1.1 (可能是错的?)。

Screenshots: 4.0.3 (OK) and 4.1.1 (possibly wrong?).

对话框通话监听为准应该调用的确,然后的总是的通话 OnDateSetListener 监听器。取消还是调用设置方法,并设置它调用该方法的两倍。

Dialog calls whichever listener it should call indeed, and then always calls OnDateSetListener listener. Canceling still calls the set method, and setting it calls the method twice.

复制:的使用#1 code,但增加了以下code(你会看到这解决了#1,但只有在视觉/ UI)

Replicate: Use #1 code, but add code below (you'll see this solves #1, but only visually/UI):

picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d("Picker", "Cancel!");
            }
        });

预计:的

Expected:

pressing BACK键或单击对话框外应束手无策 pressing取消应该打印的选择器取消! pressing设置应该打印的选择器设置! Pressing the BACK key or clicking outside the dialog should do nothing. Pressing "Cancel" should print Picker Cancel!. Pressing "Set" should print Picker Set!.

电流:的

Current:

pressing BACK键或单击对话框中的打印选择器设置! 外 pressing取消版画选择器取消!,然后选择器设置! pressing设置版画选择器设置!,然后选择器设置! 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!.

日志行显示的行为:

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!

其他注释和评论

在结束语围绕 DatePickerFragment 没关系。我简单的问题你,但我测试过了。

Other notes and comments

Wrapping it around a DatePickerFragment doesn't matter. I simplified the problem for you, but I've tested it.

推荐答案

注:的固定为棒棒糖,source这里。自动类的客户使用(包括所有的Andr​​oid版本兼容)更新。的

Note: Fixed as of Lollipop, source here. Automated class for use in clients (compatible with all Android versions) updated as well.

在下载这个类。 实施 OnDateSetListener 在你的活动(或更改类,以满足您的需求)。

触发此code对话框(在此示例中,我用它在片段): Download this class. 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");

而这一切都需要!的原因,我还是保持我的回答是接受,是因为我仍然preFER我的解决方案,因为它有一个非常小的足迹在客户端code ,它解决了根本问题(监听器被调用的框架类),整个配置的变化能正常工作,它的路线的code逻辑在不受此漏洞的困扰previous的Andr​​oid版本的默认实现(见类源)。

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).

OK,看起来像它确实是一个错误,其他人已经装满了。 发行34833 。

OK, looks like it's indeed a bug and someone else already filled it. Issue 34833.

我发现,这个问题可能是在 DatePickerDialog.java 。其中记载:

I've found that the problem is possibly in DatePickerDialog.java. Where it reads:

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();
}

现在,如果有人能告诉我,我怎么能提出到Android补丁/ bug报告,我会很高兴。同时,我提出了一个可能的修复(简单)为 DatePickerDialog.java 中的问题有一个附加的版本。

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.

构造函数中的监听器设置为,后来创建自己的 BUTTON_POSITIVE 按钮。就这样,详情如下。

Set the listener to null in the constructor and create your own BUTTON_POSITIVE button later on. That's it, details below.

这个问题是因为 DatePickerDialog.java ,你可以在源代码中看到,调用一个全局变量( mCallBack ),存储已传递在构造函数中的监听器:

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:

    /**
 * @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.
}

因此​​,关键是要提供一个监听器将存储作为侦听器,然后推出自己的按钮(下面是原来的code #1,更新中):

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):

    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.

此外,由于 DatePickerDialog.java 检查一个时,它读取 mCallback (因为API 3天/1.5似乎的---不能检查,当然蜂窝),也不会引发异常考虑棒棒堂固定的问题,我不打算调查一下:只要使用默认的实现(包括在我公司提供的类)。

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).

起初我很害怕没有要求的 clearFocus(),但我在这里测试,日志行很干净。因此,该行建议我可能甚至不毕竟必要的,但我不知道。

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.

我刚才在下面的评论,这是一个概念,你可以下载我使用的类从我的谷歌驱动器帐户。我用的方式,默认的系统实现上使用不会受错误的版本。

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.

我花了几个假设(按钮名等),适合我的需要,因为我想,以减少样板code在客户类到最低限度。全部使用例子:

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");