Suche
Die verschiedenen Möglichkeiten, Textelemente mit unterschiedlichen Hervorhebungen darzustellen

Ausgezeichnete Texte

Manfred Borzechowski

Die JTextArea ist wohl allen Swing-ProgrammiererInnen bekannt, die schon einmal mehr als eine Zeile Text darstellen wollten. Sie kann aber alle Zeichen nur in identischer Weise darstellen; längere Texte bekommen daher schnell den Charakter einer Bleiwüste. Für eine ergonomischere Darstellung von Textelementen, eventuell mit Fettdruck, Hervorhebungen durch Farben oder anderen Schriftgrößen, bietet das Swing-API mehrere Möglichkeiten, deren Einsatz dieser Artikel zeigt.

In Ihrem Leben als Java-EntwicklerIn ist es wohl eher unwahrscheinlich, dass Sie eine komplette Textverarbeitung in Java programmieren werden. Dennoch können Sie mit dem punktuellen Einsatz von Formatierungen und Textauszeichnungen Ihre Anwendungen optisch aufwerten und ergonomischer gestalten.
Beispiel Hilfetexte: Selbst wenn Sie nicht die JavaHelp-Technologie einsetzen, können Sie erläuternde Dokumentation zu Ihren Anwendungen in HTML verfassen und durch Swing-Komponenten anzeigen lassen.
Beispiel Annotationen: Ein Benutzer mag zu einzelnen Datensätzen eines Geschäftsvorgangs eigene Bemerkungen hinzufügen. Hierbei könnte es ihm erlaubt sein, einzelne Wörter fett oder kursiv hervorzuheben.
Beispiel Konsole: Eine Anwendung, die eine länger dauernde Aktion in einem separatem Thread oder System-Prozess startet, mag die verschiedenen Ausgaben dieser Aktion in einer Konsole anzeigen. Warnungen oder Fehler (oder Ausgaben nach System.err anstatt System.out) könnten sogleich durch eine rote Schrift automatisch kenntlich gemacht werden.
Beispiel Syntax Highlighting: Ein Editor für eine proprietäre Skript- oder Makrosprache könnte Schlüsselwörter und Kommentare farblich hervorheben.
Für diese verschiedenen Anwendungsfälle bietet Ihnen das Swing-API jeweils unterschiedlich geeignete Mittel und Wege:

  • Mit der JEditorPane können Sie Texte im HTML- oder RTF-Format anzeigen lassen.
  • Mit AttributeSets können Sie programmatisch Textattribute ändern und neue Abschnitte mit speziell definierten Formatierungen einfügen.
  • Mit Styles können Sie benannte AttributeSets komfortabler verwenden.
  • Mit EditorKits bekommen Sie Actions zur Textmanipulation, die Sie direkt an Oberflächenkomponenten wie JMenuItems oder JButtons anbinden können.

Im folgenden werden wir diese vier Mechanismen betrachten und in Beispielen angewandt finden.

JEditorPane

JEditorPane bietet sich an, wenn Sie komplette Texte im HTML- oder RTF-Format darstellen wollen. Diese können schon statisch vorliegen, wie etwa Hilfetexte, Sie können sie aber auch dynamisch zur Laufzeit des Programms zusammenbauen, wie etwa Berichte über einen Datenbestand (Abb. 1).

Abb. 1: Anzeige von HTML mit der JEditorPane

Die Verwendung der JEditorPane ist schnell programmiert. Zum Darstellen von dynamisch zusammengebautem Text erzeugen Sie eine Instanz, sagen ihr den Mime-Type des Inhalts (text/html oder text/rtf) und übergeben ihr dann den Text als String. Wie üblich bei datenanzeigenden Swing-Komponenten mit eventuell umfangreichem Inhalt bauen Sie die JEditorPane in eine JScrollPane ein. (Wussten Sie, dass ab dem JDK1.4 zum Scrollen die Mouse-Wheels unterstützt werden?).

Listing 1

import java.awt.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

