Weitere Grafikelemente

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 werden 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 JLabel. 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 beim 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 JTextField erzeugt diese Methode kein Event, wenn die Enter-Taste gedrückt wird, sondern erzeugt dadurch einen neuen Paragraphen. Dieses Verhalten kann man aber ändern. 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.

In diesem Programm wird die TextArea nicht bearbeitbar gemacht. Alle Eingaben im TextField werden aber dort an den vorhandenen Text angehängt.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

class TestFrame extends JFrame
        implements ActionListener
{
    JTextArea textarea;
    JTextField textfield;

    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:
        textarea = new JTextArea();
        JScrollPane OutP = new JScrollPane(textarea);
        add("Center", OutP);
        OutP.setPreferredSize(new Dimension(400, 400));
        textarea.setEditable(false);

        // TextField:
        add("South", textfield = new JTextField());
        textfield.addActionListener(this);

        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    @Override
    public void actionPerformed (ActionEvent e)
    {
        if (e.getSource() == textfield) // enter in TextField
        {
            textarea.append(textfield.getText() + "\n");
            textfield.setText("");
        }
    }
}

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

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.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;

/**
 * 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));
    }

    @Override
    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();
        setLocationRelativeTo(null);
        setVisible(true);
    }

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

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.

Die Klasse JComboBox ist generisch und man kann den Typ der Elemente spezifizieren. Wir verwenden hier Strings.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Test extends JFrame
        implements ActionListener
{
    JLabel label;
    JComboBox<String> 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<String>(strings);
        box.addActionListener(this);

        setLayout(new BorderLayout());
        add("Center", label);
        add("South", box);

        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    @Override
    public void actionPerformed (ActionEvent e)
    {
        if (e.getSource() == box)
        {
            if (box.getSelectedIndex() >= -1)
            {
                label.setText(box.getSelectedItem().toString());
            }
        }
    }

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

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.

Will man alle Auswahlmöglichkeiten gleichzeitig darstellen, so gibt es JList. Diese neue Komponente erzeugt ein Ereignis vom Typ ListSelectionEvent. 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.BorderLayout;
import java.awt.Dimension;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class Test extends JFrame
        implements ListSelectionListener
{
    JLabel label;
    JList<String> 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<String>(strings);
        list.addListSelectionListener(this);

        setLayout(new BorderLayout());
        add("Center", label);
        add("South", new JScrollPane(list));

        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    @Override
    public void valueChanged (ListSelectionEvent e)
    {
        if (e.getSource() == list)
        {
            if (list.getSelectedIndex() >= -1)
            {
                label.setText(list.getSelectedValue().toString());
            }
        }
    }

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

}

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
JMenuBar menu=new JMenuBar()
setMenuBar(menu);
// Ezeuge ein Menue
JMenu files=new JMenu("Files");
menu.add(files);
// Erzeuge darin einen Menue-Eintrag
JMenuItem ItemClose=new JMenuItem("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 JMenuItem auch JCheckBoxItem, die ein Häkchen erhalten, wenn sie angewählt sind.

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.

Menu 

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

class TestFrame extends JFrame
        implements ActionListener
{
    JMenuItem close;
    JCheckBoxMenuItem ignore;
    JPopupMenu popup;
    JButton popupclose;

    public TestFrame ()
    {
        // build up the menu bar
        JMenuBar m = new JMenuBar();
        setJMenuBar(m);

        JMenu menu = new JMenu("Test"); // first menu
        m.add(menu);

        // a normal menu item
        close = new JMenuItem("Close"); // an item
        close.addActionListener(this);
        menu.add(close);

        // a checkbox item
        menu.add(ignore = new JCheckBoxMenuItem("Ignore Close in Menu"));
        ignore.setState(true);

        // popup menu:
        popup = new JPopupMenu();
        popupclose = new JButton("Close");
        popup.add(popupclose);
        popupclose.addActionListener(this);
        add(popup);

        JPanel panel = new JPanel();
        add("Center", panel);

        panel.addMouseListener( // for the popup menu
                new MouseAdapter()
                {
                    @Override
                    public void mousePressed (MouseEvent e)
                    {
                        if (SwingUtilities.isRightMouseButton(e))
                        // right mouse click
                        {
                            popup.pack();
                            popup.show(e.getComponent(), e.getX(), e.getY());
                        }
                    }
                });

        setSize(600, 600);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    @Override
    public void actionPerformed (ActionEvent e)
    {
        if (e.getSource() == close
                && !ignore.getState())
        {
            dispose();
            System.exit(0);
        }

        else if (e.getSource() == popupclose)
        {
            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. 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. Dann muss der Dialog noch mit 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.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

class CloseQuestion extends JDialog
        implements ActionListener
{
    private boolean Yes = false;

    public CloseQuestion (JFrame f)
    {
        super(f, "Close", true);

        // call constructor of dialog with modal-flag true
        // Label for the question:
        add("North", new JLabel("Really close?"));

        // Panel for the yes/no buttons
        JPanel p = new JPanel();
        JButton b;
        p.add(b = new JButton("Yes"));
        b.addActionListener(this);
        p.add(b = new JButton("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);
        setVisible(true);
    }

    @Override
    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;
    }
}

Es gibt auch vorgefertigte Dialoge zur Eingabe von Text oder zur Abfrage auf Ja oder Nein. Der einfachste zeigt eine Meldung an, die der benutzer bestätigen muss. Er benötigt eine Komponente zumAnzeigen, gewöhnlich ein JFrame.

JOptionPane.showMessageDialog(this, "Error!"); 

Ein anderer Dialog erlaubt einzeilige Eingaben. Falls der String gleich null ist, hat der Benutzer die Eingabe abgebrochen. 

String s = JOptionPane.showInputDialog(this, "Enter Name", ""); 

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.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.Scrollbar;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;

/**
 * Interface, das vom Dialog benachrichtigt wird. Dies ist eine elegante Art,
 * zwischen Komponenten zu kommunizeiren.
 */
