Wenn Sie editierbaren Text in Java darstellen möchten, haben Sie mehrere Möglichkeiten. Sie können bspw. eine JTextArea oder ein JTextField verwenden und simple Formatierungen daran vornehmen, die das komplette Textfeld betreffen (bspw. die Hintergrundfarbe oder Schrift verändern). Sollen sich bestimmte Textabschnitte voneinander unterscheiden, haben Sie zuerst einmal ein Problem, weil das mit einer JTextArea oder einem JTextField nicht funktioniert. Eine Alternative wäre es, HTML in einem JEditorPane darzustellen. Die Editierbarkeit ist in diesem Fall jedoch stark eingeschränkt. Es gibt noch eine weitere Möglichkeit: Sie können ein javax.swing.JTextPane mit einem eigenen javax.swing.text.StyledDocument versehen. Diese Variante wird Ihnen in diesem Kapitel vorgestellt.
Ziel
Das Ziel dieses Kapitels ist es, ein StyledDocument zu entwickeln, mit dem Sie Text in einem JTextPane individuell formatiert darstellen können. Außerdem bekommen Sie noch eine kleine GUI-Komponente, mit der Sie die Funktionalität des StyledDocuments in einer JTextPane ausprobieren können.

Beachten Sie jedoch, dass es in diesem Kapitel in erster Linie um das StyledDocument und nicht die grafische Darstellung geht. Die GUI-Komponente wird folglich nicht bis ins letzte Detail ausgereift sein und deren Funktionsweise nicht weiter erläutert werden.
Das StyledDocument
Ein StyledDocument formatiert den Inhalt einer Text-Komponente, die mit einem solchen Document umgehen kann. Hierzu gehört bspw. die javax.swing.JTextPane. Wie genau so ein Document funktioniert, können Sie detailliert in der Java API-Dokumentation nachlesen. Der Text-Komponente wird das Document gesetzt. Anschließend kann (über den Aufruf von Methoden des Documents) die Darstellung der Text-Komponente an beliebigen Stellen verändert werden.
Unser Document nennen wir EditorDocument und lassen es direkt von javax.swing.text.DefaultStyledDocument erben. Das DefaultStyledDocument nimmt uns im Vergleich zum Interface StyledDocument schon einige Arbeit ab.
package de.jbb.style;
import javax.swing.text.DefaultStyledDocument;
public class EditorDocument extends DefaultStyledDocument {}
Um nun bspw. die Schriftart für eine bestimmte Textstelle zu verändern, erzeugen wir eine Methode, der die neue Schriftart übergeben wird. Zusätzlich muss natürlich noch spezifiziert werden, ab welcher Position (start) und bis zu welcher Position (end) die Schriftart verändert werden soll.
public void setFont(int start, int end, String font) {}
Für dieses Vorhaben benötigen Sie zuerst ein javax.swing.text.SimpleAttributeSet. Dieses SimpleAttributeSet definiert, wie die Textstelle formatiert sein soll (bspw. fett, kursiv, 16 Punkte groß, …).
SimpleAttributeSet sas = new SimpleAttributeSet();
Der nächste Schritt besteht darin, dem SimpleAttributeSet die neue Schriftart zu setzen. Hierzu wird die Klasse javax.swing.text.StyleConstants verwendet. Diese Klasse bietet mehrere statische Methoden, mit welchen das SimpleAttributeSet manipuliert werden kann. Um die Schriftart zu verändern, wird die Methode setFontFamily mit dem SimpleAttributeSet und dem Namen der gewünschten Schriftart aufgerufen.
StyleConstants.setFontFamily(sas, font);
Natürlich muss das manipulierte SimpleAttributeSet noch den entsprechenden Zeichen zugeordnet werden. Dies geschieht mit dem Aufruf setCharacterAttributes(int offSet, int length, AttributSet s, boolean replace) der Klasse DefaultStyledDocument.
offset = Position, ab der die Zeichen formatiert werden sollen
length = Anzahl der zu formatierenden Zeichen
s = Formatierung der Zeichen als SimpleAttributeSet
replace = true, falls die alten Formatierungen ersetzt werden sollen
setCharacterAttributes(start, end - start, sas, false);
Daraus resultiert die Methode
public void setFont(int start, int end, String font) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setFontFamily(sas, font);
setCharacterAttributes(start, end - start, sas, false);
}
Anhand dieser Methode fällt es nun nicht mehr schwer, weitere abzuleiten:
// kursiv
public void setItalic(int start, int end, boolean active) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setItalic(sas, active);
setCharacterAttributes(start, end - start, sas, false);
}
// fett
public void setBold(int start, int end, boolean active) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setBold(sas, active);
setCharacterAttributes(start, end - start, sas, false);
}
// unterstrichen
public void setUnderline(int start, int end, boolean active) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setUnderline(sas, active);
setCharacterAttributes(start, end - start, sas, false);
}
// Schriftgröße
public void setFontSize(int start, int end, int size) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setFontSize(sas, size);
setCharacterAttributes(start, end - start, sas, false);
}
// Textfarbe
public void setForeground(int start, int end, Color col) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setForeground(sas, col);
setCharacterAttributes(start, end - start, sas, false);
}
// Hintergrundfarbe
public void setBackground(int start, int end, Color col) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setBackground(sas, col);
setCharacterAttributes(start, end - start, sas, false);
}
Die Attribute fett, kursiv und unterstrichen können jeweils nur zwei Zustände annehmen – aktiv oder inaktiv. Ob ein Text nun bspw. unterstrichen oder der Unterstrich entfernt werden soll, spezifiziert der Parameter boolean active. Um zwischen den beiden Zuständen zu wechseln, sollten Sie noch eine Methode bereitstellen, die den aktuellen Zustand einer Textstelle auslesen kann (bspw. fett oder nicht fett). Hierzu kommen Sie mit der Methode getCharacterElement(pos) des DefaultStyledDocument an ein javax.swing.text.Element. Aus diesem können Sie wiederum die aktuell gesetzten Formatierungen über die Methode getAttributes auslesen. Die spezifischen Formatierungen (fett, kursiv, Schriftgröße, …) dieses Zeichens werden über eine statische Methode der bereits bekannten Klasse StyleConstants abgefragt.
public boolean isItalic(int pos) {
Element element = getCharacterElement(pos);
return StyleConstants.isItalic(element.getAttributes());
}
public boolean isBold(int pos) {
Element element = getCharacterElement(pos);
return StyleConstants.isBold(element.getAttributes());
}
public boolean isUnderline(int pos) {
Element element = getCharacterElement(pos);
return StyleConstants.isUnderline(element.getAttributes());
}
Als Letztes soll es noch die Möglichkeit geben, Bilder in den Text einzufügen. Dies ist prinzipiell erst einmal ähnlich wie bei den bereits bekannten Methoden. Der einzige Unterschied besteht darin, dass ein Bild natürlich kein “CharacterAttribute” ist. Deshalb kann hier nicht die Methode setCharacterAttributes verwendet werden. Stattdessen greifen wir auf die Methode insertString(int position, String str, AttributeSet as) der Klasse DefaultStyledDocument zu, welche den Inhalt des Dokuments manipulieren kann.
public void setIcon(int pos, ImageIcon ico) throws BadLocationException {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setIcon(sas, ico);
insertString(pos, " ", sas);
}
Hiermit ist unser EditorDocument fertig.
package de.jbb.style;
import java.awt.Color;
import javax.swing.ImageIcon;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
public class EditorDocument extends DefaultStyledDocument {
private static final long serialVersionUID = -2191570369962370294L;
public void setIcon(int pos, ImageIcon ico) throws BadLocationException {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setIcon(sas, ico);
insertString(pos, " ", sas);
}
public boolean isItalic(int pos) {
Element element = getCharacterElement(pos);
return StyleConstants.isItalic(element.getAttributes());
}
public boolean isBold(int pos) {
Element element = getCharacterElement(pos);
return StyleConstants.isBold(element.getAttributes());
}
public boolean isUnderline(int pos) {
Element element = getCharacterElement(pos);
return StyleConstants.isUnderline(element.getAttributes());
}
public void setItalic(int start, int end, boolean active) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setItalic(sas, active);
setCharacterAttributes(start, end - start, sas, false);
}
public void setBold(int start, int end, boolean active) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setBold(sas, active);
setCharacterAttributes(start, end - start, sas, false);
}
public void setUnderline(int start, int end, boolean active) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setUnderline(sas, active);
setCharacterAttributes(start, end - start, sas, false);
}
public void setFont(int start, int end, String font) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setFontFamily(sas, font);
setCharacterAttributes(start, end - start, sas, false);
}
public void setFontSize(int start, int end, int size) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setFontSize(sas, size);
setCharacterAttributes(start, end - start, sas, false);
}
public void setForeground(int start, int end, Color col) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setForeground(sas, col);
setCharacterAttributes(start, end - start, sas, false);
}
public void setBackground(int start, int end, Color col) {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setBackground(sas, col);
setCharacterAttributes(start, end - start, sas, false);
}
public EditorDocument() {
super();
}
public EditorDocument(Content arg0, StyleContext arg1) {
super(arg0, arg1);
}
public EditorDocument(StyleContext arg0) {
super(arg0);
}
}
Text formatieren
Um nun Text formatiert darstellen zu können, müssen Sie ein neues JTextPane erzeugen, und diesem das EditorDocument zuweisen.
EditorDocument doc = new EditorDocument(); JTextPane pane = new JTextPane(doc);
Nun können Sie mit den Methoden des EditorDocuments den Text nach belieben formatieren. Verändern Sie bspw. die Schriftgröße der Zeichen 5 bis 15 in der JTextPane auf 20 Punkte:
doc.setFontSize(5, 15, 20);
Oder lassen Sie die Zeichen 20 bis 40 fett darstellen:
doc.setBold(20, 40, true);
Sinnvoller ist es natürlich, wenn die Zeichen nicht willkürlich verändert werden. Wenn Sie dem User die Möglichkeit geben, eine bestimmte Textstelle zu formatieren, dann wird das in der Regel die sein, die er momentan mit der Maus markiert hat. Über die Methoden getSelectionStart() und getSelectionEnd() des JTextPanes können Sie die aktuelle Start und End Position der Selektion auslesen.
Testen
Wie am Anfang dieses Kapitels angekündigt, bekommen Sie noch eine Klasse, mit der Sie die grundlegenden Funktionen testen können. Beachten Sie, dass diese wirklich nur zu Testzwecken gedacht und nicht sonderlich ausgereift ist. Es liegt an Ihnen diese Klasse bei Bedarf noch auszubauen!
package de.jbb.style;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.BadLocationException;
public class EditorComponent extends JPanel implements ActionListener, ItemListener {
private static final long serialVersionUID = 2232612551746729177L;
// Textpane zur Darstellung
private JTextPane pane = null;
// Unser Document
private EditorDocument doc = null;
// Button um etwas fett zu markieren
private JButton bold = null;
// Button um etwas krusiv zu markieren
private JButton italic = null;
// Button um etwas zu unterstreichen
private JButton underline = null;
// Button um die Schriftfarbe zu ändern
private JButton foreground = null;
// Button um die Hintergrundfarbe zu ändern
private JButton background = null;
// Button um ein Bild einzufügen
private JButton pic = null;
// JComboBox mit allen verfügbaren Schriftarten
private JComboBox fonts = null;
// JComboBox mit unterschiedlichen Schriftgrößen
private JComboBox size = null;
// Toolbar für die Buttons und ComboBoxen
private JToolBar bar = null;
// JFileChooser um ggf. ein Bild auswählen zu können
private JFileChooser imch = null;
public EditorComponent() {
setLayout(new BorderLayout());
// Anzeigebereich erzeugen
this.doc = new EditorDocument();
this.pane = new JTextPane(this.doc);
// Tools initialisieren
this.bold = new JButton("F");
this.italic = new JButton("I");
this.underline = new JButton("U");
this.foreground = new JButton("SF");
this.background = new JButton("HF");
this.pic = new JButton("Bild");
this.fonts = new JComboBox();
this.size = new JComboBox();
// restliche Komponenten initialisieren
this.bar = new JToolBar();
this.imch = new JFileChooser();
// Einen FileFilter für die Bildauswahl setzen
// es dürfen nur jpg und png Bilder eingefügt werden
this.imch.setFileFilter(new FileFilter() {
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
if (f.getAbsolutePath().toLowerCase().endsWith(".png")) {
return true;
}
if (f.getAbsolutePath().toLowerCase().endsWith(".jpg")) {
return true;
}
return f.getAbsolutePath().toLowerCase().endsWith(".jpeg");
}
public String getDescription() {
return "Image (*.jpg, *.jpeg, *.png)";
}
});
// Verschiedene Schriftgrößen initialisieren
for (int i = 8; i < 30; i += 2) {
this.size.addItem(i);
}
// Alle verfügbaren Schriftarten auslesen und in der JComboBox anzeigen
String[] font = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
for (int i = 0; i < font.length; i++) {
this.fonts.addItem(font[i]);
}
// GUI zusammenbauen
add(this.pane);
add(this.bar, BorderLayout.NORTH);
this.bar.add(this.bold);
this.bar.add(this.italic);
this.bar.add(this.underline);
this.bar.add(this.fonts);
this.bar.add(this.size);
this.bar.add(this.foreground);
this.bar.add(this.background);
this.bar.add(this.pic);
// Listener an die Tools hängen
this.bold.addActionListener(this);
this.italic.addActionListener(this);
this.underline.addActionListener(this);
this.foreground.addActionListener(this);
this.background.addActionListener(this);
this.pic.addActionListener(this);
this.fonts.addItemListener(this);
this.size.addItemListener(this);
}
public void actionPerformed(ActionEvent evt) {
// Start und End Position der Selektion auslesen
int start = this.pane.getSelectionStart();
int end = this.pane.getSelectionEnd();
// Falls keine sinnvolle Selektion => kompletten Text bearbeiten
if (start >= end) {
start = 0;
end = this.pane.getText().length();
}
// Fett setzen
if (evt.getSource() == this.bold) {
// Falls das erste, selektierte Zeichen bereits fett dargestellt
// wird, die Fett-Formatierung aufheben, ansonsten den selektierten
// Bereich fett darstellen
this.doc.setBold(start, end, !this.doc.isBold(start));
}
// Kursiv setzen
else if (evt.getSource() == this.italic) {
// siehe "Fett setzen"
this.doc.setItalic(start, end, !this.doc.isItalic(start));
}
// Unterstreichen
else if (evt.getSource() == this.underline) {
// siehe "Fett setzen"
this.doc.setUnderline(start, end, !this.doc.isUnderline(start));
}
// Schriftfarbe verändern
else if (evt.getSource() == this.foreground) {
Color col = JColorChooser.showDialog(this, "Schriftfarbe auswählen", Color.BLACK);
if (col != null) {
this.doc.setForeground(start, end, col);
}
}
// Hintergrundfarbe verändern
else if (evt.getSource() == this.background) {
Color col = JColorChooser.showDialog(this, "Hintergrundfarbe auswählen", Color.WHITE);
if (col != null) {
this.doc.setBackground(start, end, col);
}
}
// Bild einfügen
else if (evt.getSource() == this.pic) {
int retval = this.imch.showOpenDialog(this);
if (retval == JFileChooser.APPROVE_OPTION) {
this.pane.replaceSelection("");
try {
this.doc.setIcon(this.pane.getCaretPosition(), new ImageIcon(this.imch.getSelectedFile().getAbsolutePath()));
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
}
}
}
public void itemStateChanged(ItemEvent evt) {
// Nur reagieren, falls etwas neues selektiert wurde
if (evt.getStateChange() == ItemEvent.SELECTED) {
// Start und End Position der Selektion auslesen
int start = this.pane.getSelectionStart();
int end = this.pane.getSelectionEnd();
// Falls keine sinnvolle Selektion => kompletten Text bearbeiten
if (start >= end) {
start = 0;
end = this.pane.getText().length();
}
// Schriftart setzen
if (evt.getSource() == this.fonts) {
this.doc.setFont(start, end, this.fonts.getSelectedItem().toString());
}
// Schriftgröße setzen
else if (evt.getSource() == this.size) {
this.doc.setFontSize(start, end, Integer.parseInt(this.size.getSelectedItem().toString()));
}
}
}
// Main Methode zum Testen
public static void main(String[] args) {
JFrame frame = new JFrame("EditorComponent Test");
frame.add(new JScrollPane(new EditorComponent()));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 300);
frame.setVisible(true);
}
}


{ 1 } Comments
SUPER, VIELEN DANK!!!
sehr hilfreicher Beitrag!!
Danke! Jens
Kommentar verfassen