Thursday, April 28, 2011

keyReleased not called

Do not take for granted that a call to keyPress will have a keyReleased counterpart.
Yes, Java ME VMs may not call it and the simple tip here is to not rely on keyReleased at all.

One exception, however, is when you have a very closed set of target devices and can do manual tests on all of them.
Do I need to say to NOT trust an emulator execution?

Even in this situation, when would you use keyReleased instead of keyPressed?
Lets say you have a very nice custom UI with buttons, where they can be in one of the following states:


  • Unselected: focus is not at this UI element

  • Selected: got focus

  • Pressed


My personal experience is that the pressed state is hard to perceive because users do not care about it.
They want to get things done. The faster the better.

So, instead of wasting jar size with code (or worst, images!) to draw a pressed state, run everything you need from keyPressed alone.

Of course, there are other usages of keyReleased, for example, one-button-games (OBGs), where the time between the key press and the key release might be very important to the game mechanics.
But even in these cases we can use only key press.

Most OBGs only have two states:


  • Doing: jump, fire, turn, whatever

  • Not doing


And the time doing is measured with the time between a key press and a key release.

To use only key presses at this situation you have to start the doing/action with a press and keep doing/acting until another press happens.
The change to the game mechanics is subtle and you can reach more devices with less code changes.

I had to review some of my components that relied on keyReleased to stop a key repetition, so can you.

Related topics:

Wednesday, April 27, 2011

hasPointerEvents wrong return

Touch enabled handsets are more and more common these days. Some does not even have a keyboard!

Custom User Interfaces should adapt to touch screens and, at least, display with bigger selectable items.

One easy way to know if your MIDlet is running on a touch enabled handset is to call Canvas.hasPointerEvents.

Unfortunately some Java ME Virtual Machines do not return true even if the handset DOES have a touch screen.

One workaround is to have a boolean attribute and set it to true if pointer pressed or released events are triggered:


class C extends Canvas {
boolean pointerEvents;
protected void pointerPressed(int x, int y) {
pointerEvents = true;
// treat the pointer pressed event
// ...
}
protected void pointerReleased(int x, int y) {
pointerEvents = true;
// treat the pointer released event
// ...
}
public boolean hasPointerEvents() {
return pointerEvents || super.hasPointerEvents();
}
}

This way the rest of your application UI might correctly adapt and present bigger items.

Related topics:

Sunday, April 17, 2011

sizeChanged not called

To achieve the Single Jar theory we must deal with handsets fragmentation upfront.
One special piece of these fragments is the orientation change.

Screen orientations are: portrait (height > width) and landscape (width > height).

When the change happens the Java Virtual Machine should notify the running MIDlet with a call to sizeChanged method on the currently displayed Canvas.
Unfortunately this is not guaranteed to happen.

To make sure your application is always presented the right way you can store the last sizeChanged parameter values on attributes and check them on paint method.
Check the code below:


class C extends Canvas {
int lastWidth, lastHeight;
protected void sizeChanged(int w, int h) {
lastWidth = w;
lastHeight = h;
// adjust your user interface to the
// new width and height
// ...
}
protected void paint(Graphics g) {
if (super.getWidth() != lastWidth
|| super.getHeight() != lastHeight) {
sizeChanged(super.getWidth(), super.getHeight());
}
// paint your user interface
// ...
}
}

Related topics: