4__02.rtfd -- /usr/axel/vorl/java97/skript/bw

Werbung
171
Copyright 1996-1998 by Axel T. Schreiner. All Rights Reserved.
Eine konfigurierbare Tastatur — calc/Keyboard.java
Keyboard.NUMBERS
Keyboard.CALC0
Keyboard.CALC1
Keyboard dient zum Experimentieren mit einem Keyboard-Objekt. Die Applikation endet durch
Betätigen von Q.
Ein Keyboard-Objekt ist eine frei belegbare Tastatur. Jede Taste kann mit der Maus oder durch
Tippen bedient werden und liefert dann einen ActionEvent. Welche getippten Zeichen eine
Taste auslösen und was als actionCommand verschickt wird, ist bei Konstruktion eines
Keyboard-Objekts konfigurierbar; als Voreinstellung gilt der Inhalt der Tasten.
Einige Layouts sind vordefiniert; die Unicode-Zeichen werden allerdings unter Linux/Motif
nicht dargestellt.
Keyboard demonstriert viele Feinheiten, die man beim Aufbau neuer Grafikobjekte
berücksichtigen muß.
Liest man die Quellen von Component und Container, könnte man durch Ersetzen
undokumentierter Methoden Keyboard möglicherweise ‘‘eleganter’’ implementieren, aber hier
wird eine Strategie verfolgt, die nur mit dokumentierten Methoden auskommt.
172
Beispiele und Testprogramm
Eine Tastatur wird aus einer String-Matrix konstruiert. Einige sind vordefiniert:
{apps/calc/Keyboard.java}
import java.awt.*;
import java.awt.event.*;
/** A class to simulate a rectangular keyboard with buttons and keypresses */
public class Keyboard extends Panel implements FocusListener, KeyListener {
/** pad with numbers */
public static final String[][] NUMBERS [] =
{{{ "1"}, {"2"}, { "3"}},
{{ "4"}, {"5"}, { "6"}},
{{ "7"}, {"8"}, { "9"}},
{{ "Q"}, {"0"}}};
/** pad with numbers and operators */
public static final String[][] CALC0 [] =
{{{ "1"}, {"2"}, { "3"}, {"+"},
{"-"}},
{{ "4"}, {"5"}, { "6"}, {"\327","*x","*"}, {"\367","/:","/"}},
{{ "7"}, {"8"}, { "9"}, {null},
{"=", "=\r\n"}},
{{ "Q"}, {"0"}, { "C"}}};
/** pad with numbers, operators, and parentheses */
public static final String[][] CALC1 [] =
{{{ "1"}, {"2"}, { "3"}, {"("},
{")"}},
{{ "4"}, {"5"}, { "6"}, {"+"},
{"-"}},
{{ "7"}, {"8"}, { "9"}, {"\327","*x","*"}, {"\367","/:","/"}},
{{ "Q"}, {"0"}, {"\261", "~", "_"}, {"%"}, {"=", "=\r\n"}}};
/** mostly trivial test program: keyboard for calculator */
public static void main (String args []) {
Keyboard kb = new Keyboard(NUMBERS);
kb.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent e) {
String s = e.getActionCommand();
System.out.println(s);
if (s.equals("Q")) System.exit(0);
}
});
Frame f = new Frame();
f.add("Center", kb); f.pack(); f.show();
}
{}
erste Zeile der String-Matrix definiert die Breite der Tastatur. Jeder Eintrag der Matrix
Die
muß einen String für die Taste enthalten. Ein zweiter String kann die getippten Zeichen
festlegen, die die Taste auslösen sollen, ein dritter String kann das actionCommand explizit
festlegen.
173
Konstruktion
{apps/calc/Keyboard.java}
/** @param board matrix of keys, row 0 determines width,
each entry is label [, chars [, actionCommand]]
where chars can be pressed instead of clicking label */
public Keyboard (String[][] board []) {
super(new GridLayout(board.length, board[0].length, 3,3));
for (int row = 0; row < board.length; ++ row)
for (int col = 0; col < board[0].length; ++ col)
if (col >= board[row].length || board[row][col][0] == null)
add(new Label(""));
else {
Key b = new Key(board[row][col][0],
board[row][col].length > 1 ? board[row][col][1] : null);
if (board[row][col].length > 2 && board[row][col][2] != null)
b.setActionCommand(board[row][col][2]);
add(b);
addKeyListener(b);
}
lastFocus = getComponent(0);
}
{}
Für jeden Matrixeintrag wird entweder eine Label ohne Beschriftung oder ein Key erzeugt,
dabei limitiert die erste Matrixzeile die Breite des GridLayout der Tastatur.
Falls vorhanden, erhält ein Key einen zweiten String im Matrixeintrag als zweites Argument bei
der Konstruktion — dieser String enthält die getippten Zeichen, die den Key auslösen. Ein
dritter String wird als actionCommand eingetragen.
Das Keyboard ist Container und KeyListener für alle Key-Objekte.
Fokus
Keyboard interessiert sich für alle getippten Tasten. Für externe Component-Objekte sollte man
das Keyboard als FocusListener eintragen, dann wird der Fokus immer von dort gestohlen und
der zuletzt benutzten Key zugewiesen:
{apps/calc/Keyboard.java}
/** tracks last key to have had focus */
protected Component lastFocus;
/** if this is FocusListener, steal focus from them */
public void focusGained (FocusEvent e) {
lastFocus.requestFocus();
}
/** ignored */
public void focusLost (FocusEvent e) { }
{}
174
KeyEvent-Management
In X11 würde man über Beschleuniger als Ressourcen einzelne Tasten der Tastatur mit
Knöpfen auf dem Schirm verbinden; diese würden sich dann bei Tastendruck ‘‘bewegen’’.
Im AWT 1.1 wird jeder Event nur noch einer Component zugestellt, folglich muß man hier jedes
getippte Zeichen von jedem Key aus erkennen können, obgleich nur jeder Key selbst weiß,
welche Zeichen er als äquivalent betrachtet. Eine Lösung besteht darin, das Keyboard als
KeyListener aller Key-Objekte zu verwenden und jedes getippte Zeichen vom Keyboard an alle
Key-Objekte als KeyListener des Keyboard-Objekts zuzustellen. Das Keyboard könnte dann
auch noch gezielt für externe Component-Objekte als KeyListener dienen.
{apps/calc/Keyboard.java}
/** this multicasts keyPressed to each Key */
protected KeyListener keys;
/** manage keys list */
public void addKeyListener (KeyListener l) {
keys = AWTEventMulticaster.add(keys, l);
}
public void removeKeyListener (KeyListener l) {
keys = AWTEventMulticaster.remove(keys, l);
}
/** forward keyTyped to each Key */
public void keyTyped (KeyEvent e) {
if (keys != null) keys.keyTyped(e);
}
/** ignored */
public void keyPressed (KeyEvent e) { }
public void keyReleased (KeyEvent e) { }
{}
konstruiert eine lineare Liste von Objekten, die als jede Art von
*Listener verwendet werden können. Die Konstruktion ist speziell darauf ausgelegt, die
Methoden add*Listener und remove*Listener zu implementieren.
AWTEventMulticaster
Schickt man einen Event an die Liste, wird er an alle Elemente verteilt.
Es scheint nicht möglich zu sein, einen Button per Programm optisch zu betätigen — die
Tastatursimulation ist also wenig ansprechend.
175
ActionEvent-Management
deutet an, wie man einen Verteiler für Events einrichtet. Da jeder Key seinen ActionEvent
an alle ActionListener des Keyboard-Objekts verteilen soll, richtet man einen weiteren
Verteiler speziell für ActionEvent ein:
keys
{apps/calc/Keyboard.java}
/** each Key multicasts actionPerformed to each registered ActionListener */
protected ActionListener actions;
/** manage actions list */
public void addActionListener (ActionListener l) {
actions = AWTEventMulticaster.add(actions, l);
}
public void removeActionListener (ActionListener l) {
actions = AWTEventMulticaster.remove(actions, l);
}
{}
Key
stammt von Button ab, muß aber einen ActionEvent über die actions im Keyboard
verteilen:
Key
{apps/calc/Keyboard.java}
/** A component class to model a key on the keyboard */
public class Key extends Button implements KeyListener {
protected String chars;
// any of these also triggers action
public Key (String label, String chars) {
super(label);
this.chars = chars;
this.addKeyListener(Keyboard.this);
// start analyzing keypress
enableEvents(AWTEvent.ACTION_EVENT_MASK); // register for action
}
/** multicasts action to actions multicaster */
protected void processActionEvent (ActionEvent e) {
if (actions != null) actions.actionPerformed(e); lastFocus = this;
}
{}
Aus Effizienzgründen erhält man einen Event nur, wenn die zugehörige Maske gesetzt ist. Da
addActionListener() vernünftigerweise nicht verwendet wird, würde Key einen ActionEvent
nicht empfangen, deshalb setzt man die Maske mit enableEvents() .
Ein Event kommt bei processEvent() an und wird an process*Event() übergeben. Von dort
werden normalerweise die eigenen *Listener angesprochen.
176
keyTyped
Als KeyListener muß Key passende Zeichen als ActionEvent verteilen:
{apps/calc/Keyboard.java}
/** if keyChar is in chars [or label]: maps keypress to action */
public void keyTyped (KeyEvent e) {
if ((chars != null ? chars : getLabel()).indexOf(e.getKeyChar()) >= 0) {
processActionEvent(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, getActionCommand()));
e.consume(); lastFocus = this; requestFocus();
}
}
/** ignored */
public void keyPressed (KeyEvent e) { }
public void keyReleased (KeyEvent e) { }
}
}
{}
consume() setzt ein Bit, das bei manchen Events verhindert, daß sie an weitere *Listener
verteilt werden.
Fokus-Probleme
Es zeigt sich, daß die Fokussierung eine Vielzahl von Portabilitätsproblemen aufwirft. Die hier
gewählte Strategie beruht auf folgenden Beobachtungen:
Prinzipiell sollte man jeder Component mit requestFocus() die Tastatur zuordnen können.
Praktisch muß die Component aber sichtbar sein, um überhaupt Events empfangen zu können:
Setzt man requestFocus() auf Keyboard, empfängt man bei Windows keine Zeichen.
Der erste Fokus in einem Fenster wird ziemlich zufällig gewählt: Windows setzt offenbar auf
die zuerst eingefügte Component, Linux auf die erste, die normalerweise Zeichen empfängt —
auch wenn das dann etwa ein nicht editierbares TextField ist.
Gibt es für ein nicht editierbares TextField einen KeyListener, kann man fokussieren und
tippen, aber das gibt beep-Klänge unter Linux/Motif. Setzt man in processActionEvent() den
Fokus mit Keyboard.this.requestFocus(), dann flackert das ganze Keyboard bei Knopfdruck.
Hält man den Fokus immer auf einem Key-Objekt, wird es zwar optisch ausgezeichnet, aber
wenigstens funktioniert das System.
Herunterladen