Thursday, December 9, 2010

Touch Screen support

Cros-posted at Technè - Blog de Tecnologia do C.E.S.A.R.

More and more cell phones have touch screens. And when we hear about them the first thing that we think are the most expensive smartphones. Big screens and amazing computer power.

But there are also feature phones with such screens. And amongst them there is a lot supporting Java ME. So, how do you enable an application to get the user's "point"?

Since MIDP 1.0 Canvas already had the following callback methods: pointerPressed, pointerDragged and pointerReleased. And for information there were also hasPointerEvents and hasPointerMotionEvents.

TIP: SDK 3.0 emulator already have touch support, but if you are using WTK 2.5 go to wtklib\devices\DefaultColorPhone\DefaultColorPhone.properties and change the value of touch_screen to true.

To better understand these pointer methods here is a simple example. A Canvas where we can draw freely. It bevahes like Microsoft Paint pencil.

public class TouchCanvas extends Canvas {
// starting point of the line drawn at paint method
int x1 = -1, y1 = -1;
// line final point
int x2, y2;
// flag used to know when to clear the screen
boolean clear = true;

protected void pointerPressed(int x, int y) {
x1 = x;
y1 = y;
}
protected void pointerReleased(int x, int y) {
x1 = y1 = -1;
}
protected void pointerDragged(int x, int y) {
x2 = x;
y2 = y;
repaint();
}

protected void paint(Graphics g) {
if (clear) {
g.setColor(0xffffff); // white
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0); // black
clear = false;
}

if (x1 >= 0 && y1 >= 0) {
g.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
}
}

The MIDlet below shows the Canvas and give the user a option to clear the screen.

public class TouchMIDlet extends MIDlet implements CommandListener {

TouchCanvas touchCanvas = new TouchCanvas();

public TouchMIDlet() {
touchCanvas.addCommand(new Command("Clear", Command.OK, 1));
touchCanvas.addCommand(new Command("Exit", Command.EXIT, 1));
touchCanvas.setCommandListener(this);
}

protected void destroyApp(boolean unconditional) { }

protected void pauseApp() { }

protected void startApp() {
Display.getDisplay(this).setCurrent(touchCanvas);
}

public void commandAction(Command c, Displayable d) {
if (c.getCommandType() == Command.EXIT) {
notifyDestroyed();
} else {
touchCanvas.clear = true;
touchCanvas.repaint();
}
}
}

For a more complex sample lets change two classes from Bar Chart to add touch dragging. First we add the following method to BarChart class:
public int getBarWidth (Font font) {
return font.stringWidth(widestName);
}

Now we add the following code to BarChartCanvas:
private int lastX, lastY;
protected void pointerPressed(int x, int y) {
lastX = x;
lastY = y;
}
protected void pointerDragged(int x, int y) {
int w = barChart.getBarWidth(Font.getDefaultFont());
// dragged a distance bigger than the bars width
if (lastX - x >= w || lastY - y >= w) {
barChart.nextBar();
repaint();
lastX = x;
} if (x - lastX >= w || y - lastY >= w) {
barChart.previousBar();
repaint();
lastX = x;
}
}

The smaller the bars be smoother the movement will be. This code also works on landscape mode (SDK 3.0 Emulator: View .. Orientation .. 270).

Another common action is the long press. As we do not have a callback method for it we need to count the time the user is pressing and not dragging or releasing. Below is a way to change our TouchCanvas:

private Timer timer;
protected void showNotify() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
// long press x and y
int lpx = -1, lpy = -1;
public void run() {
if (x1 >= 0) {
if (lpx == x1 && lpy == y1) {
lpx = lpy = -1;
System.out.println("long press");
} else {
lpx = x1;
lpy = y1;
}
}
}
},
// starting now and checking at every second
0, 1000);
}
protected void hideNotify() {
timer.cancel();
timer = null;
}

We hope this helps. See you next time.

Related topics:

No comments: