From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001
From: Sven Eisenhauer
+Die Klasse JScrollPane
+wurde in den vorigen Abschnitten bereits mehrfach verwendet. In Verbindung
+mit den Klassen JTextArea
+und JList
+bestand ihre Aufgabe darin, Dialogelemente, die zu groß für
+den zur Verfügung stehenden Platz waren, mit Hilfe eines verschiebbaren
+Fensters ausschnittsweise sichtbar zu machen. Dabei war es ausreichend,
+das betreffende Dialogelement an den Konstruktor von JScrollPane
+zu übergeben und anstelle des Dialogelements selbst die JScrollPane-Instanz
+an den Container zu übergeben.
+
+
+JScrollPane
+besitzt aber noch weitere Fähigkeiten, die hier kurz vorgestellt
+werden sollen. Zunächst wollen wir uns ihre wichtigsten Konstruktoren
+ansehen:
+
+
+Die Argumente vsbPolicy und
+hsbPolicy geben an, wann ein
+horizontaler bzw. vertikaler Schieberegler eingeblendet wird. Hier
+können folgende Werte angegeben werden:
+
+
+
+Tabelle 38.1: Anzeige der Schieberegler bei JScrollPane
+Wenn die Argumente vsbPolicy
+und hsbPolicy nicht angegeben
+werden, blendet JScrollPane
+die Schieberegler nur dann ein, wenn sie wirklich benötigt werden
+(wenn also das Dialogelement in der jeweiligen Ausdehnung größer
+als der verfügbare Platz ist).
+
+
+Die beiden wichtigsten zusätzlichen Fähigkeiten von JScrollPane
+bestehen darin, Spalten- und Zeilenheader anzeigen und die Eckelemente
+mit beliebigen Komponenten belegen zu können (siehe Abbildung 38.1):
+
+
+Mit setColumnHeaderView
+kann eine Komponente für den Spaltenkopf angegeben werden. Sie
+wird über dem eigentlichen Dialogelement angezeigt und bei horizontalen
+Bewegungen zusammen mit diesem verschoben. Bei vertikalen Schieberbewegungen
+bleibt sie dagegen an ihrem Platz. Analog dazu kann mit setRowHeaderView
+ein Zeilenkopf angegeben werden, der links neben der eigentlichen
+Komponente platziert wird. Er wird bei vertikalen Bewegungen verschoben
+und behält bei horizontalen Bewegungen seinen Platz bei.
+
+
+Mit setCorner
+kann in einer beliebigen der vier ungenutzten Ecken einer JScrollPane
+ein Dialogelement platziert werden. Der Parameter key
+gibt dabei an, welche Ecke belegt werden soll. Als Argument kann eine
+der Konstanten LOWER_LEFT_CORNER,
+LOWER_RIGHT_CORNER ,
+UPPER_LEFT_CORNER
+oder UPPER_RIGHT_CORNER
+der Klasse JScrollPane
+angegeben werden.
+
+
+Zu beachten ist allerdings, dass die Eckflächen unter Umständen
+gar nicht zur Verfügung stehen. Die beiden Ecken auf der linken
+Seite sind beispielsweise nur dann vorhanden, wenn ein Zeilenkopf
+eingeblendet wurde. Die rechte obere ist nur vorhanden, wenn ein vertikaler
+Schieberegler eingeblendet wurde, und die rechte untere erfordert
+sogar die Anwesenheit beider Schieberegler. Auch kann die Anwendung
+praktisch keinen Einfluss auf die Größe der Ecken nehmen.
+Diese wird ausschließlich durch die Ausdehnung der Schieberegler
+und des Zeilenkopfes bestimmt. Summa sumarum ist die Möglichkeit,
+die Ecken belegen zu können, eine nur in Ausnahmefällen
+nützliche Eigenschaft.
+
+
+Abbildung 38.1: Die Anatomie einer JScrollPane
+Das folgende Beispiel zeigt ein Programm, in dem eine JScrollPane
+ein zu großes JPanel
+mit hundert Checkboxen aufnimmt. In den beiden rechten Ecken wird
+jeweils ein JLabel
+platziert, und als Spaltenkopf wird eine Instanz der Klasse ColumnHeader
+verwendet:
+
+
+
+
+
+
+ Titel
+ Inhalt
+ Suchen
+ Index
+ DOC
+ Handbuch der Java-Programmierung, 5. Auflage
+
+ <<
+ <
+ >
+ >>
+ API
+ Kapitel 38 - Swing: Komponenten II
+
+
+
+
+
+38.1 Spezielle Panels
+
+
+
+
+
+
+
+38.1.1 JScrollPane
+
+
+
+
+
+
+
+
+
+
+public JScrollPane(Component view)
+public JScrollPane(Component view, int vsbPolicy, int hsbPolicy)
+
+
+
+javax.swing.JScrollPane
+
+
+
+
+
+Konstante
+Bedeutung
+
+VERTICAL_SCROLLBAR_NEVER
+Der vertikale Schieberegler wird nie angezeigt.
+
+VERTICAL_SCROLLBAR_ALWAYS
+Der vertikale Schieberegler wird immer angezeigt.
+
+VERTICAL_SCROLLBAR_AS_NEEDED
+Der vertikale Schieberegler wird nur angezeigt,
+wenn er tatsächlich benötigt wird.
+
+HORIZONTAL_SCROLLBAR_NEVER
+Der horizontale Schieberegler wird nie angezeigt.
+
+HORIZONTAL_SCROLLBAR_ALWAYS
+Der horizontale Schieberegler wird immer
+angezeigt.
+
+HORIZONTAL_SCROLLBAR_AS_NEEDED
+Der horizontale Schieberegler wird nur angezeigt,
+wenn er tatsächlich benötigt wird.
+
+
+
+
+
+
+
+
+public void setColumnHeaderView(Component view)
+public void setRowHeaderView(Component view)
+
+public void setCorner(String key, Component corner)
+
+
+
+javax.swing.JScrollPane
+
+
+
+
+
+
+![]()
+
+
+
+![]()
+
+
+
+
+
+ Hinweis
+
+
+
+
+Listing 38.1: Die Klasse JScrollPane
+
+
+
+
+
+001 /* Listing3801.java */
+002
+003 import java.awt.*;
+004 import java.awt.event.*;
+005 import javax.swing.*;
+006
+007 public class Listing3801
+008 extends JFrame
+009 {
+010 public Listing3801()
+011 {
+012 super("JScrollPane");
+013 addWindowListener(new WindowClosingAdapter(true));
+014 //Dialogpanel erzeugen
+015 JPanel panel = new JPanel();
+016 panel.setLayout(new GridLayout(10, 10));
+017 for (int i = 1; i <= 100; ++i) {
+018 panel.add(new JCheckBox("Frage " + i));
+019 }
+020 //JScrollPane erzeugen
+021 JScrollPane scroll = new JScrollPane(panel);
+022 scroll.setCorner(
+023 JScrollPane.UPPER_RIGHT_CORNER,
+024 new JLabel("1", JLabel.CENTER)
+025 );
+026 scroll.setCorner(
+027 JScrollPane.LOWER_RIGHT_CORNER,
+028 new JLabel("2", JLabel.CENTER)
+029 );
+030 scroll.setColumnHeaderView(new ColumnHeader(panel, 10));
+031 //JScrollPane zur ContentPane hinzufügen
+032 getContentPane().add(scroll, BorderLayout.CENTER);
+033 }
+034
+035 public static void main(String[] args)
+036 {
+037 Listing3801 frame = new Listing3801();
+038 frame.setLocation(100, 100);
+039 frame.setSize(300, 150);
+040 frame.setVisible(true);
+041 }
+042 }
+043
+044 class ColumnHeader
+045 extends JComponent
+046 {
+047 JComponent component;
+048 int columns;
+049
+050 public ColumnHeader(JComponent component, int columns)
+051 {
+052 this.component = component;
+053 this.columns = columns;
+054 }
+055
+056 public void paintComponent(Graphics g)
+057 {
+058 int width = component.getSize().width;
+059 int height = getSize().height;
+060 int colwid = width / columns;
+061 for (int i = 0; i < columns; ++i) {
+062 g.setColor(i % 2 == 0 ? Color.yellow : Color.gray);
+063 g.fillRect(i * colwid, 0, colwid, height);
+064 }
+065 g.setColor(Color.black);
+066 g.drawLine(0, height - 1, width, height - 1);
+067 }
+068
+069 public Dimension getPreferredSize()
+070 {
+071 return new Dimension(component.getSize().width, 20);
+072 }
+073 }
+
+
+Listing3801.java
+
+Die Klasse ColumnHeader ist +aus JComponent +abgeleitet und wird als Spaltenkopf für die JPanel-Komponente +verwendet. Deren Spalten sind in diesem Sonderfall alle gleich breit. +Das Panel selbst und die Spaltenzahl werden an den Konstruktor übergeben, +und mit Hilfe dieser Angaben werden die nebeneinanderliegenden Spalten +als abwechselnd grau und gelb gefärbte Rechtecke angezeigt. Zusätzlich +wird eine schwarze Linie als untere Begrenzung des Spaltenkopfes gezeichnet. +Durch Überlagern von getPreferredSize +teilt ColumnHeader der JScrollPane +seine Größe mit. Die Breite entspricht dabei der Breite +der scrollbaren Komponente, die Höhe ist fest auf zwanzig Pixel +eingestellt. + +
+Die Ausgabe des Programms ist: +
+ +
+Abbildung 38.2: Die Klasse JScrollPane
+ + + + ++JSplitPane +ist ein Panel, mit dem zwei Komponenten neben- oder übereinander +platziert werden können. Die Komponenten werden dabei durch einen +sichtbaren Separator voneinander getrennt. Der Anwender kann den Separator +verschieben und so den Platz, der beiden Komponenten zur Verfügung +steht, variieren. Ein JSplitPane +ist beispielsweise nützlich, wenn Komponenten dargestellt werden +sollen, deren Breite oder Höhe von den darin enthaltenen Daten +abhängig ist. Das Programm braucht dann den benötigten Platz +nur grob vorzugeben, und der Anwender kann durch Verschieben des Separators +zur Laufzeit festlegen, wieviel Platz er jeder Komponente zur Verfügung +stellen will. + +
+Die Konstruktoren von JSplitPane +sind: +
+
+
++public JSplitPane(int orientation) + +public JSplitPane( + int orientation, + boolean continuousLayout +) + +public JSplitPane( + int orientation, + Component leftComponent, + Component rightComponent +) + +public JSplitPane( + int orientation, + boolean continuousLayout, + Component leftComponent, + Component rightComponent +) ++ + |
++javax.swing.JSplitPane | +
+Der Parameter orientation gibt +an, wie die Elemente zueinander angeordnet werden sollen. Hat er den +Wert HORIZONTAL_SPLIT, +werden sie nebeneinander, bei VERTICAL_SPLIT +übereinander angeordnet. Hat continuousLayout +den Wert true, +so wird schon beim Verschieben des Separators der Bildschirminhalt +aktualisiert. Andernfalls erfolgt das erst nach Ende des Verschiebevorgangs. +In den Parametern leftComponent +und rightComponent werden die +beiden einzubettenden Komponenten übergeben. Alle Eigenschaften +können auch nach der Instanzierung verändert bzw. abgefragt +werden: +
+
+
++public void setOrientation(int orientation) +public void setContinuousLayout(boolean continuousLayout) +public void setLeftComponent(Component comp) +public void setRightComponent(Component comp) +public void setTopComponent(Component comp) +public void setBottomComponent(Component comp) + +public int getOrientation() +public boolean isContinuousLayout() +public Component getLeftComponent() +public Component getTopComponent() +public Component getRightComponent() +public Component getBottomComponent() ++ + |
++javax.swing.JSplitPane | +
+Die Größe der beiden Komponenten richtet sich nach ihren +minimalen und gewünschten Abmessungen. JSplitPane +ermittelt diese Werte durch Aufruf von getPreferredSize +und getMinimumSize +auf den Komponentenobjekten. Es gilt: +
+
![]() |
+![]() |
+
+
+ +Manche Komponenten liefern beim Aufruf von getMinimumSize +die Größe, die sie beim ersten Sichtbarwerden auf dem Bildschirm +hatten. In diesem Fall ist der Separator überhaupt nicht veränderbar, +denn jede Verschiebung würde die minimale Größe einer +der beiden Komponenten unterschreiten. Falls ein solches Problem auftritt, +kann Abhilfe geschaffen werden, indem durch Aufruf von setMinimumSize +die minimale Größe der Komponenten vermindert wird. |
+
+
|
+![]() |
+
+Eine interessante Methode zur Modifikation des Separators ist setOneTouchExpandable: +
+
+
++public void setOneTouchExpandable(boolean newValue) ++ + |
++javax.swing.JSplitPane | +
+Wird sie mit true +als Argument aufgerufen, erhält der Separator zwei kleine Pfeile, +die jeweils auf eine der beiden Komponenten zeigen. Wird einer von +ihnen angeklickt, so wird der Separator komplett auf die angezeigte +Seite verschoben. Die auf dieser Seite liegende Komponente wird verdeckt +und die andere erhält den vollen zur Verfügung stehenden +Platz (die von getMinimumSize +definierten Grenzen werden dabei ignoriert). Ein weiterer Klick auf +den Separator gibt der verdeckten Komponente wieder ihre Minimalgröße. + +
+Das folgende Programm zeigt einen JFrame +mit einem horizontal geteilten JSplitPane. +Als Komponenten werden zwei Instanzen der weiter unten definierten +Klasse GridComponent verwendet, +die ein Gitternetz anzeigt, dessen Maschengröße proportional +zur Ausdehnung der Komponente ist: + + +
+
+
+
+001 /* Listing3802.java */
+002
+003 import java.awt.*;
+004 import java.awt.event.*;
+005 import javax.swing.*;
+006
+007 public class Listing3802
+008 extends JFrame
+009 {
+010 public Listing3802()
+011 {
+012 super("JSplitPane");
+013 addWindowListener(new WindowClosingAdapter(true));
+014 //Linkes Element erzeugen
+015 GridComponent grid1 = new GridComponent();
+016 grid1.setMinimumSize(new Dimension(50, 100));
+017 grid1.setPreferredSize(new Dimension(180, 100));
+018 //Rechtes Element erzeugen
+019 GridComponent grid2 = new GridComponent();
+020 grid2.setMinimumSize(new Dimension(100, 100));
+021 grid2.setPreferredSize(new Dimension(80, 100));
+022 //JSplitPane erzeugen
+023 JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+024 sp.setLeftComponent(grid1);
+025 sp.setRightComponent(grid2);
+026 sp.setOneTouchExpandable(true);
+027 sp.setContinuousLayout(true);
+028 getContentPane().add(sp, BorderLayout.CENTER);
+029 }
+030
+031 public static void main(String[] args)
+032 {
+033 Listing3802 frame = new Listing3802();
+034 frame.setLocation(100, 100);
+035 frame.setSize(300, 200);
+036 frame.setVisible(true);
+037 }
+038 }
+039
+040 class GridComponent
+041 extends JComponent
+042 {
+043 public void paintComponent(Graphics g)
+044 {
+045 g.setColor(Color.gray);
+046 int width = getSize().width;
+047 int height = getSize().height;
+048 for (int i = 0; i < 10; ++i) {
+049 g.drawLine(i * width / 10, 0, i * width / 10, height);
+050 }
+051 for (int i = 0; i < 10; ++i) {
+052 g.drawLine(0, i * height / 10, width, i * height / 10);
+053 }
+054 g.setColor(Color.black);
+055 g.drawString("" + width, 5, 15);
+056 }
+057 }
+
+ |
++Listing3802.java | +
+Die Ausgabe des Programms ist: +
+ +
+Abbildung 38.3: Die Klasse JSplitPane
+ + + + ++Als letztes der speziellen Panels wollen wir uns die Klasse JTabbedPane +ansehen. Mit ihr ist es möglich, Dialoge zu erstellen, die eine +Reihe von Registerkarten enthalten. Das sind Unterdialoge, die über +ein am Rand befindliches Register einzeln ausgewählt werden können. +Derartige Registerkarten werden beispielsweise in Konfigurationsdialogen +verwendet, wenn die zu bearbeitenden Optionen nicht alle auf eine +Seite passen. + +
+JTabbedPane +stellt zwei Konstruktoren zur Verfügung: +
+
+
++public JTabbedPane() +public JTabbedPane(int tabPlacement) ++ + |
++javax.swing.JTabbedPane | +
+Der erste von beiden erzeugt ein JTabbedPane +mit oben liegenden Registern. Beim zweiten kann explizit angegeben +werden, auf welcher Seite die Register angeordnet werden sollen. Hier +können die Konstanten TOP, +BOTTOM, +LEFT +oder RIGHT +aus dem Interface SwingConstants +übergeben werden. Üblich ist es, die Register links oder +oben anzuordnen. Mit den Methoden getTabPlacement +und setTabPlacement +kann auch nachträglich auf die Anordnung zugegriffen werden. + +
+Nach der Instanzierung ist der Registerdialog zunächst leer. +Um Registerkarten hinzuzufügen, kann eine der Methoden addTab +oder insertTab +aufgerufen werden: +
+
+
++public void addTab( + String title, + Icon icon, + Component component, + String tip +) + +public void insertTab( + String title, + Icon icon, + Component component, + String tip, + int index +) ++ + |
++javax.swing.JTabbedPane | +
+Der Parameter title gibt die +Beschriftung des Registereintrags an. Mit icon +kann zusätzlich ein Icon und mit tip +ein Tooltiptext hinzugefügt werden. Der Parameter component +gibt das darzustellende Dialogelement an. Meist wird hier eine Containerklasse +übergeben (z.B. ein JPanel), +die den entsprechenden Unterdialog enthält. Die Parameter icon +und tip sind optional, d.h. +es gibt die beiden Methoden auch ohne sie. addTab +fügt die Registerkarte am Ende ein, bei insertTab +kann die Einfügeposition mit dem Parameter index +selbst angegeben werden. + +
+Bereits definierte Registerkarten können auch wieder entfernt +werden: +
+
+
++public void removeTabAt(int index) +public void removeAll() ++ + |
++javax.swing.JTabbedPane | +
+removeTabAt +entfernt die Karte mit dem angegebenen Index, removeAll +entfernt alle Karten. + +
+Mit getTabCount +kann die Anzahl der Registerkarten ermittelt werden, und mit getComponentAt +kann die Komponente einer beliebigen Registerkarte ermittelt werden. +Mit setComponentAt +kann der Inhalt einer Registerkarte sogar nachträglich ausgetauscht +werden: +
+
+
++public int getTabCount() + +public Component getComponentAt(int index) +public void setComponentAt(int index, Component component) ++ + |
++javax.swing.JTabbedPane | +
+In einem Registerdialog ist immer genau eine der Karten selektiert. +Mit getSelectedIndex +kann deren Index ermittelt werden, mit getSelectedComponent +sogar direkt auf ihren Inhalt zugegriffen werden. Die Methode setSelectedIndex +erlaubt es, programmgesteuert eine beliebige Registerkarte auszuwählen: +
+
+
++public int getSelectedIndex() +public Component getSelectedComponent() + +public void setSelectedIndex(int index) ++ + |
++javax.swing.JTabbedPane | +
+Registerkarten besitzen eine Reihe von Eigenschaften, die einzeln +verändert werden können. Die wichtigste von ihnen ist die +Möglichkeit, sie zu aktivieren oder zu deaktivieren. Nur aktivierte +Karten können ausgewählt werden, deaktivierte dagegen nicht. +Der Zugriff auf den Aktivierungsstatus erfolgt mit den Methoden setEnabledAt +und isEnabledAt: +
+
+
++public boolean isEnabledAt(int index) +public void setEnabledAt(int index, boolean enabled) ++ + |
++javax.swing.JTabbedPane | +
+
![]() |
+![]() |
+
+
+ +Bei jeder Änderung der Selektion versendet ein JTabbedPane +ein ChangeEvent +an registrierte ChangeListener. +Auf diese Weise ist es möglich, Programmcode zu schreiben, der +beim Anwählen oder Verlassen einzelner Registerkarten ausgeführt +wird. Eine der Anwendungen hierfür besteht darin, die Dialoge +auf den Registerkarten erst dann zu erzeugen, wenn sie wirklich gebraucht +werden. Dazu wird an alle Registerkarten zunächst ein leeres +Panel übergeben und erst in der Methode stateChanged +durch den eigentlichen Dialog ersetzt. Auf diese Weise spart das Programm +beim Initialisieren des Registerdialogs Rechenzeit und Speicher, was +sich vor allem bei komplexen Dialogen positiv bemerkbar machen könnte. |
+
+
|
+![]() |
+
+Das folgende Programm zeigt ein einfaches Beispiel eines Registerdialogs. +Es erstellt ein JTabbedPane +mit fünf Registerkarten, die jeweils ein JPanel +mit einem Label und einem Button enthalten. Das Label zeigt den Namen +der Karte an, der Button dient dazu, die jeweils nächste Karte +auszuwählen. Dessen Aktivität ist in der Klasse NextTabActionListener +gekapselt. Als Besonderheit wird nach jedem Seitenwechsel requestDefaultFocus +auf der neuen Seite aufgerufen, um automatisch dem ersten Dialogelement +den Fokus zu geben. + + +
+
+
+
+001 /* Listing3803.java */
+002
+003 import java.awt.*;
+004 import java.awt.event.*;
+005 import javax.swing.*;
+006
+007 public class Listing3803
+008 extends JFrame
+009 {
+010 JTabbedPane tp;
+011
+012 public Listing3803()
+013 {
+014 super("JTabbedPane");
+015 addWindowListener(new WindowClosingAdapter(true));
+016 tp = new JTabbedPane();
+017 for (int i = 0; i < 5; ++i) {
+018 JPanel panel = new JPanel();
+019 panel.add(new JLabel("Karte " + i));
+020 JButton next = new JButton("Weiter");
+021 next.addActionListener(new NextTabActionListener());
+022 panel.add(next);
+023 tp.addTab("Tab" + i, panel);
+024 }
+025 getContentPane().add(tp, BorderLayout.CENTER);
+026 }
+027
+028 class NextTabActionListener
+029 implements ActionListener
+030 {
+031 public void actionPerformed(ActionEvent event)
+032 {
+033 int tab = tp.getSelectedIndex();
+034 tab = (tab >= tp.getTabCount() - 1 ? 0 : tab + 1);
+035 tp.setSelectedIndex(tab);
+036 ((JPanel)tp.getSelectedComponent()).requestDefaultFocus();
+037 }
+038 }
+039
+040 public static void main(String[] args)
+041 {
+042 Listing3803 frame = new Listing3803();
+043 frame.setLocation(100, 100);
+044 frame.setSize(300, 200);
+045 frame.setVisible(true);
+046 }
+047 }
+
+ |
++Listing3803.java | +
+Die Ausgabe des Programms ist: +
+ +
+Abbildung 38.4: Die Klasse JTabbedPane
+| Titel + | Inhalt + | Suchen + | Index + | DOC + | Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 + |
| << + | < + | > + | >> + | API + | © 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de + |