21.05.04 Benutzereingaben auf Low-Level Ebene
Bis jetzt sind Ihre selbst gezeichneten Oberflächen noch recht statisch und reagieren nicht auf Benutzereingaben. Dies wird sich in diesem Kapitel ändern. Sie lernen, wie Sie auf das Drücken einer Taste oder die Bedienung der Applikation mit einem evtl. vorhandenen Touchscreen reagieren können.
Drei Methoden für Tastatureingaben
Ein Canvas
stellt Ihnen drei unterschiedliche Methoden zur Reaktion auf die Betätigung einer Taste auf der Handytastatur zur Verfügung, welche Sie nur noch überschreiben müssen.
Beim Drücken einer Taste wird die Methode keyPressed(int keyCode)
aufgerufen, beim Aufruf von keyReleased(int keyCode)
wurde die Taste hingegen wieder losgelassen. Und während eine Taste gehalten wird, wird in definierten Abständen die Methode keyRepeated(int keyCode)
ausgeführt.
Als Übergabeparameter der Methoden ist jeweils eine Nummer/ein Code definiert, anhand dessen eine Taste eindeutig identifiziert werden kann. Für die Standardtasten (0-9, * und #) auf einer Handytastatur stehen hierfür bereits Konstanten in der Klasse Canvas
zur Verfügung. KEY_NUM0
bis KEY_NUM9
stehen dabei für die Zahlen 0-9, KEY_POUND
für # und KEY_STAR
für *. Sie können sich jedoch auch den Namen der gedrückten Taste (des keyCodes
) ausgeben lassen. Hierzu verwenden Sie die Methode getKeyName(int keyCode)
.
Ein kleines Beispiel, das immer dann den Anzeigetext entsprechend verändert, wenn eine der Tasten 2, 5, oder 8 gedrückt, gehalten oder losgelassen wird (wie in Java SE sorgt auch in Java ME der Aufruf von repaint()
dafür, dass das Canvas
neu gezeichnet wird):
package de.jbb.j2me.ll; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Graphics; public class ActionCanvas extends Canvas { private static final String PRESSED = " gedrückt"; private static final String REPEATED = " gehalten"; private static final String RELEASED = " losgelassen"; private String drawingText = ""; protected void paint(Graphics g) { g.setColor(0, 0, 0); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(255, 255, 255); g.drawString(drawingText, getWidth() / 2, getHeight() / 2, Graphics.HCENTER|Graphics.BOTTOM); } private void setText(String text) { if (!text.equals(drawingText)) { drawingText = text; repaint(); } } protected void keyPressed(int keyCode) { if (keyCode == KEY_NUM2 || keyCode == KEY_NUM5 || keyCode == KEY_NUM8) { setText(getKeyName(keyCode) + PRESSED); } } protected void keyReleased(int keyCode) { if (keyCode == KEY_NUM2 || keyCode == KEY_NUM5 || keyCode == KEY_NUM8) { setText(getKeyName(keyCode) + RELEASED); } } protected void keyRepeated(int keyCode) { if (keyCode == KEY_NUM2 || keyCode == KEY_NUM5 || keyCode == KEY_NUM8) { setText(getKeyName(keyCode) + REPEATED); } } }
Die meisten modernen Handys besitzen jedoch mehr als diese zwölf Standardtasten. Möchten Sie auf nicht standardisierte Tasten reagieren, müssen Sie hier gerätespezifische Versionen Ihres Programms erstellen und jeweils den spezifischen keyCode
abfragen. Dies ist jedoch vor allem für Spiele, die meistens mit einem ggf. vorhandenem Steuerkreuz bedient werden können, sehr umständlich. Hier vereinfachen die so genannten Game Actions Ihr Leben.
Game Action
Über die Methode getGameAction(int keyCode)
kann ein keyCode
in einen gerätespezifischen keyCode
einer abstrakten Taste umgewandelt werden. Eine abstrakte Taste ist hierbei bspw. links oder Abschuss. Auf diese Weise können Sie relativ gerätespezifisch auf typische Eingaben, die für ein Spiel notwendig sind, reagieren. Den Rückgabewert der getGameAction
-Methode können Sie anschließend mit einer der Konstanten LEFT, RIGHT, UP, DOWN
(Richtungsanweisungen), FIRE
(bestätigen/Abschuss/OK), GAME_A, GAME_B, GAME_C
oder GAME_D
(Tasten, die das jeweilige Gerät für Aktionstasten in Spielen vorsieht) der Klasse Canvas
vergleichen.
Genauso können Sie umgekehrt eine gameAction
über die Methode getKeyCode(int gameAction)
wieder zurück in einen keyCode
verwandeln. Anschließend könnten Sie sich dann die Bezeichnung der gameAction
anhand deren keyCode
über die bereits bekannte Methode getKeyName
ausgeben lassen.
Ein kleines Beispiel:
protected void keyPressed(int keyCode) { String ext = " (" + getKeyName(keyCode) + ")"; switch(getGameAction(keyCode)) { case LEFT: drawingText = "LEFT"; break; case RIGHT: drawingText = "RIGHT"; break; case UP: drawingText = "UP"; break; case DOWN: drawingText = "DOWN"; break; case FIRE: drawingText = "FIRE"; break; case GAME_A: drawingText = "GAME_A"; break; case GAME_B: drawingText = "GAME_B"; break; case GAME_C: drawingText = "GAME_C"; break; case GAME_D: drawingText = "GAME_D"; break; default: drawingText = "No gameAction for"; } drawingText += ext; repaint(); } protected void keyReleased(int keyCode) {} protected void keyRepeated(int keyCode) {}
Der springende Punkt
Zur Vertiefung Ihrer bisherigen Kenntnisse wird ein Canvas
implementiert, durch welches man mittels Handytastatur einen kleinen Punkt steuern kann. Zusätzlich zur Navigation über die gameActions
, ist diese auch über die Tasten 4 (links), 6 (rechts), 8 (unten) und 2 (oben) möglich. Bei der Betätigung von FIRE
oder der 5, ändert der Punkt seine Farbe.
package de.jbb.j2me.ll; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Graphics; public class ActionCanvas extends Canvas { private boolean drawRed = true; private int pointSize = 10; private int x = 0; private int y = 0; private int pointStep = 5; protected void paint(Graphics g) { g.setColor(0, 0, 0); g.fillRect(0, 0, getWidth(), getHeight()); if (drawRed) { g.setColor(255, 0, 0); } else { g.setColor(255, 255, 255); } g.fillArc(x, y, pointSize, pointSize, 0, 360); } public void movePoint(int keyCode) { switch (getGameAction(keyCode)) { case LEFT: x -= pointStep; break; case RIGHT: x += pointStep; break; case UP: y -= pointStep; break; case DOWN: y += pointStep; break; case FIRE: drawRed = !drawRed; break; default: switch (keyCode) { case KEY_NUM2: y -= pointStep; break; case KEY_NUM4: x -= pointStep; break; case KEY_NUM6: x += pointStep; break; case KEY_NUM8: y += pointStep; break; case KEY_NUM5: drawRed = !drawRed; } } if (x < 0) { x = 0; } else if (x + pointSize > getWidth()) { x = getWidth() - pointSize; } else if (y < 0) { y = 0; } else if (y + pointSize > getHeight()) { y = getHeight() - pointSize; } repaint(); } protected void keyPressed(int keyCode) { movePoint(keyCode); } protected void keyRepeated(int keyCode) { movePoint(keyCode); } }
Touchscreen Funktionalitäten
Wenn das Endgerät einen Touchscreen besitzt, können Sie auch auf Eingaben über den Bildschirm reagieren. Ähnlich wie bei Tastatureingaben gibt es auch hierfür Methoden, wenn auf den Bildschirm gedrückt (pointerPressed(int x, int y)
), etwas verschoben (pointerDragged(int x, int y)
) oder der Bildschirm nicht mehr berührt (pointerReleased(int x, int y)
) wird, die in Ihrem Canvas
überschrieben werden müssen. Die übergebenen Koordinaten spezifizieren, an welcher Stelle sich das Eingabegerät/der Finger momentan befindet.
So lässt sich der springende Punkt problemlos um Touch-Funktionalitäten erweitern:
private void setBallTo(int x, int y) { if (x < 0) { x = 0; } if (x + pointSize > getWidth()) { x = getWidth() - pointSize; } if (y < 0) { y = 0; } if (y + pointSize > getHeight()) { y = getHeight() - pointSize; } this.x = x; this.y = y; repaint(); } protected void pointerDragged(int x, int y) { setBallTo(x - pointSize / 2, y - pointSize / 2); } protected void pointerPressed(int x, int y) { setBallTo(x - pointSize / 2, y - pointSize / 2); }
Flüssigere Bewegungen
Vermutlich ist Ihnen beim Ausprobieren aufgefallen, dass sich der Punkt bei der Steuerung über die Tastatur mit gehaltener Taste nur sehr ruckelig und langsam bewegt. Dies liegt daran, dass Sie nicht spezifizieren können, in welchen Abständen die Methode keyRepeated
aufgerufen wird. Es ist ein Workaround nötig.
Anstatt wiederholende Eingaben über die keyRepeated
-Methode zu realisieren, können Sie einem separaten Thread, der in einer Endlosschleife parallel zum restlichen Programm läuft, den zuletzt gedrückten keyCode
aus der keyPressed
Methode setzen. In der Endlosschleife wird dann die movePoint
Methode unseres Canvas
in regelmäßigen Abständen aufgerufen, bis wieder im Canvas
die Methode keyReleased
ausgelöst, und somit die Wiederholung beendet wurde.
Zuerst benötigen Sie ein Interface Movable
, das die Methode movePoint(int keyCode)
vorschreibt.
package de.jbb.j2me.ll; public interface Movable { void movePoint(int keyCode); }
Anschließend können Sie den RepeatHelper
mit oben genannter Funktionalität implementieren (für diese Zwecke ist ein sehr einfacher Helper ausreichend):
package de.jbb.j2me.ll; public class RepeatHelper implements Runnable { private int keyCode = -1; private int waitingTime; private boolean repeat; private Movable movable; public RepeatHelper(Movable movable, int waitingTime) { this.movable = movable; this.waitingTime = waitingTime; } public void run() { while (true) { if (repeat) { movable.movePoint(keyCode); } try { Thread.sleep(waitingTime); } catch (InterruptedException ie) { ie.printStackTrace(); } } } public void setKeyCode(int keyCode) { this.keyCode = keyCode; repeat = true; } public void stopRepeating() { repeat = false; } }
Die Implementierung des Canvas
muss nun noch an die Verwendung des RepeatHelpers
angepasst werden. Hierzu muss das Canvas
zuerst Movable
implementieren (die dazugehörige Methode movePoint(int keyCode)
wurde bereits ausprogrammiert).
public class ActionCanvas extends Canvas implements Movable
Anschließend deklarieren Sie eine Objektvariable des RepeatHelpers
, initialisieren diese im Konstruktor, verpacken das Runnable-Objekt in einem neuen Thread
, und starten diesen.
private RepeatHelper repeater; public ActionCanvas() { repeater = new RepeatHelper(this, 40); new Thread(repeater).start(); }
Die keyPressed
-Methode wird dahingehend angepasst, dass nach dem Aufruf der movePoint
-Methode der keyCode
dem RepeatHelper
gesetzt wird – sofern der keyCode
einer Navigationstaste entspricht.
protected void keyPressed(int keyCode) { movePoint(keyCode); if (getGameAction(keyCode) != FIRE && keyCode != KEY_NUM5) { repeater.setKeyCode(keyCode); } }
keyRepeated
fällt nun selbstverständlich weg. Dafür muss in der keyReleased
Methode die Wiederholung des RepeatHelpers
beendet/unterbrochen werden.
protected void keyReleased(int keyCode) { repeater.stopRepeating(); }
Auf der nächsten Seite finden Sie eine Zusammenfassung des in diesem Kapitel erarbeiteden Codes.
Hallo, ich bin Anfänger und programmiere Java über Eclipse. Wie kann ich bei „Graphics“ eine Eingabe einer ganzen Zahl programmieren?
Danke für eine Hilfe.
Mit vielen Grüßen
P- Prüfer
Hallo Petra,
erst einmal die Frage (da Sie Anfängerin sind), ob Sie wissen, dass Sie sich im Java ME – also Java für mobile Endgeräte wie Handys – Kapitel befinden, und nicht im Java SE (für ganz normale Computer)?
Grüße
Stefan