果冻豆DatePickerDialog-是否可以取消?


148

--- 主持人注意:今天(7月15日),我注意到这里已经有人遇到了这个问题。但是我不确定是否应该将此重复项关闭,因为我认为我对此问题提供了更好的解释。我不确定是否应该编辑另一个问题并将此内容粘贴到那里,但是我不太愿意改变别人的问题。---

我这里有些奇怪

我认为问题不取决于您针对哪个SDK开发。设备操作系统的版本很重要。

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

DatePickerDialog已在Jelly Bean中更改(?),现在仅提供了“完成”按钮。以前的版本包括“ 取消”按钮,这可能会影响用户体验(不一致,以前的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();

预计:一个取消按钮出现在对话框中。

当前:取消”按钮没有出现。

屏幕截图: 4.0.3(确定)和 4.1.1(可能是错误的?)。

问题2:错误的解雇行为

对话框调用确实应调用的任何侦听器,然后始终调用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!");
            }
        });

预期:

  • 按BACK键或在对话框外单击将不起作用
  • 按下“取消”将打印选择器取消!
  • 按下“设置”将打印选择器设置!

当前:

  • 按下BACK键或在对话框外部单击将打印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!

其他注释和评论

  • 将其包裹在a DatePickerFragment没关系。我为您简化了问题,但已对其进行了测试。

恭喜,您似乎在Android中发现了一个错误。您可以在这里报告
迈克尔·汉普顿

1
非常好的书面错误报告。我可以完全理解它,而无需测试运行代码。
Cheok Yan Cheng 2012年

6
我敦促大家投票解决这个问题!发行34833
布拉德利

您不能只是覆盖按钮功能,使其表现为由于在对话框外触摸而被关闭了吗?
Karthik Balakrishnan

2
错误在2年后仍然开放...令人难以置信。
erdomester 2014年

Answers:


115

注意:自棒棒糖开始固定在此处提供用于客户端(与所有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");

这就是全部!我之所以仍将答案保持为“接受”的原因是因为我仍然更喜欢我的解决方案,因为它在客户端代码中的占用空间很小,它解决了基本问题(在框架类中调用了侦听器),并且可以在配置更改中正常工作并将代码逻辑路由到此错误所困扰的早期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();
}

我猜可能是:

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

现在,如果有人可以告诉我如何向Android提出补丁/错误报告,我将很高兴。同时,我DatePickerDialog.java在此处的Issue中建议了一个可能的修复程序(简单)作为附件版本。

避免错误的概念

null在构造函数中将listener设置为,然后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.
}

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

现在,由于我上面发布的可能的更正,它可以工作。

并且由于每当它读取时都会DatePickerDialog.java检查a (自API 3 / 1.5时代以来,它似乎 ---当然无法检查Honeycomb),因此它不会触发异常。nullmCallback考虑到Lollipop解决了这个问题,我不再研究它:仅使用默认实现(在我提供的类中介绍)。

起初,我担心不会调用clearFocus(),但是我已经在这里进行了测试,并且Log行很干净。因此,我提议的那条线甚至可能根本没有必要,但我不知道。

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

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

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

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

11
研究工作很棒!遗憾的是有必要,但仍然很棒。
CommonsWare

1
非常好!对于更新我的文本字段并调用/不调用正确的回调的问题,我一直犹豫不决。谢谢!我想知道建议的解决方法是“面向未来的”还是您认为在修复错误后会导致问题?
跨度

1
@RomainGuidoux参见最新答案。链接中的类具有仅在软糖豆中调用该方法的技巧。在下面的任何内容上,它都会绕过它并使用默认的系统实现,将ondateset的系统调用路由到您的活动。只是在软糖豆中,在路由回调之前需要采取其他措施(避免错误),这涉及调用honeycomb +方法。但是同样,仅在JB中。
davidcsb 2012年

1
很棒的工作在这里。对于这个问题有多么荒谬,我无话可说。
Bill Phillips

5
TimePickerDialog怎么样?看来TimePickerDialog没有getTimePicker()
lokoko

16

我要在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*/ }
  }
}

只需记住:如果您将按钮名称固定为“确定”和“取消”,则最好使用标准android.R.string.okandroid.R.string.cancel字段,而不是用户自己的。并感谢您的答复。
davidcsb 2012年

啊,很好,我没有注意到他们提供了“确定”和“取消”文本。谢谢!
dmon 2012年

