Swing

Inhalt

 

 

Texteingabe und Textausgabe

 

Benutzergesteuerte Layouts

 

Auswahlfelder

 

Menüs

 

Dialoge

 

Übungsaufgaben

Texteingabe und Textausgabe

Wir haben schon im Kapitel über Grafikausgaben und im Kapitel über Events eine Reihe von Komponenten (Panel, Canvas, Button) kennen gelernt, die sich in Java-Applikationen mit graphischer Benutzeroberfläche einsetzen lassen. Dort hatten wir auch schon Layouts kennen gelernt zur Anordnung dieser Komponenten.

Wir wollen hier zunächst die wichtigsten Komponenten kennen lernen, mit denen sich Text ein- und ausgeben lässt.

Die Komponente zum Anzeigen eines statischen Textes heißt Label. Der Text lässt sich entweder mit dem Konstruktor oder mit der Methode setText setzen. Ein Label ist immer einzeilig.

Die Komponente, mit der der Benutzer einzeiligen Text ein- und ausgeben kann, ist JTextField. Die Textvorgabe erfolgt genau wie bei Label. Man kann das JTextField nicht-editierbar machen, um nur einen Text anzuzeigen. Gewöhnlich will man aber, dass der Benutzer den angezeigten Text verändern kann. Dann kann man den Text einfach mit getText auslesen. Ein JTextfield kann auch ein ActionEvent produzieren, wenn der Benutzer die Enter-Taste drückt.

Will man mehrzeiligen Text ein- und ausgeben, so verwendet man eine JTextArea. Im Unterschied zum TextField erzeugt diese Methode kein Event, wenn die Enter-Taste gedrückt wird, sondern erzeugt dadurch einen neuen Paragraphen. Man verwendet gewöhnlich kein Event dieser Komponenten, sondern wertet den Inhalt z.B. beim Druck auf einen Knopf aus.

Damit wir im Text scrollen können, verwenden wir eine JScrollPane. Dies ist eine Komponente, die ein JTextField aufnehmen kann und Scrollbalken anzeigt, sofern dies notwendig ist.

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

class TestFrame extends JFrame
    implements ActionListener
{   JTextArea Out;
    JTextField In;
    
    public TestFrame ()
    {   super("Test");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
        setLayout(new BorderLayout());
        setSize(500,400);
        
        // Label:
        add("North",new JLabel("Test",JLabel.CENTER));
        
        // TextArea:
        Out=new JTextArea();
        JScrollPane OutP=new JScrollPane(Out);
        add("Center",OutP);
        OutP.setPreferredSize(new Dimension(400,400));
        
        // TextField:
        add("South",In=new JTextField());
        In.addActionListener(this);
        
        pack();
        setVisible(true);
    }
    
    public void actionPerformed (ActionEvent e)
    {   if (e.getSource()==In) // enter in TextField
        {   Out.append(In.getText()+"\n");
            In.setText("");
        }
    }
}

public class Test
{   public static void main (String args[])
    {   new TestFrame();
    }
}

Das Beispiel funktioniert so, dass der Benutzer in der Eingabezeile unten einen Text eingeben kann. Drückt man dort die Enter-Taste, so wird der Text an die JTextArea oben angehängt und die Eingabezeile gelöscht.

Es wäre in diesem Beispiel sinnvoll, die JTextArea durch setEditable(false) nicht edierbar zu machen. Außerdem könnte man mit setBackground(Color.gray) den Hintergrund z.B. auf Grau einstellen.

Benutzergesteuerte Layouts

Wir besprechen hier zwei Layout-Varianten, die vom Benutzer verändert werden können.

JSplitPlane erzeugt eine vertikal oder horizontal geteilte Ansicht, wobei der Benutzer die Teilung aktiv verschieben kann. Der Container enthält also zwei Komponenten. Deren Minimalgröße bestimmt, wie weit sich der Schieber nach rechts oder links verschieben lässt. Wir setzen daher die Minimalgröße unserer Canvas auf 10 mal 10 Pixel. Zu beachten ist noch, dass der übrige Raum zwischen den Komponenten verteilt wird. Wir wollen eine gleiche Aufteilung und setzen daher das Aufteilungsgewicht auf 0.5.

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

/**
 * Eine Canvas, die mit Farbe gefüllt wird.
 */
class TestCanvas extends Canvas
{   Color C;
    