interface ColorEditListener
{
    public void setColor (Color c);

    public void close ();
}

/**
 * Horizontaler Scrollbalken für eine Farbe.
 */
class ColorScrollbar extends JPanel
        implements AdjustmentListener
{
    public int value;
    ColorEdit coloreditor;
    Scrollbar scrollbar;

    public ColorScrollbar (ColorEdit ce, String s, int value)
    {
        coloreditor = ce;
        this.value = value;

        // Das einfachste layout mit einer Zeile und beliebig vielen Spalten.
        setLayout(new GridLayout(1, 0));

        // Links das Label
        add(new JLabel(s));

        // Daneben der Scrollbalken mit Breite 40
        add(scrollbar = new Scrollbar(Scrollbar.HORIZONTAL, value, 40, 0, 255));

        // Das Fenster soll die Größe 300x30 haben
        setPreferredSize(new Dimension(300, 30));

        scrollbar.addAdjustmentListener(this);
    }

    /**
     * Ereignismethode für den AdjustmentListener
     */
    @Override
    public void adjustmentValueChanged (AdjustmentEvent e)
    {
        value = scrollbar.getValue(); // lies neuen Wert
        scrollbar.setValue(value); // Scrollbar dort fixieren
        coloreditor.setcolor();// benachrichtige den Color Editor
    }

    public int getValue ()
    {
        return value;
    }
}

/**
 * Dialog zum Ändern der Farbe über die Rot-, Grün-, und Blau-Komponente.
 */
class ColorEdit extends JDialog
        implements ActionListener
{
    ColorScrollbar red, green, blue;
    JLabel redlabel, greenlabel, bluelabel;
    Color color, colorold;
    JPanel colorpanel;
    String name;
    ColorEditListener coloreditlistener;

    public ColorEdit (Frame frame, ColorEditListener cl, Color c)
    {
        super(frame, "Edit Color", false);
        coloreditlistener = cl;
        colorold = color = c;

        // Panel fuer die Scrollbars:
        JPanel center = new JPanel();
        center.setLayout(new GridLayout(0, 1));
        center.add(red = new ColorScrollbar(this, "Red", color.getRed()));
        center.add(green = new ColorScrollbar(this, "Green", color.getGreen()));
        center.add(blue = new ColorScrollbar(this, "Blue", color.getBlue()));
        add("Center", center);

        // Panel fuer die Buttons:
        Panel buttons = new Panel();

        // Buttons
        JButton b;
        buttons.add(b = new JButton("OK"));
        b.addActionListener(this);
        buttons.add(b = new JButton("Cancel"));
        b.addActionListener(this);
        add("South", buttons);

        // Panel fuer das Farb-Display
        colorpanel = new JPanel();
        colorpanel.add(new JLabel("Your Color"));
        colorpanel.setBackground(color);
        add("North", colorpanel);

        pack();
        setVisible(true);
    }

    @Override
    public void actionPerformed (ActionEvent e)
    {
        if (e.getActionCommand().equals("Cancel"))
        {
            color = colorold;
            tell(); // alte Farbe zuruecksetzen
        }
        coloreditlistener.close();
        dispose();
    }

    public void setcolor () // benachrichtigt den Listener
    {
        color = new Color(red.getValue(), green.getValue(), blue.getValue());
        colorpanel.setBackground(color);
        colorpanel.repaint(); // Zeichnet das Farb-Display neu
        tell();
    }

    void tell () // Benachrichtgung an Listener
    {
        coloreditlistener.setColor(color);
    }
}

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

    public TestFrame ()
    {
        // Set an icon image, a 32x32 PNG file in the same folder as this class.
        setIconImage(new javax.swing.ImageIcon(
                getClass().getResource("hearts32.png")).getImage());

        // React to the closing in another way than by setDefaultAction
        addWindowListener(
                new WindowAdapter()
                {
                    @Override
                    public void windowClosing (WindowEvent e)
                    {
                        dispose();
                        System.exit(0);
                    }
                });

        // Add to mouseclicks into the window.
        addMouseListener(
                new MouseAdapter()
                {
                    @Override
                    public void mousePressed (MouseEvent e)
                    {
                        coloredit();
                    }
                });

        // Starting with the default color.
        C = UIManager.getColor("Panel.background");

        setSize(400, 400);
        setLocationRelativeTo(null);
        setVisible(true);
    }

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

    @Override
    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);
        CE.setLocationRelativeTo(this);
    }

    // Methoden von ColorEditListener

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

    @Override
    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.

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