在超级(上下文,主题,空,dateToShow.get(YEAR),dateToShow.get(MONTH),dateToShow.get(DAY_OF_MONTH))处获取nullpointer异常;
Kishore

Errr ...您传递的是null dateToShow吗?另一个null实际上是“ fix”,因此应该在那里。您在哪个版本上?
dmon 2012年

你能告诉我import你买了Field哪一个吗?我有6个选项,但没有一个起作用。
Adil Malik

8

在修复该错误之前,我建议不要使用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();

用;更改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();

4

基于大卫·塞萨里诺David Cesarino)的解决方案的TimePicker的解决方案,“ TL; DR:全球解决方案的1-2-3个简单的步骤”

TimePickerDialog不提供类似DatePickerDialog.getDatePicker的功能。因此,必须提供OnTimeSetListener侦听器。为了保持与DatePicker解决方法的相似性,我保留了旧的mListener概念。您可以根据需要进行更改。

调用和侦听器与原始解决方案相同。只包括

import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;

扩展父类,

... implements OnDateSetListener, OnTimeSetListener

实行

 @Override
 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
 ...
 }

示例调用

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

(已更新以处理取消)

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

在s3 API 18上尝试时它对我
不起作用

3

如果有人想要快速解决方法,这是我使用的代码:

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

}

其中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"/>

如果您有兴趣,这里是完整的教程


这对我来说很棒。易于理解,易于实施!经过4.4
erdomester

3

我的简单解决方案。如果您想再次触发它,只需运行“ 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 
    }
}

@AbdelHady您的代码编辑不正确,但我对其进行了编辑以使其更加清晰。无需添加“ isJellyBeanOrAbove()”方法,没有优势,只是增加了不必要的复杂性。调用resetFired很便宜。
Daniel Ryan

这里的代码是错误的,因为onDateSet在以任何方式关闭对话框时都会被调用一次,如果该方式是为了设置时间,那么它将再次被触发,因此我们需要捕获第二个调用,而不是第一个调用和您一样,
AbdelHady 2014年

关于isJellyBeanOrAbove(),低于Jellybean的版本没有整个问题所涉及的错误,并且考虑到我们想捕获第二个调用,除非我们进行此检查,否则相信代码不会运行,相信我,我已经尝试过多次在仿真器和真实设备(具有不同版本)上编写代码,并且它像一种魅力一样工作
AbdelHady 2014年

我不确定这是否值得一票。我在两年前发布了这篇文章,此后一直在我们的商业应用程序中运行良好。我们的质量检查团队或数千名用户未报告任何错误。这是人们可以扩展的简单答案。要再次启动它,请调用“ resetFired”。
Daniel Ryan

在我们的应用程序中,不需要“ isJellyBeanOrAbove”。如果您在正确的区域调用“ resetFired”,则该应用将在所有版本中均能正常运行。
Daniel Ryan

3

根据Ankur Chaudhary 在类似问题上的出色回答TimePickerDialog,如果我们在内部检查onDateSet是否有给定的视图isShown(),它将以最小的努力解决整个问题,而无需扩展选择器或检查代码周围的一些丑陋标志甚至检查操作系统版本,只需执行以下操作:

public void onDateSet(DatePicker view, int year, int month, int day) {
    if (view.isShown()) {
        // read the date here :)
    }
}

当然可以按照安库onTimeSet的答案做同样的事情


1
最好的答案,在所有其他!
hiew1

2

我处理这种情况的方法是使用标志并覆盖onCancel和onDismiss方法。

仅当用户在对话框或后退按钮外部触摸时,才会调用onCancel。onDismiss总是被调用

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

1

如果您的应用程序不使用操作栏,则有一个非常简单的解决方法。顺便说一句,某些应用程序依赖此功能来工作,因为从日期选择器中取消具有特殊含义(例如,它将日期字段清除为空字符串,这对于某些应用程序是有效且有意义的输入类型),并使用布尔值标志阻止将日期设置为“确定”两次,在这种情况下将无济于事。

回覆。实际上,您不必创建新的按钮或自己的对话框。关键是要与旧版本的Android,越野车(4. )和将来的所有版本都兼容,尽管当然不能肯定后者。请注意,在Android 2中,android.app.Dialog的onStop()根本不执行任何操作,而在4. *版本中,它执行mActionBar.setShowHideAnimationEnabled(false),这仅在您的应用具有操作栏时才重要。DatePickerDialog中的onStop()(继承自Dialog)仅提供mDatePicker.clearFocus()(截至Android 4.3的最新修补程序),这似乎不是必需的。

