Jelly Bean DatePickerDialog --- 有没有办法取消? [英] Jelly Bean DatePickerDialog --- is there a way to cancel?

查看:20
本文介绍了Jelly Bean DatePickerDialog --- 有没有办法取消?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

--- 版主注意:今天(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 在 Jelly Bean 中已更改 (?),现在仅提供一个 Done 按钮.以前的版本包含一个取消按钮,这可能会影响用户体验(不一致,以前的 Android 版本的肌肉记忆).

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

预期:取消按钮出现在对话框中.

当前:取消按钮不会出现.

屏幕截图: 4.0.3(好的)和 4.1.1(可能错了?).

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

Dialog 调用它应该调用的任何侦听器,然后总是调用 OnDateSetListener 侦听器.取消仍然调用set方法,设置它调用方法两次.

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 代码,但在下面添加代码(你会看到这解决了 #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!");
            }
        });

预期:

  • 按 BACK 键或点击对话框外应该什么都不做.
  • 按取消"应打印选择器取消!.
  • 按设置"应打印选择器设置!.

当前:

  • 按 BACK 键或在对话框外单击会打印 Picker Set!.
  • 按取消"打印选择器取消!,然后打印选择器设置!.
  • 按Set"打印Picker Set!,然后打印Picker Set!.
  • 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.
    • 推荐答案

      注意:从 Lollipop 开始修复来源在这里.自动用于客户端的类(与所有 Android 版本兼容)也更新了.

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

      1. 下载这个课程.
      2. 在您的活动中实现 OnDateSetListener(或更改类以满足您的需要).
      3. 使用此代码触发对话框(在本示例中,我在 Fragment 中使用它):

      1. Download this class.
      2. Implement OnDateSetListener in your activity (or change the class to suit your needs).
      3. 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");
      

      这就是全部!我仍然保持我的答案为已接受"的原因是因为我仍然更喜欢我的解决方案,因为它在客户端代码中占用的空间非常小,它解决了基本问题(在框架类中调用的侦听器)在配置更改时工作正常,并将代码逻辑路由到未受此错误困扰的以前 Android 版本中的默认实现(请参阅类源).

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

      好的,看起来它确实是一个错误并且其他人已经填补了它.问题 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();
      }
      

      我猜可能是:

      @Override
      protected void onStop() {
          // instead of the full tryNotifyDateSet() call:
          if (mCallBack != null) mDatePicker.clearFocus();
          super.onStop();
      }
      

      现在,如果有人能告诉我如何向 Android 提出补丁/错误报告,我会很高兴.同时,我在那里的问题中建议了一个可能的修复(简单)作为 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.

      在构造函数中将侦听器设置为 null 并稍后创建您自己的 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.
      }
      

      所以,诀窍是提供一个 null 侦听器作为侦听器存储,然后滚动您自己的一组按钮(以下是 #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 开始,似乎 --- 当然不能检查 Honeycomb),它赢了不会触发异常. 考虑到 Lollipop 解决了这个问题,我不打算研究它:只需使用默认实现(在我提供的类中涵盖).

      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.

      正如我在下面的评论中指出的那样,这是一个概念,您可以从我的 Google Drive 下载我正在使用的课程帐户.我使用的方式,默认系统实现用于不受错误影响的版本.

      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.

      我做了一些适合我需要的假设(按钮名称等),因为我想将客户端类中的样板代码减少到最少.完整使用示例:

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

      这篇关于Jelly Bean DatePickerDialog --- 有没有办法取消?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