Autor: 'Kriz'
Wichtiger Hinweis!!!
Die Grundlagen über die Java-Programmierung ist nicht Bestandteil der Tutorialreihe! Zum Lernen von Java empfehle ich das hervorragende Online-Tutorial von Guido Krüger, welches man sich kostenlos von www.javabuch.de runterladen kann.
In diesem Tutorial geht es um eine Statusleiste (statusbar), die am unteren Ende eines Java Fensters erscheint und die den Benutzer über diverse Dinge informieren soll. Diese diversen Dinge sind z.B. Hovertexte, die beim Überfahren von Menüeinträgen oder Buttons mit dem Mauspfeil erscheinen. Diese Technik kennt man von vielen Anwendungen, daher dürfte das Thema für die meisten Anwender nichts Neues sein.
Leider - um es vorwegzunehmen - unterscheiden sich die Mittel des AWTs und Swings in einigen Stellen sehr voneinander, so daß Hovertexte von Menüeinträgen mit dem AWT nicht möglich sind. Einem Objekt der AWT-Klasse MenuItem oder CheckboxMenuItem fehlt die Implementierung des Interfaces MouseListener. Wie wir noch sehen werden, ist dieses Interface der Schlüssel für die Hovertexte. Zum Glück sind z.B. die Buttons nicht davon betroffen.
Wir werden in diesem Tutorial eine AWT-Statusbar entwickeln. Die Statusbar wird vorerst nichts anderes anzeigen als Text. Spezielle Features bekannt aus anderen Statusbar Implementierungen wie Icons usw. werden erstmal nicht behandelt. In einem weiteren Tutorial werde ich dann die Swing-Variante vorstellen.
Für die Realisierung einer Statusbar, die nur Text anzeigen soll, eignet sich auf den ersten Blick ein Objekt der Klasse Label. Ein Label zeigt nur Text an, wobei man diesen wahlweise linksbündig, zentriert oder rechtsbündig ausrichten lassen kann. Ein Label allein ist allerdings nicht der ganze Zauber für eine Statusbar, denn dummerweise erstreckt sich der Anzeigebereich des Labels standardmäßig nur auf die maximale Breite des Strings, den man dem Label anfangs mitgibt. Man muß also das Label zwingen, sich irgendwie über den gesamten Anzeigebereich des Fensters auszustrecken.
Glücklicherweise gibt es ja noch die Klasse Panel, ein eigener Bereich innerhalb eines Java-Fensters. Einem Panel kann man vorab ein definiertes Layout übergeben, daß die Anzeigeformatierung des Panel-Inhalts übernimmt. Wir nehmen dazu ein Layout, welches sich über die ganze Fensterbreite erstreckt: Das GridLayout. Wir rufen es mit den Argumenten 1 und 1 auf, welches ein Feld definiert, welches maximal 1 Komponente breit und 1 Komponente hoch ist. In dieses formatierte Panel betten wir unser Label mittels der Methode add()ein. Um die ganze Sache auch zukünftig wiederverwendbar zu machen, leiten wir einfach von der Klasse Panel unsere eigene Klasse Statusbar ab:
import java.awt.*; public class Statusbar extends Panel { protected Label label = null; public Statusbar() { super(new GridLayout(1,1)); label = new Label("Bereit.", Label.LEFT); add(label); } public String getText() { return label.getText(); } public void setText(String text) { if(text == null) label.setText("Bereit."); else label.setText(text); } }
So, diese neue Klasse kann man nun als Fensterelement in jedes AWT-Fenster einbauen. Vorzugsweise sollte eine Statusbar immer an unteren Rand eines Fensters erscheinen, daher wäre es von Vorteil, wenn das Fenster als Layout das BorderLayout besitzt. Die Statusbar kann man dann im Konstruktor der Fensterklasse ganz einfach so hinzufügen:
public MyFrame() { super("My Frame"); setLayout(new BorderLayout()); Statusbar statusbar = new Statusbar(); add("South", statusbar); // ... mehr Code }
Das war der erste Teil der Aktion. Nun wollen wir ja noch, daß beim Überfahren eines Buttons mit der Maus sich der Text in der Statusbar dynamisch verändert bzw. daß solange kein Button überfahren wird, in der Statusbar der Standardtext „Bereit.“ erscheint.
Wie man ja wissen sollte, werden Ereignisse in Java mittels sogenannter Events verarbeitet. Jedes AWT-Objekt, daß aus der Basisklasse Component abgeleitet worden ist, hat die Möglichkeit sich selbst bei einem sogenannten event listener anzumelden. Es gibt in Java für verschiedene Ereignisarten verschiedene event listener.
Einer davon ist der MouseListener, der als Interface existiert. Implementiert eine Components-Klasse dieses Interface, so muß es diverse Methoden implementieren, meistens Ereignismethoden, die die Behandlung eines Events bearbeiten. Wer mit der Technik von Events noch nicht vertraut ist, der möge sich vorher bitte die entsprechenden Kapitel aus dem Java-Tutorial (Link siehe oben) von Guido Krüger anschauen!
Nun gut, der MouseListener stellt fünf Methoden bereit, die implementiert werden müssen:
Die Methoden 3 bis 5 interessieren uns trotz ihrer Zwangsimplementierung nicht, übrig bleiben mouseEntered() und mouseExited(). Die erste Methode mouseEntered() benachrichtigt den Listener (in diesem Falle unser Hauptfenster), daß mit der Maus in den Bereich von Komponente X eingedrungen worden ist, d.h. der Mauspfeil ruht auf der Komponente X. Die zweite Methode mouseExited() benachrichtigt den Listener, daß mit der Maus der Bereich von Komponente X verlassen worden ist.
Und genau hier setzt unser Hovertext-Prinzip an: Ein AWT-Button besitzt die Möglichkeit sich bei einem MouseListener anzumelden, d.h. nach der Anmeldung wird er auf die MouseEvents überwacht. Wie bereits am Anfang erwähnt besitzen die MenuItems und CheckboxMenuItems des AWTs diese Möglichkeit nicht (Swing ja!).
Wir melden unser Hauptfenster als Listener für MouseEvents an und können ab dann überwachen, ob mit der Maus beispielsweise der Button X berührt wird. Sofort wird eine MouseEvent Nachricht an unser Hauptfenster geschickt und wir müssen nur noch innerhalb der beiden Methoden mouseEntered() und mouseExited() prüfen, wer das Event ausgelöst hat und dementsprechend den Text unserer Statusbar aktualisieren.
Es folgt nun eine typische Frame-Klasse für unser Fenster, in der sowohl die Statusbar als auch ein Testbutton vorhanden sind. Der Einfachheit halber gehe ich hier davon aus, daß die Klasse Statusbar im selben Verzeichnis liegt wie die folgende Klasse MyFrame:
import java.awt.*; import java.awt.event.*; public class MyFrame extends Frame implements MouseListener { protected Statusbar statusbar = null; protected Button button = null; public static void main(String[] args) { MyFrame theApp = new MyFrame(); } public MyFrame() { super("Testprogramm für Hovertext"); setLayout(new BorderLayout()); setSize(400, 400); setLocation(100, 100); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent event) { dispose(); System.exit(0); } }); button = new Button("Testbutton"); button.addMouseListener(this); add("North", button); statusbar = new Statusbar(); add("South", statusbar); setVisible(true); } public void mouseEntered(MouseEvent event) { Object obj = event.getSource(); if(obj instanceof Button) { Button b = (Button)obj; if(b == button) statusbar.setText("Die Maus berührt den Button!"); } else statusbar.setText(null); } public void mouseExited(MouseEvent event) { statusbar.setText(null); } public void mousePressed(MouseEvent event) { // Bleibt leer } public void mouseReleased(MouseEvent event) { // Bleibt leer } public void mouseClicked(MouseEvent event) { // Bleibt leer } }
Dieses Programm kann man nun ausführen und staunen, wie sich beim Überfahren des Buttons mit der Maus der Text der Statusbar verändert. Beim Verlassen erscheint wieder „Bereit.“. Der merkwürdige Teil mit addWindowListener() und dem WindowAdapter() im Konstruktor sorgt nebenbei dafür, daß das Fenster beendet werden kann, ohne „hart“ beendet werden zu müssen. Details hierzu findet man wieder im Java-Tutorial von Guido Krüger.
Was passiert nun? Unser Fenster ist als MouseEvent Listener durch Implementierung des Interfaces MouseListener aus dem Paket java.awt.event nun berechtigt, alle Nachrichten des Typs MouseEvent abzufangen und zu behandeln, sofern sich alle Komponenten durch den Methodenaufruf addMouseListener(this) direkt beim Fenster angemeldet haben. Vergißt man diesen Aufruf, so passiert beim Überfahren/Verlassen dieser Komponente nichts!
Wenn nun die Maus auf den Buttonbereich trifft, so schickt dieser Button eine Nachricht vom Typ MouseEvent an das Fenster. Das Fenster registriert dies und ruft nun die entsprechende Event-Behandlungsmethode auf, in diesem Fall mouseEntered(). Dort wird als erstes geprüft, welcher Typ von Komponente das Event gesendet hat. Das geschieht dadurch, indem man einer Instanz vom Typ Object den Rückgabewert der Methode event.getSource() zuweist. Dann prüft man, um welches Komponententyp es sich handelt. So kann man vorab unterscheiden, ob man nur bestimmte Komponententypen behandeln möchte. Mittels des instanceof Operators von Java kann man nun feststellen, ob die sendende Komponenten ein Button ist. Wenn ja, wird nun ein temporärer Button erzeugt und mittels Typumwandlung aus dem Object ein Button gemacht. Diesen temporären Button vergleichen wir nacheinander mit allen in Frage kommenden Buttons, in unserem Fall mit dem Button „button“. Bei Objektvergleichen werden in Java die Typen verglichen, nicht die Werte!
So, da es tatsächlich der Button „button“ gewesen war, können wir nun auf die setText() Methode der Klasse Statusbar zurückgreifen und einen neuen Text setzen. Dieser erscheint dann in der Statusbar.
Beim Verlassen des Button-Bereichs wird das ganze analog mit der Methode mouseExited() wiederholt. Hierbei brauchen wir aber keine Objekt-Analyse mehr durchführen, sondern rufen sofort die setText() Methode von Statusbar auf mit dem Wert null. Dadurch wird automatisch der Text „Bereit.“ wieder in die Statusbar geschrieben. Dieses Verhalten geschieht immer dann, wenn man mit der Maus in „leeren“ Bereichen rumfährt, wo sich keine angemeldeten Komponenten befinden.
Man kann sich die Arbeit mit den Texten in der Methode mouseEntered() dadurch sparen, indem man direkt eine Klasse aus der betreffenden Komponentenklasse selber ableitet (also beispielsweise MyButton aus Button) und in der neuen Klasse eine Variable vom Typ String anlegt und eine öffentliche Methode wie getHovertext() mit String als Rückgabewert. Der Konstruktor sollte dann zusätzlich um ein Stringargument erweitert werden, welches den Hovertext beinhaltet:
import java.awt.*; public class MyButton extends Button { protected String hovertext; public MyButton(String buttontext, String hovertext) { super(buttontext); this.hovertext = hovertext; } public String getHovertext() { return hovertext; } }
Anstatt eines Buttons nimmt man nun ein MyButton und instanziert diesen mit dem passenden Hovertext. Nun muß man nur die Fensterklasse wie folgt abändern:
import java.awt.*; import java.awt.event.*; public class MyFrame extends Frame implements MouseListener { protected Statusbar statusbar = null; protected MyButton button = null; public static void main(String[] args) { MyFrame theApp = new MyFrame(); } public MyFrame() { super("Testprogramm für Hovertext"); setLayout(new BorderLayout()); setSize(400, 400); setLocation(100, 100); button = new MyButton("Testbutton", "Die Maus berührt den Button!"); button.addMouseListener(this); add("North", button); statusbar = new Statusbar(); add("South", statusbar); setVisible(true); } public void mouseEntered(MouseEvent event) { Object obj = event.getSource(); if(obj instanceof MyButton) { MyButton b = (MyButton)obj; statusbar.setText(b.getHovertext()); } else statusbar.setText(null); } public void mouseExited(MouseEvent event) { statusbar.setText(null); } public void mousePressed(MouseEvent event) { // Bleibt leer } public void mouseReleased(MouseEvent event) { // Bleibt leer } public void mouseClicked(MouseEvent event) { // Bleibt leer } }
Man braucht nun nicht mehr auf einen bestimmten Button hin zu prüfen, sondern ruft einfach sofort die getHovertext() Methode auf. Sie liefert automatisch immer den richtigen Hovertext.
Diese Hovertext-Technik läßt sich bis auf die Menüeinträge des AWTs auf jede AWT-Komponente abbilden: Button, Panel, Label, List, Checkbox usw. Ja selbst das Hauptfenster selber könnte sich durch den Aufruf addMouseListener(this) im Konstruktor als zu überwachende Komponente anmelden. Ob das Sinn macht, hängt dabei von der zugrundeliegenden Idee ab…
Der zweite Tipp gehört zum Thema „Dekoration“, was ich anfangs erstmals weglassen wollte. Leider kann man in der AWT nicht so einfach Bilder, Icons usw. als Komponente in das Codegerüst einfügen, daß diese Dinge nicht unbedingt portabel sind. Es ist aber möglich, aus der Klasse Canvas eine abgeleitete Klasse MyIcon zu erzeugen, die durch Überladen der Methode paint() beispielsweise ein 16*16 Pixel großes Bild anzeigt und sonst nichts weiter tut. Effekte wie aus der Statusbar von WinZip mit den grünen Statusbällen sind dann kein Problem mehr, vor allem wenn man je nach Status mal den grünen Ball anzeigt oder einen roten Ball.
Ein Objekt von MyIcon kann dann als eigenständige Komponente in die Statusbar eingefügt werden. Doch wie? Wir haben ja hier eine reine textbasierte Statusbar, die nichts mit Icons usw. zu tun hat. Nun ja, wenn man nun anstatt des Statusbar-Layouts GridLayout das Layout GridBagLayout nimmt, dann kann man rechts außen ein Layoutfeld für MyIcon reservieren und den restlichen Platz links davon für den Text reservieren. Dann hat man eine Statusleiste, die links Text anzeigt und rechts je nach Programmstatus ein Icon usw.
Wenn man das Ganze nun weiterspinnt, eröffnen sich da einem ungeahnte Möglichkeiten! So könnte man beispielsweise bei bestimmten Aktionen die Label-Komponente der Statusbar ausblenden (oder temporär ganz entfernen) und stattdessen eine aus Canvas entwickelte Progressbar (also einen Fortschrittsbalken von 0% bis 100%) erscheinen lassen. Ist die Aktion erfüllt, wird die Progressbar wieder entfernt und das Label taucht wieder auf. Man sieht, Java bietet da viele Möglichkeiten. Auch wenn das AWT viele bekannte Dinge nicht von Haus aus anbietet (z.B. die Progressbar), so findet man diese Dinge letztendlich doch wieder im Repertoire von Swing…
Dieses Tutorial stammt aus der ehemaligen Sammlung des resourcecode.de und konnte dank der freundlichen Zustimmung des Autors in das thewall-Wiki übertragen werden.