因此,用不执行任何操作的方法替换onStop()在许多情况下应该可以修复您的应用,并确保在可预见的将来保持不变。因此,只需使用您自己的类扩展DatePickerDialog类,并重写onStop()即可创建一个虚拟方法。根据您的要求,您还必须提供一个或两个构造函数。还要注意,不要试图通过例如直接用活动栏来做某事来尝试过度解决此问题,因为这将使您的兼容性仅限于最新版本的Android。还要注意,能够为DatePicker的onStop()调用超级方法会很好,因为该错误仅存在于DatePickerDialog本身的onStop()中,而没有在DatePickerDialog的父类中。但是,这需要您从自定义类中调用super.super.onStop(),哪个Java不允许您这样做,因为它违反了封装哲学:)下面是我用来重写DatePickerDialog的小类。我希望此评论对某人有用。沃伊特克·雅罗斯(Wojtek Jarosz)

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

}


即使使用了ActionBar,如果您从不隐藏/显示ActionBar,这可能也是可以接受的解决方法。为了使事情更加安全,您可以重写onStart也不执行任何操作(然后将安全地取消对动画的调用)。即使您隐藏/显示它,也只是禁用了动画。
丹尼尔(Daniel)

0


尝试以下概念。

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


onDateSet()方法调用两次(如果u在模拟器中签入,则调用两次。如果使用真实设备,则它将一次正确调用。如果使用仿真器,则使用计数器。如果您在真实设备中工作,则
当用户单击DatePickerDialog中的按钮时,忽略计数器变量 。
为此,您应该保持一个计数器值,并且当方法第一次调用时什么都不做,而在方法第二次调用时执行任何操作。
请参考下面的代码片段

   static int counter=0;       //Counter will be declared globally.

    DatePickerDialog picker = new DatePickerDialog(
            this,
            new OnDateSetListener() {
                @Override
                public void onDateSet(DatePicker v, int y, int m, int d) {

                   counter++;
                   if(counter==1) return;
                   counter=0;
                   //Do the operations here

                }
            },
            2012, 6, 15);
    picker.show();



取消datepicker dilalog对我有用。对于模拟器,它不起作用

DialogInterface.OnClickListener dialogOnClickListener=new DialogInterface.OnClickListener()
        {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO Auto-generated method stub

                if(which==Dialog.BUTTON_NEGATIVE)
                {
                    Log.i(tagName, "dialog negative button clicked");
                    dialog.dismiss();
                }

            }

        };

        mDatePickerDialog.setButton(Dialog.BUTTON_NEGATIVE, "Cancel", dialogOnClickListener);


它对我来说是一个真正的设备,但对于模拟器来说,它不能正常工作。我认为这是一个Android模拟器错误。


0

一个简单的解决方案是使用布尔值跳过第二次运行

boolean isShow = false; // define global variable


// when showing time picker
TimePickerDialog timeDlg = new TimePickerDialog( this, new OnTimeSetListener()
            {

                @Override
                public void onTimeSet( TimePicker view, int hourOfDay, int minute )
                {
                    if ( isShow )
                    {
                        isShow = false;
                        // your code
                    }

                }
            }, 8, 30, false );

timeDlg.setButton( TimePickerDialog.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick( DialogInterface dialog, int which )
                {
                    isShow = false;
                }
            } );
timeDlg.setButton( TimePickerDialog.BUTTON_POSITIVE, "Set", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick( DialogInterface dialog, int which )
                {
                    isShow = true;
                }
            } );

timeDlg.show();

再次,就像我告诉过你的其他人,这并没有解决的bug。我不想听起来很粗鲁,但是你们应该在发布本文之前测试自己的解决方案……只是为了证明我的观点,使用您的代码,并让用户按回以取消该对话框,然后明白我的意思。该错误的原因onStop在不应该调用该方法的情况下...触发两次是该错误的结果。
davidcsb 2013年

如果我不够清楚,请让我做:按返回以取消对话框调用onDateSet。因此,坏了。
davidcsb 2013年