    public TestCanvas (Color c)
    {   C=c;
        setMinimumSize(new Dimension(10,10));
    }
    
    public void paint (Graphics g)
    {   Dimension d=getSize();
        g.setColor(C);
        g.fillRect(0,0,d.width,d.height);
    }
}

/**
 * Das ist ein Panel mit zwei horizontalen Farbfeldern.
 * Der Benutzer kann die Trennung verschieben.
 */
class TestPanel extends JPanel
{   public TestPanel ()
    {   setPreferredSize(new Dimension(400,200));
        // Erzeuge eine SplitPane mit zwei Farbfeldern:
        JSplitPane pane=new JSplitPane(
            JSplitPane.HORIZONTAL_SPLIT,
            new TestCanvas(Color.RED),
            new TestCanvas(Color.GREEN));
        pane.setResizeWeight(0.5);
        // In der Mitte hinzufügen:
        setLayout(new BorderLayout());
        add("Center",pane);
    }   
}

public class Test extends JFrame
{   public Test ()
    {   super("Test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add("Center",new TestPanel());
        pack();
    }
    
    public static void main (String args[])
    {   JFrame F=new Test();
        F.setVisible(true);
    }
}

Die andere Art, mehrere Komponenten auf kleinem Raum unterzubringen ist JTabbedPane.

Dazu müssen wir nur den Code folgendermaßen ändern.

JTabbedPane pane=new JTabbedPane();
pane.add("Rot",new TestCanvas(Color.RED));
pane.add("Grün",new TestCanvas(Color.GREEN));

Es gibt noch die Möglichkeit, Icons anstelle von Text und Tooltips (Texte, die beim Verharren der Maus über dem Reiter erscheinen) zu verwenden.

Auswahlfelder

Oftmals will man den Benutzer aus mehreren Möglichkeiten auswählen lassen. Dazu gibt es die JComboBox. Beim Klick auf ein Klappfeld öffnet sich eine Liste mit Möglichkeiten. Wählt der Benutzer eine davon aus, so wird ein ActionEvent ausgelöst. Im folgenden Programm fangen wir dieses Event ab und stellen das Resultat in einen Label dar.

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class Test extends JFrame
    implements ActionListener
{   JLabel label;
    JComboBox box;
    
    String strings[]={"Apfel","Birne","Kirsche"};
    
    public Test ()
    {   super("Test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        label=new JLabel("",JLabel.CENTER);
        label.setPreferredSize(new Dimension(100,50));
        box=new JComboBox(strings);
        box.addActionListener(this);
        
        setLayout(new BorderLayout());
        add("Center",label);
        add("South",box);
        
        pack();
    }
    
    public void actionPerformed (ActionEvent e)
    {   if (e.getSource()==box)
        {   if (box.getSelectedIndex()>=-1)
            {   label.setText(box.getSelectedItem().toString());
            }
        }
    }
    
    public static void main (String args[])
    {   JFrame F=new Test();
        F.setVisible(true);
    }
}

Man kenn den ausgewählten Index abfragen oder auch das ausgewählte Objekt, das man mit toString umwandeln kann. Natürlich kann man die Auswahl auch über das Programm setzen. Näheres findet man in der Dokumentation.

Die entsprechende Komponente des AWT heißt Choice und arbeitet ziemlich ähnlich.

Will man alle Auswahlmöglichkeiten gleichzeitig darstellen, so gibt es JList. Diese neue Komponente erzeugt ein Ereignis vom Typ ListSelectionEvent, das es nur in Swing gibt. Es muss daher ein anderes Paket eingebunden werden. Im folgenden Programm betten wir die Liste außerdem mit Scrollbalken ein, die erscheinen würden, wenn die Liste zu lang wäre.

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

public class Test extends JFrame
    implements ListSelectionListener
{   JLabel label;
    JList list;
    
    String strings[]={"Apfel","Birne","Kirsche"};
    
    public Test ()
    {   super("Test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        label=new JLabel("",JLabel.CENTER);
        label.setPreferredSize(new Dimension(100,50));
        list=new JList(strings);
        list.addListSelectionListener(this);
        
        setLayout(new BorderLayout());
        add("Center",label);
        add("South",new JScrollPane(list));
        
        pack();
    }
    
    public void valueChanged (ListSelectionEvent e)
    {   if (e.getSource()==list)
        {   if (list.getSelectedIndex()>=-1)
            {   label.setText(list.getSelectedValue().toString());
            }
        }
    }
    
    public static void main (String args[])
    {   JFrame F=new Test();
        F.setVisible(true);
    }

}

Menüs

Gemeint sind die Auswahlmenüs, die unterhalb der Titelzeile von Fenstern erscheinen. Es gibt daneben in Java 1.1 auch noch Popup-Menüs, die mitten auf einer Seite erscheinen können.

Ein Menü besteht aus einem MenuBar, der verschiedene Menu-Objekte enthält. Jedes ist ein Klappmenü, das wiederum MenuItem-Objekte enthält, die der Benutzer letztendlich wählen kann. Das Menü wird im Konstruktor des Frames aufgebaut. Das entsprechende Code-Segment sieht so aus:

// Erzeuge Menueleiste
MenuBar menu=new MenuBar()
setMenuBar(menu);
// Ezeuge ein Menue
Menu files=new Menu("Files");
menu.add(files);
// Erzeuge darin einen Menue-Eintrag
MenuItem ItemClose=new MenuItem("Close");
ItemClose.setActionListener(this);
files.add(ItemClose);

Wie man sieht, ist der Prozess dreistufig: Menüleiste, Menü und Menüeintrag. Man kann allerdings auch Menüs in Menüs schachteln. Dies erzeugt Aufklapp-Menüs, wie man sie von graphischen Benutzoberflächen her kennt.

Es gibt neben normalen MenuItem auch CheckboxItem, die ein Häkchen erhalten, wenn sie angewählt sind.

MenuItem-Objekte erzeugen Action-Events wie Button-Objekte. CheckboxItem-Objekte erzeugen dagegen Item-Events, die von einem ItemListener mit der Methode itemStateChanged abgehört werden können.

Popup-Menüs werden gewöhnlich durch einen rechten Mausklick auf das Fenster ausgelöst. Das folgende Programm demonstriert die Erzeugung eines Menüs und eines Popup-Menüs. Die Ereignisse des verwendeten CheckboxMenuItem "Ignore" werden hier nicht abgefangen. Stattdessen wird im Bedarfsfall dessen Status abgefragt.

import java.awt.*;
import java.awt.event.*;

class TestFrame extends Frame
    implements ActionListener
{   CheckboxMenuItem Ignore;
    PopupMenu PM;
    
    public TestFrame ()
    {   // build up the menu bar
        MenuBar m=new MenuBar();        
        setMenuBar(m);
        
        Menu test=new Menu("Test"); // first menu
        m.add(test);
        
        MenuItem item=new MenuItem("Close"); // an item
        item.addActionListener(this);
        test.add(item);
        
        test.add(Ignore=new CheckboxMenuItem("Ignore"));
            // a checkbox item
        Ignore.setState(true);

        // popup menu:
        PM=new PopupMenu();
        MenuItem mi=new MenuItem("Close");
        mi.addActionListener(this);
        PM.add(mi);
        add(PM);
        
        addMouseListener( // for the popup menu
            new MouseAdapter ()
            {   public void mousePressed (MouseEvent e)
                {   if (e.isPopupTrigger() || e.isMetaDown())
                        // right mouse click
                    {   PM.show(e.getComponent(),
                            e.getX(),e.getY());
                    }
                }
            }
        );
        
                
        setSize(400,400);
        show();
    }
    
    public void actionPerformed (ActionEvent e)
    {   if (e.getActionCommand().equals("Close")
            && !Ignore.getState())
        {   dispose(); System.exit(0);
        }
    }
}

public class Test
{   public static void main (String args[])
    {   new TestFrame();
    }
}

Das Popup-Menu wird über einen MouseAdapter aktiviert. Dazu wird auf Druck der rechten Maustaste reagiert. Da es auf manchen Systemen nur eine Maustaste gibt, ist dies in Java dadurch simuliert, dass es wie der Druck der Meta-Taste gleichzeitig mit der Maustaste weitergegeben wird. Daneben gibt es noch eine Abfrage darauf, ob ein Popup-Trigger ausgelöst wurde, die wir ebenfalls verwenden, obwohl sie unter Windows nicht aktiv ist.

Das Popup-Menü erzeugt nach seiner Anzeige Events, wenn der Benuter einen Menüpunkt auswählt. Es informiert dann die registrierten Listener über diese Events, genau wie das normale Menü.

Dialoge

Dialoge sind kleine Fenster, die zu einen Frame gehören, und für Benutzereingaben eingeblendet werden. Beispiele sind Ja/Nein-Abfragen, Suchen und Ersetzen oder auch der Dialog zur Dateiauswahl.

Dialoge können modal oder nicht-modal sein.

Die Erzeugung eines Dialogs ist ähnlich wie die Erzeugung eines Fensters. Man erzeugt einer Instanz der Dialog-Klasse mit new TestDialog(), wobei TestDialog ein Kind von Dialog ist, das im Konstruktor den Dialog aufbaut. Dann muss der Dialog noch mit show (oder besser setVisible(true)). angezeigt werden. Bei nicht-modalen Dialogen läuft das Programm danach normal weiter.

Als Beispiel erzeugen wir einen Dialog, der abfragt, ob das Fenster wirklich geschlossen werden soll. Wir machen den Dialog natürlich modal.

class CloseQuestion extends Dialog
    implements ActionListener
{   private boolean Yes=false;
    public CloseQuestion (Frame f)  
    {   super(f,"Close",true); 
            // call constructor of dialog with modal-flag true
        // Label for the question:
        add("North",new Label("Really close?"));
        // Panel for the yes/no buttons
        Panel p=new Panel();
        Button b;
        p.add(b=new Button("Yes")); b.addActionListener(this);
        p.add(b=new Button("No")); b.addActionListener(this);
        add("South",p);
        // pack set it 100x100 off the frame and show it 
        // (blocking the frame)
        pack();
        setLocation(f.getLocation().x+100,f.getLocation().y+100);
        show();
    }
    public void actionPerformed (ActionEvent e)
    {   if (e.getActionCommand().equals("Yes")) 
            // pressed the yes button
        {   Yes=true;
        }
        dispose(); // frame continues its job
    }
    public boolean isYes () // to ask for the result
    {   return Yes;
    }
}

Interessant ist, wie die Größe eines Dialogs eingestellt werden kann. Die Methode pack stellt hier eine Minimalgröße ein, die genügt, um alle Dialogelement anzuzeigen. Um dabei Textfeldern eine genügende Größe zu geben gibt es Konstruktoren von TextField und TextArea, die eine gewisse Spalten- und Zeilenzahl fordern. Bei anderen Elemenenten (z.B. Canvas) kann man getPreferredSize überschreiben, um die gewünschte Größe sicherzustellen.

Beispiel

Als komplexeres Beispiel für einen nicht-modalen Dialog erzeugen wir einen Dialog, der die Auswahl der aktiven Farbe erlaubt. Das Beispiel zeigt außerdem die Behandlung von Scroll-Balken.

Weiterhin zeigt das Programm, wie Objekte miteinander kommunizieren können. Der EventDialog benachrichtigt nämlich einen ColorEditListener über die gerade gewählte Farbe und darüber, wann der Dialog beendet wurde. In unserem Fall fungiert das Fenster selbst als ColorEditListener. Wir übergeben den Listener der Einfachheit halber im Konstruktor des Dialogs. Man sollte wohl eigentlich eine addColorEditListener-Methode schreiben und mehrere Listener vorsehen. Dies bleibt einer Übungsaufgabe vorbehalten.

Da dieses Beispiel recht komplex ist, beschreiben noch einmal die wichtigsten Komponenten

import java.awt.*;
import java.awt.event.*;

interface ColorEditListener 
    // Interface, das vom Dialog benachrichtigt wird
{   public void setColor (Color c);
    public void close ();
}

class ColorScrollbar extends Panel 
    // ein einzelner Scrollbar (fuer jede Farbe einen)
    implements AdjustmentListener // fuer Aenderungen im Scrollbar
{   public int Value;
    ColorEdit CE;
    Scrollbar SB;

    public ColorScrollbar (ColorEdit ce, String s, int value)
    {   CE=ce;
        Value=value;
        setLayout(new GridLayout(1,0));
        add(new Label(s)); // links das Label
        add(SB=new Scrollbar(Scrollbar.HORIZONTAL,value,40,0,255));
            // daneben der Scrollbar, 
            // horizontal mit Werten von 0 bis 255,
            // 40 ist die Breite des Schiebers.
        SB.addAdjustmentListener(this);
    }

    public void adjustmentValueChanged (AdjustmentEvent e)
    {   Value=SB.getValue(); // lies neuen Wert
        SB.setValue(Value); // Scrollbar dort fixieren
        CE.setcolor();
    }

    public Dimension getPreferredSize () 
        // Damit der Scrollbar nicht zu knapp ist
    {   return new Dimension(200,20);
    }

    public int value () { return Value; }
}

class ColorEdit extends Dialog // der Farb-Editor
    implements ActionListener
{   ColorScrollbar Red, Green, Blue;
    Label RedLabel,GreenLabel,BlueLabel;
    Color C,Cold;
    Panel CP;
    String Name;
    ColorEditListener CL;

    public ColorEdit (Frame F, ColorEditListener cl, Color c)
    {   super(F,"Edit Color",false);
        CL=cl;
        Cold=C=c;

        // Panel fuer die Scrollbars:
        Panel p=new Panel();
        p.setLayout(new GridLayout(0,1));
        p.add(Red=new ColorScrollbar(this,"Red",C.getRed()));
        p.add(Green=new ColorScrollbar(this,"Green",C.getGreen()));
        p.add(Blue=new ColorScrollbar(this,"Blue",C.getBlue()));
        add("Center",p);

        // Panel fuer die Buttons:
        Panel pb=new Panel();
        Button b;
        pb.add(b=new Button("OK")); b.addActionListener(this);
        pb.add(b=new Button("Cancel")); b.addActionListener(this);
        add("South",pb);

        // Panel fuer das Farb-Display
        CP=new Panel();
        CP.add(new Label("Your Color"));
        CP.setBackground(C);
        add("North",CP);

        pack();
        show();
    }

    public void actionPerformed (ActionEvent e)
    {   if (e.getActionCommand().equals("Cancel"))
        {   C=Cold; tell(); // alte Farbe zuruecksetzen
        }
        CL.close(); dispose();
    }

   public void setcolor () // benachrichtigt den Listener
    {   C=new Color(Red.value(),Green.value(),Blue.value());
        CP.setBackground(C);
        CP.repaint(); // Zeichnet das Farb-Display neu
        tell();
    }

    void tell () // Benachrichtgung an Listener
    {   CL.setColor(C);
    }
}

class TestFrame extends Frame
    implements ColorEditListener
{   ColorEdit CE=null;
    Color C;

    public TestFrame ()
    {   addWindowListener(
            new WindowAdapter ()
            {   public void windowClosing (WindowEvent e)
                {   dispose(); System.exit(0);
                }
            }
        );
        addMouseListener(
            new MouseAdapter ()
            {   public void mousePressed (MouseEvent e)
                {   coloredit();
                }
            }
        );
        
        C=Color.gray; 
            // sowas wie getBackground geht hier noch nicht!
        setSize(400,400);
        show();
    }

    public void paint (Graphics g)
    {   g.setColor(C);
        g.fillRect(0,0,getSize().width-1,getSize().height-1);
    }

    public void update (Graphics g)
    {   paint(g);
    }

    public void coloredit () 
        // nur wenn noch kein Dialog offen ist!
    {   if (CE==null) CE=new ColorEdit(this,this,C);
    }

    // Methoden von ColorEditListener

    public void setColor (Color c)
    {   C=c; repaint();
    }

    public void close ()
    {   CE=null;
    }
}

public class Test
{   public static void main (String args[])
    {   new TestFrame();
    }
}

Übungsaufgaben

  1. Schreiben Sie ein Programm, mit dem der Benutzer in einem Fenster malen kann. Das Fenster soll eine Auswahlbox einblenden, in der die aktuelle Zeichenfarbe unter einigen Vorgaben gewählt werden kann. Die Grafik braucht nicht gepuffert zu werden.
  2. Schreiben Sie ein Testprogramm zu CloseDialog.
  3. Verändern Sie ColorEdit so, dass es beliebig viele ColorDialogListener-Listener geben kann. Trennen Sie außerdem die close-Methode des Interfaces von der setColor-Methode.

Lösungen.

Aufgaben ohne Lösung

  1. Schreiben Sie ein Fenster mit einem vertikalen Schieberegler, der beim Schieben von links nach rechts alle möglichen Kombinationen von rot und grün durchläuft und diese Kombinationen darüber in einem Fenster darstellt.
  2. Erweitern Sie 1. um Menüpunkte, die den Benutzer zwischen verschiedenen Farbkombinationen (rot-grün, grün-blau, rot-blau) wählen lassen.

Zurück zum Java-Kurs