Jelly Bean DatePickerDialog ---有没有办法取消?

dav*_*ino 147 android datepicker android-datepicker android-4.2-jelly-bean

--- 主持人注意:今天(7月15日),我注意到有人在这里遇到过这个问题.但是我不确定将这个作为副本关闭是否合适,因为我认为我提供了一个更好的解释.我不确定是否应该编辑其他问题并将其粘贴到那里,但我不太愿意过多地改变别人的问题.---

我有一些奇怪的东西.

我不认为问题取决于您构建的SDK.设备操作系统版本是重要的.

问题#1:默认情况下不一致

DatePickerDialog在Jelly Bean中被更改了(?),现在只提供了一个Done按钮.以前的版本包括一个取消按钮,这可能会影响用户体验(不一致,以前Android版本的肌肉记忆).

复制:创建一个基本项目.把它放进去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();
Run Code Online (Sandbox Code Playgroud)

预期:在对话框中显示"取消"按钮.

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

截图: 4.0.3(OK)和 4.1.1(可能错误?).

问题#2:错误的解雇行为

Dialog会调用它应该调用的侦听器,然后始终调用OnDateSetListener侦听器.取消仍然调用set方法,并设置它调用方法两次.

复制:使用#1代码,但在下面添加代码(您将看到这解决了#1,但只能在视觉上/ UI):

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

预期:

  • 按BACK键或在对话框外单击不应该执行任何操作.
  • 按"取消"应该打印Picker取消!.
  • 按"设置"应该打印Picker Set!.

当前:

  • 按BACK键或在对话框外单击打印Picker Set!.
  • 按"取消"打印选择器取消!然后选择器套装!.
  • 按"设置"打印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!
Run Code Online (Sandbox Code Playgroud)

其他说明和评论

  • 把它包裹起来DatePickerFragment并不重要.我为你简化了问题,但我已经测试过了.

dav*_*ino 115

注意:固定为Lollipop,源于此处.用于客户端的自动(与所有Android版本兼容)也已更新.

TL; DR:1-2-3全球解决方案的简单步骤:

  1. 下载这个课程.
  2. OnDateSetListener在您的活动中实施(或根据您的需要更改课程).
  3. 使用此代码触发对话框(在此示例中,我在其中使用它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");
    
    Run Code Online (Sandbox Code Playgroud)

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

原始答案(保留历史和教学原因):

错误来源

好吧,看起来确实是一个错误而其他人已经填满了它.问题34833.

我发现问题可能在于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();
}
Run Code Online (Sandbox Code Playgroud)

我猜它可能是:

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

现在,如果有人能告诉我如何向Android提出补丁/错误报告,我会很高兴.与此同时,我提出了一个可能的修复(简单)作为问题的附加版本DatePickerDialog.java.

避免错误的概念

null在构造函数中设置侦听器,BUTTON_POSITIVE稍后创建自己的按钮.就是这样,详情如下.

出现问题的原因是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.
}
Run Code Online (Sandbox Code Playgroud)

因此,诀窍是提供一个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();
Run Code Online (Sandbox Code Playgroud)

现在它会起作用,因为我上面发布了可能的修正.

并且由于DatePickerDialog.java检查null它何时读取mCallback(从API 3/1.5的日期看起来 ---当然不能检查Honeycomb),它不会触发异常.考虑到Lollipop解决了这个问题,我不打算研究它:只使用默认的实现(在我提供的类中提到).

起初我害怕不打电话clearFocus(),但我已经在这里测试过,Log系统很干净.所以我提出的那条线毕竟可能根本不需要,但我不知道.

与先前API级别的兼容性(已编辑)

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

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

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");
Run Code Online (Sandbox Code Playgroud)

  • 这项研究非常棒!遗憾的是,这是必要的,但仍然很棒. (11认同)
  • TimePickerDialog怎么样?似乎TimePickerDialog没有getTimePicker() (5认同)

dmo*_*mon 16

我将在David Cesarino发布的解决方案中添加我自己的riff,以防你没有使用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*/ }
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 8

在修复bug之前,我建议不要使用DatePickerDialog或TimePickerDialog.使用自定义AlertDialog和TimePicker/DatePicker小部件;

更改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();
Run Code Online (Sandbox Code Playgroud)

用;更改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();
Run Code Online (Sandbox Code Playgroud)