感谢您显示错误。我编辑答案,以便可以使用后退按钮。您的答案有效,但DatePicker dp = picker.getDatePicker(); 由于未添加getTimePicker()方法,因此不适用于TimePickers。因此,这将是一个有效的答案
chamikaw

我们如何试图跳过第二轮?!我已经尝试了好几次,当收盘以任何方式对话框onDateSet将被调用一次,但选择当“完成”或“设置”,然后它会被称为两次。因此,我们只需要跳过第一个,因此,如果调用它两次,则&仅然后我们才有正确的日期
AbdelHady 2014年

0

您可以重写onCancel()并使用setOnDismissListener()来检测否定的用户操作。通过DatePickerDialog.BUTTON_POSITIVE,您知道用户想要设置一个新日期。

 DatePickerDialog mDPD = new DatePickerDialog(
                      getActivity(), mOnDateSetListener, mYear, mMonth, mDay);
 mDPD.setOnCancelListener(new OnCancelListener() {
    @Override
    public void onCancel(DialogInterface dialog) {
        // do something onCancek
        setDate = false;
    }
 });

 mDPD.setOnDismissListener(new OnDismissListener() {
    @Override
    public void onDismiss(DialogInterface arg0) {
        // do something onDismiss
        setDate = false;
    }
});

mDPD.setButton(DatePickerDialog.BUTTON_POSITIVE, "Finish", new DatePickerDialog.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // user set new date
        setDate = true;
    }
});

然后检查setDate:

public void onDateSet(DatePicker view, int year, int month, int day) {
    if(setDate){
        //do something with new date
    }
}

0

这是我的取消按钮上的DatePickerDialog的解决方法类,并通过后退按钮放弃了它。以DatePickerDialog样式进行复制和使用(由于侦听器是有状态的,因此在使用时必须创建新实例,否则需要更多代码才能使其工作)

用:

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

类:

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

}


0

我正在使用日期选择器,时间选择器和数字选择器。每当用户选择一个数字时,数字选择器就会在关闭选择器之前调用onValueChanged,因此,我已经有了这样的结构,仅在关闭选择器时才对值进行处理:

public int interimValue;
public int finalValue;

public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
    this.interimValue = newVal;
}

public void onDismiss(DialogInterface dialog) {
    super.onDismiss(dialog);
    this.finalValue = this.interimValue;
}

我将其扩展为为按钮设置自定义onClickListeners,并带有一个参数来查看单击了哪个按钮。现在,我可以在设置最终值之前检查点击了哪个按钮:

public int interimValue;
public int finalValue;
public boolean saveButtonClicked;

public void setup() {
    picker.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.BUTTON_SAVE), new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            picker.onClick(dialog, which); // added for Android 5.0
            onButtonClicked(true);
        }
    });
    picker.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.BUTTON_CANCEL), new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            picker.onClick(dialog, which); // added for Android 5.0
            onButtonClicked(false);
        }
    });
}

public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
    this.interimValue = newVal;
}

public void onButtonClicked(boolean save) {
    this.saveButtonClicked = save;
}

public void onDismiss(DialogInterface dialog) {
    super.onDismiss(dialog);
    if (this.saveButtonClicked) {
        // save
        this.finalValue = this.interimValue;
    } else {
        // cancel
    }
}

然后,我将其扩展为与日期和时间选择器的日期和时间类型以及数字选择器的int类型一起使用。

我发布此消息是因为我认为它比上面的某些解决方案更简单,但是现在我已经包含了所有代码,所以我想它并没有简单得多!但这很适合我已经拥有的结构。

Lollipop的更新:显然,此错误并非在所有Android 4.1-4.4设备上都不会发生,因为我收到了一些日期和时间选择器未调用onDateSet和onTimeSet回调的用户的报告。该错误已在Android 5.0中正式修复。我的方法仅在存在错误的设备上起作用,因为我的自定义按钮没有调用对话框的onClick处理程序,这是在不存在错误的情况下调用onDateSet和onTimeSet的唯一位置。我更新了上面的代码以调用对话框的onClick,因此无论是否存在该错误,现在都可以使用。


0

我喜欢上面的大卫·塞萨里诺(David Cesarino)的答案,但我希望它可以代替损坏的对话框,并且可以在可能缺少取消/取消行为不正确的任何对话框上使用。这是DatePickerDialog / TimePickerDialog的派生类,应作为替代品使用。这些不是自定义视图。它使用系统对话框,但只是将取消/后退按钮的行为更改为可以正常工作。

这应该适用于API级别3和更高级别。因此,基本上是任何版本的Android(我分别在豆形软糖和棒棒糖上进行了测试)。

DatePickerDialog:

package snappy_company_name_here;

import android.content.Context;
import android.content.DialogInterface;
import android.widget.DatePicker;

/**
 * This is a modified version of DatePickerDialog that correctly handles cancellation behavior since it's broken on jellybean and
 * kitkat date pickers.
 *
 * Here is the bug: http://code.google.com/p/android/issues/detail?id=34833
 * Here is an SO post with a bunch of details: http://stackoverflow.com/questions/11444238/jelly-bean-datepickerdialog-is-there-a-way-to-cancel
 *
 * @author stuckj, created on 5/5/15.
 */
public class DatePickerDialog extends android.app.DatePickerDialog implements DialogInterface.OnClickListener
{
    final CallbackHelper callbackHelper;

    // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
    private static class CallbackHelper implements OnDateSetListener
    {
        private final OnDateSetListener callBack;
        private boolean dialogButtonPressHandled = false; // To prevent setting the date when the dialog is dismissed...

        // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
        public CallbackHelper(final OnDateSetListener callBack)
        {
            this.callBack = callBack;
        }

        @Override
        public void onDateSet(final DatePicker view, final int year, final int monthOfYear, final int dayOfMonth)
        {
            if (!dialogButtonPressHandled && (callBack != null))
            {
                callBack.onDateSet(view, year, monthOfYear, dayOfMonth);
            }
        }
    }

