当关闭TimePickerDialog时,OnTimeSet也会被调用。

27
今天我尝试使用TimePickerDialog,但是我注意到了一些缺陷。
  1. 当对话框被取消时(例如通过点击外部区域),也会调用OnTimeSet。
  2. 当用户点击“完成”按钮时,OnTimeSet将被调用两次。
我正在使用的API版本为18。
其他人是否遇到了这些问题?你们是如何解决的?
5个回答

35
您应该使用View类已经提供的方法:
new TimePickerDialog.OnTimeSetListener() {
    @Override
    public void onTimeSet(TimePicker view, int hour, int minute) {  
        if (view.isShown()) {
            // This method will return true only once...
        }
    }
};

1
谢谢,这是一个非常令人沮丧的问题! - Jdruwe
2
但是这个逻辑在三星4.0.4版本中不起作用。view.isShown()总是返回false。 - Shanki Bansal
@ShankiBansal 是的,在4.0.4版本上它不起作用,因此您可以进行操作系统版本检查。如果版本低于Jelly Bean,则无需使用view.isshown。 如果您仍然遇到问题或找到其他方法,请告诉我。 - Ankur Chaudhary
这肯定是被接受的答案,这是一个非常令人沮丧的问题,我讨厌它,但喜欢你的回答 :) - AbdelHady
现在已经是2017年了,但是Google仍然没有解决这个bug。 - KuLdip PaTel

16

今天我遇到了完全相同的问题。我无法弄清楚为什么会发生这种情况,但是找到了一个简单的解决方法:

当对话框被关闭时,方法onTimeSet()被调用一次,在点击“完成”按钮时将被调用两次。无论哪种情况,都会产生一个不必要的onTimeSet()调用。因此,我决定始终忽略第一次调用。

这是代码:

Calendar mcurrentTime = Calendar.getInstance();
int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY);
int minute = mcurrentTime.get(Calendar.MINUTE);

TimePickerDialog mTimePicker;
mTimePicker = new TimePickerDialog(MainActivity.this, new TimePickerDialog.OnTimeSetListener() 
    {
        int callCount = 0;   //To track number of calls to onTimeSet()

        @Override
        public void onTimeSet(TimePicker timePicker, int selectedHour, int selectedMinute) 
        {
             if(callCount == 1)    // On second call
             {
                 timeString = selectedHour + ":" + selectedMinute + ":00";
                 Log.d("TEST", "Chosen time : "+ timeString);           
             }

             callCount++;    // Incrementing call count.

        }
    }, hour, minute, true);

    mTimePicker.setTitle("Pick Time");
    mTimePicker.show();

我以同样的方式处理它(还有其他方法吗?),但我讨厌这些Android的bug。顺便说一下,当callCount为1时,你不应该将其重置为0吗? - Alessandro Roaro
callCount 不需要重置,因为每次打开日期选择对话框时都会创建一个新的 mTimePicker 实例。 - Tony
有点奇怪它被调用了两次,但这是默认行为。这似乎是一个便宜而快速的解决方法,肯定会使用它。 - kabuto178
3
在三星4.0.4版本中,onTimeSet()方法只会被调用一次。因此,你的逻辑将不再起作用。你需要在那里添加一个版本检查。 - Shanki Bansal

5

再次强调:这是Android中多个对话框类型中的一个已确认的错误。已经提出了两种解决方法,一种是将状态保存在(实例)变量中,另一种是询问对话框是否isShown()。但是,在Android 4.0.4中,isShown()似乎不可靠,并且如果要重新显示对话框,则保存状态会变得混乱。
更好的解决方案是在对话框内部保存状态,因为调用该方法的是同一个实例:

public void onDateSet(DatePicker picker, int year, int monthOfYear, int dayOfMonth) {
    if (picker.getTag() == null) {
        picker.setTag("TAGGED");
        // Only gets called once per Dialog
    }
}

这很简洁有效。


2
使用计数来避免问题。当 TimePickDialog 被选中超过两次时,它也应该能正常工作。
            TimePickerDialog tpd = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {
            int count = 0;
            @Override
            public void onTimeSet(TimePicker view, int setHour, int setMinute) {
                if(count % 2 == 0) {
                   //set time here
                }
                count++;

            } }, hour, minute, true);

这个解决方案最好的地方是不需要检查版本。 - Chintan Shah

1
感谢Tony发布的解决方法。这个方法大多数情况下都有效,但并非总是如此。我们发布了带有此解决方法(以及版本检查)的应用程序;然而,在三星Galaxy Note GT-8000(Android 4.4.2)上,此解决方案失败了。默认的4.4.2设备存在此bug,解决方案有效,但三星似乎已经在4.4.2版本中修复了此问题,所以onTimeSet()仅被调用一次,我们忽略了第二次调用。

我们正在发布一个解决方案,今天我们应用了它。虽然我对这个解决方案不太满意,因为它又是一个hack/workaround,但它可能有助于在版本检查无法帮助和OEM合并选择性修复的情况下使用。

我们早期的实现方式是

                        if((android.os.Build.VERSION.SDK_INT >=
                            Build.VERSION_CODES.ICE_CREAM_SANDWICH) &&
                            (android.os.Build.VERSION.SDK_INT <
                                    Build.VERSION_CODES.LOLLIPOP)){
                        if(ccount == 1){
                            // Do Your Processing
                            count = 0;
                        }else{
                            // Ignore event. Bug in Android API
                            count++;
                        }
                    }else{
                        // Do Your Processing
                    }

我们的新实现是

                        if((android.os.Build.VERSION.SDK_INT >=
                            Build.VERSION_CODES.ICE_CREAM_SANDWICH) &&
                            (android.os.Build.VERSION.SDK_INT <
                                    Build.VERSION_CODES.LOLLIPOP)){
                        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
                        StackTraceElement e = stacktrace[4];
                        String methodName = e.getMethodName();
                        if(methodName.equals("onClick")){
                            // Do Your Processing
                        }else{
                            // Ignore event. Bug in Android API
                        }
                    }else{
                        // Do Your Processing
                    }

希望它能帮助其他人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接