Is there a way to use cancel in Android JellyBean TimePickerDialog?
Asked Answered
S

2

6

As far as I am aware, versions prior to Jelly Beans, had Set and Cancel buttons when using TimePickerDialog. Jelly Beans has only Done button.

I could just leave the Done button, and could close the dialog by clicking outside the dialog, but my onTimeSetListener gets called if pressing Done or outside the dialog.

So I found this stackoverflow question which describes, that there is a bug when using DatePickerDialog. To fix the issue using DatePickerDialog, when initializing I had to set the onDateSetListener to null, and implement my own buttons to handle the BUTTON_POSITIVE (Set) and BUTTON_NEGATIVE (Cancel) onClick method. Which is fine, because when button Set is called I can access the DatePicker values like this

int yearPicked = dateDlg.getDatePicker().getYear();
int monthPicked = dateDlg.getDatePicker().getMonth();
int dayPicked = dateDlg.getDatePicker().getDayOfMonth();

So there is no need to use onDateSetListener, but if I would, it would again be called when pressing Set or Cancel.

I tried to use TimePickerDialog the same way, but the problem is, that inside BUTTON_POSITIVE onClick method, I cannot access the hour and minutes values as before, because TimePickerDialog does not provide TimePicker as DatePickerDialog provides DatePicker. And again if I would used onTimeSetListener, it would be called by pressing anything.

Calendar cal = Calendar.getInstance();

int hour = cal.get(Calendar.HOUR_OF_DAY);
int min = cal.get(Calendar.MINUTE);

final TimePickerDialog timeDlg = new TimePickerDialog(PreferencesActivity.this, null, hour, min, true);

// Make the Set button
timeDlg.setButton(DialogInterface.BUTTON_POSITIVE, "Set", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
        if (which == DialogInterface.BUTTON_POSITIVE) {
            // CANNOT ACCES THE VALUES
            Toast.makeText(PreferencesActivity.this, "Set", Toast.LENGTH_SHORT).show();
        }
    }
});


// Set the Cancel button
timeDlg.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
        if (which == DialogInterface.BUTTON_NEGATIVE) {
            Toast.makeText(PreferencesActivity.this, "Cancel", Toast.LENGTH_SHORT).show();
        }
    }
});

timeDlg.show();
S answered 30/12, 2012 at 15:50 Comment(3)
Well, you can still register a TimePickerDialog.OnTimeSetListener to capture the values entered, but simply don't do anything with them if the negative button is tapped, right?Coan
That is the problem, OnTimeSetListener is called when positive, negative and back button is pressed.S
I had the same problem, note that the accepted answer below is not backward compatible (pre jb). Sice I needed sdk 8+, I solved it with a boolean inside an extended fragment dialog, and overrided onClick() there.Katerine
C
8

Based on your comment, it should be easy enough to guard the OnTimeSetListener callback with a boolean to prevent your logic from running in the case 'Cancel' is clicked.

I had a quick play with your code, and the following seemed to work alright:

private boolean mIgnoreTimeSet = false;

final TimePickerDialog timeDlg = new TimePickerDialog(PreferencesActivity.this, PreferencesActivity.this, hour, min, true);

// Make the Set button
timeDlg.setButton(DialogInterface.BUTTON_POSITIVE, "Set", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
        mIgnoreTimeSet = false;
        // only manually invoke OnTimeSetListener (through the dialog) on pre-ICS devices
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) timeDlg.onClick(dialog, which);
        Toast.makeText(getApplicationContext(), "Set", Toast.LENGTH_SHORT).show();
    }
});

// Set the Cancel button
timeDlg.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
        Toast.makeText(getApplicationContext(), "Cancel", Toast.LENGTH_SHORT).show();
        mIgnoreTimeSet = true;
        dialog.cancel();
    }
});

timeDlg.show();

@Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
    if (mIgnoreTimeSet) return;
    // get the values here - this will only run if 'Set' was clicked
}

For the sake of the example, I've assumed your PreferencesActivity implements OnTimeSetListener. Since onTimeSet(...) gets called after onClick(...), you can simply toggle a boolean that will determine whether you should do something with the values in the callback or just ignore them.

I have to agree it's not ideal, but at least functional. After the holidays I'll try to have another look in a 'better' solution.

One alternative would be to use reflection to get a handle on the internally used TimePicker widget, but that's more likely to break in future Android releases.


// Edit: Potential alternative? (not tested)

Second alternative, which might be a bit 'cleaner': extend TimePickerDialog and overwrite the (empty) public method:

public void onTimeChanged(TimePicker view, int hourOfDay, int minute)

In there, keep track of the user-set hour of the day and minute and implement two getters that return them. Then only get these values from the dialog if the positive button gets clicked, but just fall through for 'Cancel'. Basically you then won't need an OnTimeSetListener anymore, and hence it won't matter whether it is ever gets called or not.


// Edit2: Support for pre-ICS devices

After receiving a couple of remarks about the proposed solution not working on pre-ICS devices, I made a small change to the above code - even tough it wasn't within the scope of the original question.

The main difference between pre-ICS and post-ICS devices is that the OnTimeSetListener callback isn't automatically invoked after your dialog button's onClick() method(s) is run. A simple workaround for that is to call the onClick() method on the dialog, which will then call the listener. In the solution above, I've added a version code check, because otherwise your listener will end up being called twice on post-ICS devices, which may lead to undesired side-effects.

Tested and confirmed behaviour on Android 2.1-update1 and Android 4.2.2.

Coan answered 31/12, 2012 at 5:45 Comment(2)
Thank you for your answer, spend a day playing with the code and searching. The trick with boolean works nice. My PreferecesActivity does not implement OnTimeListener, but I got the idea.S
@AdilMalik: after reading some of the comments saying the proposed solution doesn't work on pre-ICS devices, I made a small change to accommodate for that. Please refer to the 2nd edit for more details.Coan
A
1

I found this thread when experiencing the same "Done-button-only" problem with the TimePickerDialog for Jelly Bean on my Nexus 7 (my Nexus One, Gingerbread, having Set and Cancel buttons for that same TimePickerDialog). It would appear, however, that with a subsequent update to 4.3 Jelly Bean on my Nexus 7 (Build Number JWR66Y) that the proper buttons, Set and Cancel, have returned.

In researching this problem, however, I did come across this Stackoverflow thread which rather meticulously outlines bigger problems with DatePickerDialog (with similar issues indicated for TimePickerDialog in a referenced bug report) and offers a solution.

Beware that using these dialogs must be considered carefully since they do not function correctly in certain versions of Android (the solution offered in the aforementioned thread isn't being used "in production code", according to its author).

Anjelicaanjou answered 17/10, 2013 at 3:42 Comment(1)
Nice to hear that the problem has been fixed with the latest version. ThanksS

© 2022 - 2024 — McMap. All rights reserved.