    /**
     * Sets the positive and negative buttons to use the dialog callbacks we define.
     */
    private void setButtons(final Context context)
    {
        setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), this);
        setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), this);
    }

    @Override
    public void onClick(final DialogInterface dialog, final int which)
    {
        // ONLY call the super method in the positive case...
        if (which == DialogInterface.BUTTON_POSITIVE)
        {
            super.onClick(dialog, which);
        }

        callbackHelper.dialogButtonPressHandled = true;
    }

    @Override
    public void onBackPressed()
    {
        getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private DatePickerDialog(final Context context,
                             final OnDateSetListener callBack,
                             final int year,
                             final int monthOfYear,
                             final int dayOfMonth,
                             final CallbackHelper callbackHelper)
    {
        super(context, callbackHelper, year, monthOfYear, dayOfMonth);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @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(final Context context,
                            final OnDateSetListener callBack,
                            final int year,
                            final int monthOfYear,
                            final int dayOfMonth)
    {
        this(context, callBack, year, monthOfYear, dayOfMonth, new CallbackHelper(callBack));
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private DatePickerDialog(final Context context, final int theme, final OnDateSetListener listener, final int year,
                             final int monthOfYear, final int dayOfMonth, final CallbackHelper callbackHelper)
    {
        super(context, theme, callbackHelper, year, monthOfYear, dayOfMonth);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context The context the dialog is to run in.
     * @param theme the theme to apply to this dialog
     * @param listener 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(final Context context, final int theme, final OnDateSetListener listener, final int year,
                            final int monthOfYear, final int dayOfMonth)
    {
        this(context, theme, listener, year, monthOfYear, dayOfMonth, new CallbackHelper(listener));
    }
}

TimePickerDialog:

package snappy_company_name_here;

import android.content.Context;
import android.content.DialogInterface;
import android.widget.TimePicker;

/**
 * This is a modified version of TimePickerDialog that correctly handles cancellation behavior since it's broken on jellybean and
 * kitkat date pickers.
 *
 * Here is the bug: http://code.google.com/p/android/issues/detail?id=34833
 * Here is an SO post with a bunch of details: http://stackoverflow.com/questions/11444238/jelly-bean-datepickerdialog-is-there-a-way-to-cancel
 *
 * @author stuckj, created on 5/5/15.
 */
public class TimePickerDialog extends android.app.TimePickerDialog implements DialogInterface.OnClickListener
{
    final CallbackHelper callbackHelper;

    // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
    private static class CallbackHelper implements OnTimeSetListener
    {
        private final OnTimeSetListener callBack;
        private boolean dialogButtonPressHandled = false; // To prevent setting the date when the dialog is dismissed...

        // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
        public CallbackHelper(final OnTimeSetListener callBack)
        {
            this.callBack = callBack;
        }

        @Override
        public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute)
        {
            if (!dialogButtonPressHandled && (callBack != null))
            {
                callBack.onTimeSet(view, hourOfDay, minute);
            }
        }
    }

    /**
     * Sets the positive and negative buttons to use the dialog callbacks we define.
     */
    private void setButtons(final Context context)
    {
        setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), this);
        setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), this);
    }

    @Override
    public void onClick(final DialogInterface dialog, final int which)
    {
        // ONLY call the super method in the positive case...
        if (which == DialogInterface.BUTTON_POSITIVE)
        {
            super.onClick(dialog, which);
        }

        callbackHelper.dialogButtonPressHandled = true;
    }

    @Override
    public void onBackPressed()
    {
        getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private  TimePickerDialog(final Context context,
                              final OnTimeSetListener callBack,
                              final int hourOfDay, final int minute, final boolean is24HourView, final CallbackHelper callbackHelper)
    {
        super(context, callbackHelper, hourOfDay, minute, is24HourView);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context Parent.
     * @param callBack How parent is notified.
     * @param hourOfDay The initial hour.
     * @param minute The initial minute.
     * @param is24HourView Whether this is a 24 hour view, or AM/PM.
     */
    public TimePickerDialog(final Context context,
                            final OnTimeSetListener callBack,
                            final int hourOfDay, final int minute, final boolean is24HourView)
    {
        this(context, callBack, hourOfDay, minute, is24HourView, new CallbackHelper(callBack));
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private TimePickerDialog(final Context context, final int theme, final OnTimeSetListener callBack, final int hourOfDay,
                            final int minute, final boolean is24HourView, final CallbackHelper callbackHelper)
    {
        super(context, theme, callbackHelper, hourOfDay, minute, is24HourView);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context Parent.
     * @param theme the theme to apply to this dialog
     * @param callBack How parent is notified.
     * @param hourOfDay The initial hour.
     * @param minute The initial minute.
     * @param is24HourView Whether this is a 24 hour view, or AM/PM.
     */
    public TimePickerDialog(final Context context, final int theme, final OnTimeSetListener callBack, final int hourOfDay,
                            final int minute, final boolean is24HourView)
    {
        this(context, theme, callBack, hourOfDay, minute, is24HourView, new CallbackHelper(callBack));
    }
}

这与上述海洋的答案非常相似。
stickj 2015年

0

我使用Lambda表达式的ClearButton的工作版本:

public class DatePickerFragment extends DialogFragment {
    private OnDateSelectListener dateSelectListener;
    private OnDateClearListener dateClearListener;

    public void setDateSelectListener(OnDateSelectListener dateSelectListener) {
        this.dateSelectListener = dateSelectListener;
    }

    public void setDateClearListener(OnDateClearListener dateClearListener) {
        this.dateClearListener = dateClearListener;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current date as the default date in 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);

        // Create a new instance of DatePickerDialog and return it
        DatePickerDialog dialog = new DatePickerDialog(getActivity(), null, year, month, day);
        dialog.setCancelable(true);
        dialog.setCanceledOnTouchOutside(true);
        dialog.setTitle("Select Date");
        dialog.setButton(BUTTON_POSITIVE, ("Done"), (dialog1, which) -> {
            DatePicker dp = dialog.getDatePicker();
            dialog.dismiss();
            dateSelectListener.onDateSelect(dp.getYear(), dp.getMonth(), dp.getDayOfMonth());
        });
        dialog.setButton(BUTTON_NEUTRAL, ("Clear"), (dialog1, which) -> {
            dialog.dismiss();
            dateClearListener.onDateClear();
        });
        dialog.setButton(BUTTON_NEGATIVE, ("Cancel"), (dialog1, which) -> {
            if (which == DialogInterface.BUTTON_NEGATIVE) {
                dialog.cancel();
            }
        });
        dialog.getDatePicker().setCalendarViewShown(false);
        return dialog;
    }


    public interface OnDateClearListener {
        void onDateClear();
    }

    public interface OnDateSelectListener {
        void onDateSelect(int year, int monthOfYear, int dayOfMonth);
    }
}

0

对于TimePickerDialog,解决方法如下:

TimePickerDialog createTimePickerDialog(Context context, int themeResId, TimePickerDialog.OnTimeSetListener orignalListener,
                                                         int hourOfDay, int minute, boolean is24HourView) {
        class KitKatTimeSetListener implements TimePickerDialog.OnTimeSetListener {
            private int hour;
            private int minute;

            private KitKatTimeSetListener() {
            }

            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                this.hour = hourOfDay;
                this.minute = minute;
            }

            private int getHour() { return hour; }
            private int getMinute() {return minute; }
        };

        KitKatTimeSetListener kitkatTimeSetListener = new KitKatTimeSetListener();
        TimePickerDialog timePickerDialog = new TimePickerDialog(context, themeResId, kitkatTimeSetListener, hourOfDay, minute, is24HourView);

        timePickerDialog.setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), (dialog, which) -> {
            timePickerDialog.onClick(timePickerDialog, DialogInterface.BUTTON_POSITIVE);
            orignalListener.onTimeSet(new TimePicker(context), kitkatTimeSetListener.getHour(), kitkatTimeSetListener.getMinute());
            dialog.cancel();
        });
        timePickerDialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), (dialog, which) -> {
            dialog.cancel();
        });

        return timePickerDialog;
    }