class HtmlStringAnzeiger extends JPanel {

public static void main(String args[]) throws Exception {
JFrame f = new JFrame("HtmlStringAnzeiger");

// Hier wird der anzuzeigende Text dynamisch zur
// Laufzeit zusammengestellt.
DateFormat dateFormat =
DateFormat.getDateInstance(DateFormat.LONG);
int wert = 3;
String text =
"

Inventar

Am " +dateFormat.format(new Date()) +" besitzen Sie


  • +wert
    +” Exemplare der besonders seltenen marelukkischen

“;

f.getContentPane().add(new HtmlStringAnzeiger(text), BorderLayout.CENTER);
f.setBounds(10,10,225,180);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

// Hier wird einer JEditorPane der Mime-Type und der Text
// zugewiesen und diese dann in eine JScrollPane gepackt
HtmlStringAnzeiger(String text) throws Exception {
setLayout(new BorderLayout());

JEditorPane pane = new JEditorPane();
pane.setContentType(“text/html”);
pane.setText(text);
pane.setEditable(false);

JScrollPane scrollPane = new JScrollPane(pane);
add(scrollPane, BorderLayout.CENTER);
}

Das nachträgliche programmatische Ändern von angezeigtem Text, etwa mit einer append()-Methode, ist in der JEditorPane nicht vorgesehen. Man kommt also eventuell auf die Idee, für Änderungen den kompletten Text zu ersetzen – was für viele kleine Änderungen unvernünftig ist. Sie können sich allerdings von der JEditorPane auch das darunter liegende Dokument geben lassen, dieses ist das Model der JEditorPane und enthält die anzuzeigenden Daten (eine ähnliche Arbeitsteilung wie bei einer JList und ihrem ListModel). Falls der Inhaltstyp eben text/html ist, handelt es sich bei diesem Dokument um ein HTMLDocument aus dem Package javax.swing.text.html. Es enthält Methoden wie:

public void insertBeforeStart(
Element elem, String htmlText) oder public void insertAfterEnd( Element elem, String htmlText)

mit denen Sie ganze HTML-Elemente einfügen können. Zusätzlich zum Text müssen Sie allerdings das Elternelement angeben, letztlich müssten Sie also die komplette Element-Struktur des HTMLDocuments kennen und benutzen. Der Artikel TicTacToe Revisited [1] der Java Swing Connection beschreibt eine mögliche Vorgehensweise. Für unsere einfachen Beispielanwendungen wäre diese Vorgehensweise unangemessener Aufwand, wir werden ja noch andere Möglichkeiten kennen lernen, mit Dokumenten sinnvoll zu arbeiten.

Für den anderen Fall der Darstellung von bereits vorhandenen HTML-Dateien übergeben Sie der Instanz der JEditorPane eine URL, die auf eine solche HTML-Datei zeigt. Da Hilfetexte meist mit Querverweisen ausgestattet sind (oder zumindest sein sollten) müssen Sie die JEditorPane noch dazu bringen, beim Klicken auf einen Hyperlink diesen Querverweisen auch zu folgen. Dazu brauchen Sie einen HyperlinkListener, der auf HyperlinkEvents der JEditorPane reagiert, aus dem Event die URL des Hyperlinks extrahiert und die JEditorPane dann dazu auffordert, die in der URL referenzierte HTML-Datei anzuzeigen.

Nebenbei gesagt, können Sie auch andere Komponenten dazu bringen, HTML anzuzeigen (etwa JLabel, JButton, JToolTip und einige andere). Dazu müssen Sie den anzuzeigenden Text lediglich in <HTML> und </HTML> einfassen und schon sind mehrzeilige JToolTips und JButtons mit großen Überschriften möglich (Abb. 2).

Abb. 2: Komponenten mit HTML-Text

Nun schauen wir uns an, wie Sie eine Textanzeige realisieren können, in der Sie auch zur Laufzeit Ihres Programms noch weiteren formatierten Text anfügen können, etwa für eine Konsole zur Anzeige von Ergebnissen eines separat ablaufenden Prozesses, oder für ein Dialogsystem. Sie beginnen mit einer Instanz von JTextPane (das ist eine von JEditorPane abgeleitete Klasse). Diese JTextPane können Sie wie üblich in eine JScrollPane einbauen und dann irgendwo anzeigen. Zum Textanfügen arbeiten Sie allerdings mit dem Model, das ist ein StyledDocument, welches Sie sich von der JTextPane geben lassen. Die uns in erster Linie interessierende Methode lautet:

public void insertString(
int offset,
String str,
AttributeSet a)
throws BadLocationException

Sie erlaubt Ihnen das Anfügen eines Strings an das Ende des Dokuments, wenn Sie doc.length() als offset verwenden.

Die Formatierung des angefügten Strings geschieht durch den Parameter AttributeSet. Das ist ein Interface, welches eine Sammlung von Keys und Values beschreibt. Die Klasse SimpleAttributeSet implementiert nicht nur dieses Interface, sondern sogar auch das Interface MutableAttributeSet, was Ihnen das Hinzufügen neuer Attribute erlaubt (die Instanzen der Klasse arbeiten intern mit einer normalen Hashtable). Um nun diejenigen Keys und Values zu setzen, die tatsächlich eine Textauszeichnung bewirken, verwenden Sie am günstigsten die Hilfsklasse StyleConstants. Sie bietet Methoden wie diese:

public static void setBold(MutableAttributeSet a, boolean b)
public static void setForeground(MutableAttributeSet a, Color fg)
public static void setFontSize(MutableAttributeSet a, int s)

Abpictureung 3 zeigt, wie das Wort kleine in einer kleinen Schriftgröße und wie das Wort Schweinchen fett und rosa dargestellt wird (die normalen Attribute für Schweinchen eben).

Abb. 3: Fette rosa Schweinchen

Listing 2 zeigt, wie die kleinen fetten rosa Schweinchen durch unterschiedliche AttributeSets realisiert werden.

Listing 2

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;

public class TextMitAttributeSets {
public static void main(String args[]) throws Exception {
JFrame frame = new JFrame("TextMitAttributeSets");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JTextPane pane = new JTextPane();
pane.setEditable(false);

// Arbeite nicht mit der Pane, sondern mit dem darunterliegenden Model
Document doc = pane.getStyledDocument();

// Das erste Wort ohne irgendwelche speziellen Attribute (null)
doc.insertString(doc.getLength(), "Drei ",null);

// Baue ein AttributeSet für kleine Schrift und verwende dieses
// für das nächste Wort
SimpleAttributeSet klein = new SimpleAttributeSet();
StyleConstants.setFontSize(klein, 10);
doc.insertString(doc.getLength(), "kleine ",klein);

// Baue ein AttributeSet für fette rosa Schrift
// und verwende dieses für das letzte Wort
SimpleAttributeSet fettRosa = new SimpleAttributeSet();
StyleConstants.setBold(fettRosa, true);
StyleConstants.setForeground(fettRosa, Color.PINK);
doc.insertString(doc.getLength(),"Schweinchen", fettRosa);

JScrollPane scrollPane = new JScrollPane(pane);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.setSize(200, 60);
frame.setVisible(true);
}
}

Eine weitere Methode von StyledDocuments ist:

setCharacterAttributes(
int offset,
int length,
AttributeSet s,
boolean replace)

Sie erlaubt das Anwenden von AttributeSets auf Bereiche von schon vorhandenem Text. Der boolean Parameter replace bestimmt, ob die Attribute im übergebenen AttributeSet die schon bestehenden Attribute komplett ersetzen oder ob sie zusätzlich appliziert werden.

Wenn man zusätzlich einen Mechanismus hat, um die unterschiedlichen syntaktischen Elemente (Schlüsselwörter, Kommentare etc.) eines Textes zu erkennen, kann man schnell eine einfache Anzeige mit Syntax Highlighting bauen. Das kleine Codebeispiel auf der Leser-CD benutzt eine Regular Expression von Robert D. Cameron [3], um ein XML-Dokument in seine Bestandteile zu zerlegen. Die gefundenen Teile werden dann als Elemente, Processing Instructions und CDATA-Sections identifiziert und mit jeweils anderen AttributeSets versehen (Abb. 4). Als Übungsaufgabe können Sie das Beispiel zu einem XML-Editor erweitern.

Abb. 4: XML Syntax Highlighting in einer JTextPane

Ein Style ist ein Interface für benannte AttributeSets. Das uns schon bekannte StyledDocument erlaubt auch, neue Styles durch Angabe eines Namens zu definieren. Der Clou ist, dass neue Styles einen bestehenden anderen als Parent bekommen und dessen Attribute zur Darstellung herangezogen werden. Deswegen müssen Sie nur neue, abweichende Attribute wirklich setzen. Sie gehen dabei von einem default-Style aus, der bereits definiert ist. Die Methode addStyle() hat einen eigentlich irreführenden Namen, Sie müssen nämlich den Namen des neuen Styles und den Parent-Style übergeben und bekommen den neuen Style zurück. Diesen werden Sie in der Regel noch variieren, und zwar am günstigsten mit den Hilfsmethoden von StyleConstants (Listing 3)

Listing 3

// Der Default-Style als Parent der anderen wird angepaßt.
Style defaultStyle = doc.getStyle("default");
StyleConstants.setFontSize(defaultStyle, 14);
StyleConstants.setFontFamily(defaultStyle, "SansSerif");

// Zwei neue Styles mit dem Default-Style als Parent.
Style eingabeStyle = doc.addStyle("eingabe", defaultStyle);
StyleConstants.setForeground(eingabeStyle, Color.blue);
StyleConstants.setItalic(eingabeStyle, true);

Style ausgabeStyle = doc.addStyle("ausgabe", defaultStyle);

// Der Fehler-Style ist eine Variante des Ausgabe-Styles
Style fehlerStyle = doc.addStyle("fehler", ausgabeStyle);
StyleConstants.setForeground(fehlerStyle, Color.red);

Von einem StyledDocument können Sie sich die Styles über ihren Namen geben lassen. Damit können Sie zum Beispiel einfach eine append()-Methode implementieren, mit der ein Client ausgezeichneten Text anfügen kann ohne mit AttributeSets hantieren zu müssen (Listing 4). Das DialogSystem (Abb. 5) benutzt diese beiden Mechanismen.

Listing 4

Abb. 5: Unterschiedliche Styles für Eingaben, Ausgaben und Fehlermeldungen

Die JTextPane und die JEditorPane verwenden – abhängig vom Inhaltstyp – intern unterschiedliche EditorKits, mit denen wir bisher nicht viel Kontakt hatten. Ihr praktischer Nutzwert liegt darin, dass sie Actions anbieten, die Sie direkt mit AbstractButtons (also zum Beispiel JButtons oder JMenuItems) verbinden können. Wählt ein Benutzer einen solchen Knopf oder Menüeintrag aus, wird sogleich die actionPerformed()-Methode der Action aufgerufen und darüber der Text manipuliert; Sie müssen keinen weiteren zusätzlichen Code schreiben! Ein gewisser Aufwand besteht aber darin, aus dem Array aller implementierten Actions die geeigneten herauszufinden. Eine Übersicht über die bereits vorhandenen Actions bekommen Sie mit einer einfachen Schleife wie etwa folgender:

Das Beispiel auf der Leser-CD zeigt die Actions der drei EditorKit-Klassen DefaultEditorKit, StyledEditorKit und HTMLEditorKit. Darunter sind sowohl Actions zum Bewegen des Carets und zum Auswählen von Textbereichen als auch zum Setzen von AttributeSets. Und als Sahnehäubchen ist es bei dem StyledEditorKit möglich, mit geeigneten Konstruktoren zusätzliche, parametrisierte Actions zu erzeugen, die ebenfalls an AbstractButtons angebunden werden können und neue, bisher noch nicht definierte Styles setzen.

Abb. 6: Ein Editor mit drei JButtons zum Formatieren.

In dem Beispiel AnnotationEditorStyled (Listing 5 und Abb. 6) wird mit einer JTextPane und ihrem StyledEditorKit gearbeitet. In der Methode initActionTable() werden vom StyledEditorKit alle Actions unter ihrem Namen in einer Hashtable abgelegt. Die Methode initToolBar() erstellt zwei JButtons zum Formatieren des Textes in Fettdruck und Kursiv; als ActionListener fungieren die vorhandenen Actions mit den Namen font-bold und font-italic. Ein weiterer JButton soll die Schriftfarbe auf Rot stellen; für ihn wird eine spezielle Action erzeugt die eine Formatierung mit der im Konstruktor übergebenen Color vornimmt.

Listing 5

import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;

class AnnotationEditorStyled extends JFrame {

JTextPane pane;
Hashtable actionTable;

public static void main(String args[]) throws Exception {
new AnnotationEditorStyled();
}

AnnotationEditorStyled() {
super("AnnotationEditorStyled");
pane = new JTextPane();
JScrollPane scrollPane = new JScrollPane(pane);
getContentPane().add(scrollPane, BorderLayout.CENTER);
initActionTable();
initToolbar();
setBounds(10,10,225,180);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

// Alle Actions werden unter ihrem Namen in einer Hashtable abgelegt
private void initActionTable() {
actionTable = new Hashtable();
StyledEditorKit editorKit = (StyledEditorKit) pane.getEditorKit();
Action[] actions = editorKit.getActions();
for (int i=0; i 

Eine Variante zu diesem Beispiel ist der AnnotationEditorHTML.
Er verzichtet darauf, eine neue Action für die Schriftfarbe zu definieren, bedient
sich aber zusätzlich der write()-Methode des EditorKits um den vom Benutzer
formatierten Text auch im HTML-Quelltext auszugeben. In einer realen Anwendung
würden Sie ihn möglicherweise in einer Datei oder einer Datenbank abspeichern.

Ausblick

Die Packages unter javax.swing.text beinhalten noch viele weitere Konzepte,
von denen wir nicht gesprochen haben. Man kann zum Beispiel analysieren wie ein
Dokument in Elemente zerfällt und durch Views dargestellt wird. Was wir allerdings
in diesem kurzen Artikel behandelt haben reicht völlig aus, Texte optisch aufzuwerten
und ergonomischer zu gestalten.

Geschrieben von
Manfred Borzechowski
Kommentare

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *