Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Reply
Developer
Ted_Hopp
Posts: 1,305
Registered: ‎01-21-2009
My Device: Not Specified

Save, Discard, Cancel, and all that

[ Edited ]

The seems to be a slow but never-ending stream of posts on the forum about how various life-cycle methods of Screen interact. The documentation is somewhat cryptic and many of the answers suggest that even experienced developers might be a bit vague about it all. (There's also clearly something wrong in the API documentation: onClose is described as calling onSavePrompt, and the documentation for onSavePrompt claims that the framework will call onClose if it returns true. If this were correct..., well, I'm sure you see the problem.)

 

I finally got tired of coding up PopupScreens that would not close, or that would never save data, etc. After some experimentation, I developed my own mental model for how this all works. The code below captures what I believe is going on in the Screen class and, for the onSavePrompt, in the MainScreen class. I make no claims that this is how these classes actually work; just that they seem to behave as if they were coded this way.

 

I'm posting it here for two purposes:

  1. to ask for your feedback about anything I got wrong or left out;
  2. to help anyone else who is as confused as I was.

 

// In class Screen:

// The start of the close process begins here.
public boolean onClose() {
if ((getStyle() & DEFAULT_CLOSE) == DEFAULT_CLOSE) {
if (!isDirty() || onSavePrompt()) {
close();
return true;
}
}
return false;
}

public boolean isDirty() {
// logic based on documentation only
Manager manager = getDelegate();
return manager != originalManager() || manager.isDirty();
}

protected boolean onSavePrompt() {
return true;
}

// This never gets called by the Screen class,
// but see below for MainScreen
protected boolean onSave() {
if (isDataValid()) {
try {
save();
return true;
} catch (IOException e) {
Dialog.alert("An error occurred while attempting to save data.");
}
}
return false;
}

protected boolean keyCharUnhandled(char key, int status, int time) {
return (key == Characters.ESCAPE /* && status == 0 ??? */)
? onClose()
: false;
}

// ==============================
// In class MainScreen:

protected boolean onSavePrompt() {
switch (Dialog.ask(Dialog.D_SAVE)) {
case Dialog.SAVE:
return onSave();
case Dialog.DISCARD:
return true;
default:
return false;
}
}

 

P.S. I have no idea why the MainScreen logic for onSavePrompt isn't used at the Screen level. Or at least the Screen-level logic should have been to call onSave. But it isn't, so we just live with what is.




Solved? click "Accept as solution". Helpful? give kudos by clicking on the star.
Please use plain text.
Developer
simon_hain
Posts: 15,947
Registered: ‎07-29-2008
My Device: Z10 LE
My Carrier: O2 Germany

Re: Save, Discard, Cancel, and all that

to avoid the save dialog you have to overwrite onSavePrompt to return false:

 

protected boolean onSavePrompt() {
return false;
}

 

if you want to leverage the save prompt you have to overwrite save(). it is called if one of the screens fields reports to be dirty. it can be manually triggered by setDirty(true).

 

if you want to check values you can do that in onClose().

 

 

 

----------------------------------------------------------
feel free to press the like button on the right side to thank the user that helped you.
please mark posts as solved if you found a solution.
@SimonHain on twitter
Please use plain text.
Developer
Ted_Hopp
Posts: 1,305
Registered: ‎01-21-2009
My Device: Not Specified

Re: Save, Discard, Cancel, and all that

[ Edited ]

If onSavePrompt() always returns false, good luck trying to close a dirty screen through the framework. You will have to explicitly pop the screen or call close in your own code, because the escape key or the Close menu item isn't going to get past the test in onClose.

 

The save dialog is only an issue with subclasses of MainScreen; it is never generated for PopupScreen subclasses (unless you do it yourself). I wrote a PopupScreen that had OK and Cancel buttons and where I wanted the ESCAPE. key to act like Cancel. This was to replace the Save/Discard/Cancel dialog (which wasn't going to happen automatically anyway). I did this by adding a boolean field called "saving" that was initialized to false. I added a field change listener to the OK button that set it to true and called onClose; and a field change listener to the Cancel button that just called onClose. Then I overrode onSavePrompt:

 

protected boolean onSavePrompt() {
if (saving) {
saving = false; // in case we don't make it to close()
 return onSave();
}
return true;
}

 

Voilá, no save dialog, data validation only when needed, and the rest of the framework logic still intact.

 

It is indeed possible (and tempting) to short-circuit the entire close logic of the framework by simply overriding onClose, but all that logic is actually useful (from time to time :smileywink:). The best way to take advantage of it and also check values is to override isDataValid. That way, data validation will only take place when the data are indeed going to be saved. When overriding isDataValid for a Screen subclass (as opposed to, say, an EditField), it's important to call super.isDataValid so that the individual fields are validated. My general recipe for Screen-derived classes is:

 

public boolean isDataValid() {
boolean valid = super.isDataValid(); // checks each field individually
if (valid) {
// Do inter-field validation here.
// For example, this is a good place to check
// that the 'password' and 'confirm password'
// fields have equal values.
}
return valid;
}

 

I've found that "working within the system" pays off. When dealing with screen closing issues, I used to always start by thinking about overriding onClose. After developing this model of how the framework operates, I changed my approach and now never do that. My code is logically simpler (although not always shorter), more maintainable, and, most importantly, my screen behavior is more consistently in line with that of other applications and with RIM guidelines.

 




Solved? click "Accept as solution". Helpful? give kudos by clicking on the star.
Please use plain text.
New Developer
vewert
Posts: 6
Registered: ‎12-11-2009
My Device: Not Specified

Re: Save, Discard, Cancel, and all that

Hi.  Like others I have been (and still am) often confused by the life-cycle methods when closing a screen.  To help me make sense of it a created a flow chart that tries to map the flow.  I have put the flowchart onto my Blog: http://ewert-technologies.ca/blog/blog5.php/articles/navigationg-blackberry-screen-saving-flow

 

Hopefully this will be useful.  I also make no claims to the accuracy of it, but hopefully it is close.

Victor Ewert
http://www.ewert-technologies.ca
Please use plain text.
Developer
Simpler
Posts: 49
Registered: ‎01-17-2010
My Device: Curve 9300

Re: Save, Discard, Cancel, and all that

The flowchart that vewert posted is extremently helpful.  

 

Thanks...

- Simulator 6.0.0.668 (9300)
- Windows 7 64bit
- Curve 9300 with App Version: 6.0.0.668 (2949)
Please use plain text.
Developer
Ted_Hopp
Posts: 1,305
Registered: ‎01-21-2009
My Device: Not Specified

Re: Save, Discard, Cancel, and all that

Victor's flowchart is pretty close. Be aware that the logic shown for onSavePrompt() is valid only for MainScreen-derived classes. Other screens (like PopupScreen) by default just return true from onSavePrompt, without putting up an options dialog.

 

Also, as far as I can tell, the DEFAULT_CLOSE style bit affects the onClose() logic (something that isn't shown in the flowchart).




Solved? click "Accept as solution". Helpful? give kudos by clicking on the star.
Please use plain text.