我将所有事件委托给包装器KitKatSetTimeListener,并且仅在单击BUTTON_POSITIVE的情况下才触发回原始的OnTimeSetListener。


0

在测试了此处发布的一些提示之后,我个人认为此解决方案是最简单的。我在DatePickerDialog构造函数中将“ null”作为侦听器传递,然后单击“确定”按钮时,我将调用onDateSearchSetListener:

datePickerDialog = new DatePickerDialog(getContext(), null, dateSearch.get(Calendar.YEAR), dateSearch.get(Calendar.MONTH), dateSearch.get(Calendar.DAY_OF_MONTH));
    datePickerDialog.setCancelable(false);
    datePickerDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.d("Debug", "Correct");
            onDateSearchSetListener.onDateSet(datePickerDialog.getDatePicker(), datePickerDialog.getDatePicker().getYear(), datePickerDialog.getDatePicker().getMonth(), datePickerDialog.getDatePicker().getDayOfMonth());
        }
    });
    datePickerDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.d("Debug", "Cancel");
            dialog.dismiss();
        }
    });

-1

我知道这篇文章已经存在了将近一年,但我认为我应该发表自己的发现。您仍然可以保留侦听器(而不是将其设置为Mull),并且仍可以按预期进行此工作。关键是隐式设置“确定”或(和)“取消”按钮。我测试了它,并为我感到满意。监听器不会被解雇两次。

看这个例子

private void setTime(){
final Calendar c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);

final TimePickerDialog timepicker = new TimePickerDialog(this.getActivity(),
        timePickerListener,
        hour, 
        minute, 
        DateFormat.is24HourFormat(getActivity()));

timepicker.setButton(DialogInterface.BUTTON_POSITIVE, "Print", new    
    android.content.DialogInterface.OnClickListener(){
        @Override
        public void onClick(DialogInterface dialog,int which) {
            print = true;
            timepicker.dismiss();           
        }
});

timepicker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new 
    android.content.DialogInterface.OnClickListener(){
        @Override
        public void onClick(DialogInterface dialog,int which){
            print = false;
            timepicker.dismiss();       
        }
});

timepicker.setCancelable(false);
timepicker.show();
}

简单明了,它不能按照您所说的那样工作。timePickerListener无论您在对话框中做什么,仍会调用。我什至不需要测试就知道,您只需要查看:如果您未将其设置为nulltryNotifyTimeSet()则将onTimeSet()onClick()和中调用侦听器onStop()
davidcsb

换句话说,千万不能让本机类持有到您的收听参考(即,通过它在构造函数)。与按钮的处理方式无关。
davidcsb

大卫,您好:您没有对此进行测试,并且您认为它不会起作用。这是我目前在我的应用程序中使用的确切代码,它像冠军一样工作。
鳄鱼2013年

1)我从没说过这没用。我说这没像你说的那样起作用。请再次阅读。2)你违反了LSP和SRP,浪费工时不必要改变所有整个这并不需要任何变化,开始与客户端逻辑。3)你的答案地址的问题,是的,否则,我将其标记为清除为“不回答”,但4)你的答案仍然是(抱歉这件事)非常低效,没有解决根本的设计问题(侦听器调用),因此只有下降投票权。6)您禁用了它,使其成为强制布尔的模态窗口。因此,那里有6个严重问题!
davidcsb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.