Java Swing

Eine API und Grafikbibliothek zum Programmieren von grafischen Benutzeroberflächen in Java.

Links

Architektur

Nach dem Model-View-Controller Prinzip werden eigentlich Model, View und Controller getrennt. Swing Anwendungen fassen dagegen View und Controller zusammen. Das wird auch Model View Presenter genannt.

Beispiel

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

/**
 * @see <a href="http://java.sun.com/docs/books/tutorial/uiswing/learn/example2.html">Swing Tutorial</a>
 */

public class MySwingApplication implements ActionListener
{
    private static String labelPrefix = "Number of button clicks: ";
    private int numClicks = 0;
    final JLabel label = new JLabel(labelPrefix + "0    ");

    public Component createComponents()
    {
        JButton button = new JButton("Hit me!");
        button.setMnemonic(KeyEvent.VK_I);
        button.addActionListener(this);
        label.setLabelFor(button);

        JPanel pane = new JPanel(new GridLayout(0, 1));
        pane.add(button);
        pane.add(label);
        pane.setBorder(BorderFactory.createEmptyBorder(
                                        30, //top
                                        30, //left
                                        10, //bottom
                                        30) //right
                                        );

        return pane;
    }

    public void actionPerformed(ActionEvent e) {
        numClicks++;
        label.setText(labelPrefix + numClicks);
    }

    private static void initLookAndFeel()
    {
        String lookAndFeel = null;

        lookAndFeel = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
        try
        {
                UIManager.setLookAndFeel(lookAndFeel);
        }
        catch (Exception e)
        {
                System.err.println("Couldn't get specified look and feel");
        }
    }

    private static void createAndShowGUI() {
        initLookAndFeel();
        JFrame.setDefaultLookAndFeelDecorated(true);
        JFrame frame = new JFrame("My Swing Application");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        MySwingApplication app = new MySwingApplication();
        Component contents = app.createComponents();
        frame.getContentPane().add(contents, BorderLayout.CENTER);

        frame.pack();
        frame.setVisible(true);

        // let the window appear at the center of the screen
        setLocationRelativeTo(null);

    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

GUI Elemente

Combo Boxes

Oracle Combo Boxes

Eine ComboBox erzeugen und mit Werten füllen

String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" };
JComboBox aComboBox = new JComboBox(petStrings);

Wenn man mehr Kontrolle über die Box haben möchte:

/** This action class is used to deal with every interaction for our drop down box */
private class MyAction implements ActionListener
{
    @Override
    public void actionPerformed(ActionEvent e)
    {
        JComboBox lJComboWhichFiredAction=(JComboBox) e.getSource();
        Object    lSelectedItem=lJComboWhichFiredAction.getSelectedItem();
        System.out.println("Selected item: "+lSelectedItem);
    }
}

/** This renderer is used to display each entry in our drop down box */
private class MyRenderer implements ListCellRenderer
{
    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
    {
        String lValue=(value==null) ? "" : (String) value;

        // show the selected item in UPPER case
        if(isSelected)
        {
            lValue=lValue.toUpperCase();
        }

        return new JLabel(lValue);
    }
}

/**
 * This class contains a container for the data our drop down box should display.
 * By extending AbstractListModel we do not need to take care of firing list data events
 */

private class MyDataModel extends AbstractListModel implements ComboBoxModel
{
    private static final long serialVersionUID = 1L;

    private List<String> myData;

    private String selectedItem=null;

    /**
     * Constructor
     * @param pElements Array which is used to fill this data model
     */

    public MyDataModel(String [] pElements)
    {
        myData=new ArrayList<String>();
        if(pElements!=null) for(String s : pElements)
        {
            myData.add(s);
        }
    }

    @Override
    public int getSize()
    {
        return myData.size();
    }

    @Override
    public Object getElementAt(int index)
    {
        return myData.get(index);
    }

    @Override
    public void setSelectedItem(Object anItem)
    {
        selectedItem=null;

        if( (anItem instanceof String) &amp;&amp; myData.contains((String) anItem) )
        {
            selectedItem=(String) anItem;
        }
    }

    @Override
    public Object getSelectedItem()
    {
        return selectedItem;
    }

}

Und dann

String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" };
MyDataModel dm=new MyDataModel(petStrings);
JComboBox aComboBox = new JComboBox(dm);

aComboBox.setRenderer(new MyRenderer());            
aComboBox.addActionListener(new MyAction());
aComboBox.setSelectedIndex(2);

Swing Layout Manager

Die Platzierung von GUI Elementen via Spring kann man mithilfe verschiedener Layout Manager erreichen.

Kein Layout Manager

Without a Layout Manager (Absolute Positioning)

Für jede Komponente muss über setBounds genau angegeben werden, wo der Knopf hin soll und wie breit er sein soll.

public class Example extends JFrame
{
 ...
 setLayout(null);
 JButton demo = new JButton("OK");
 demo.setBounds(posX, posY, sizeX, sizeY);
 add(demo);
 pack();
 ...
}

FlowLayout Manager

FlowLayout Manager

Die Komponenten werden reihenweise hinzugefügt, sobald die Reihe voll ist, wird die nächste Reihe angefangen.

public class Example extends JFrame
{
 ...
 LayoutManager layManager=new FlowLayout(FlowLayout.LEFT);
 setLayout(layManager);
 JButton demo = new JButton("OK");
 add(demo);
 ...
 pack();
 ...
}

GridLayout Manager

GridLayout Manager

Die Komponenten werden automatisch nacheinander in einem x/y Raster abgelegt. Auf die Platzierung der Objekte kann kein direkter Einfluss genommen werden. Ein Vorteil ist, dass der Inhalt (z.B. Buttons) im Grid gleich groß ist.

public class Example extends JFrame
{
 ...
 LayoutManager layManager=new GridLayout(5, 4);
 setLayout(layManager);
 JButton demo = new JButton("OK");
 add(demo);
 ...
 pack();
 ...
}

GridBagLayout Manager

GridBagLayout Manager

Wie der GridLayout Manager, man kann aber über sogenannte GridBagConstraints direkten Einfluss darauf nehmen, wo die Komponenten platziert werden.

public class Example extends JFrame
{
 ...
 LayoutManager layManager=new GridBagLayout();
 setLayout(layManager);
 JButton demo = new JButton("OK");
 GridBagConstraints c = new GridBagConstraints();
 c.gridx=1;
 c.gridy=3;
 add(demo, c);
 ...
 pack();
 ...
}

BorderLayout Manager

BorderLayout Manager

Trennt in 5 Bereiche ab. Zwei sehr breite oben und unten (NORTH, SOUTH), einen großen Bereich in der Mitte (CENTER) und je nach eines links und rechts von der Mitte (WEST, EAST). Sehr praktisch, wenn man z.B. eine Toolbar oder eine Statusleiste platzieren möchte.

public class Example extends JFrame
{
 ...
 LayoutManager layManager=new BorderLayout();
 setLayout(layManager);
 JToolBar toolbar = new JToolBar();
 ...
 add(toolbar, BorderLayout.NORTH);
 ...
}

BoxLayout Manager

BoxLayout Manager

Ernöglicht es, Komponenten in eine Reihe oder einer Spalte anzuordnen. Die Komponenten werden dabei nicht größer dargestellt als unbedingt notwendig.

public class Example extends JFrame
{
 ...
 JPanel panel=new JPanel();
 LayoutManager layManager=new BoxLayout(panel, BoxLayout.Y_AXIS);
 panel.setLayout(layManager);
 JButton demo = new JButton("OK");
 panel.add(demo);
 ...
 this.add(panel);
 ...
 pack();
 ...
}

CardLayout Manager

CardLayout Manager

Beinhaltet mehrere alternative Container, die jeweils über einen String identifiziert werden und zwischen denen dann über diesen String hin und her geschaltet werden kann.

public class Example extends JFrame
{
 ...
 JPanel panelCards;
 ...
 panelCards=new JPanel();
 LayoutManager layManager=new CardLayout();
 panelCards.setLayout(layManager);

 JPanel panel1=new JPanel();
 panel1.add(new JButton("Demo1_1"));

 JPanel panel2=new JPanel();
 panel2.add(new JButton("Demo2_1"));
 panel2.add(new JButton("Demo2_2"));

 JPanel panel3=new JPanel();
 panel3.add(new JButton("Demo3_1"));

 panelCards.add(panel1, "CARD1");
 panelCards.add(panel2, "CARD2");
 panelCards.add(panel3, "CARD3");

 add(panelCards);
 ...
 pack();
 ...

 public void switchToCard2()
 {
  ((CardLayout) panelCards.getLayout()).show(panelCards, "CARD2");
 }

 ...

}

Tabbed Panes

Tabbed Pane Tabbed Panes gehören zwar eigentlich nicht zu den Layout Managern, haben aber eine ähnliche Funktionalität wie der CardLayout Manager. Sie nehmen alternative Container auf, zwischen denen der User dann über Tabs hin und her schalten kann.

public class Example extends JFrame
{
 ...
 JTabbedPane tabbedPane = new JTabbedPane();

 JPanel panel1=new JPanel();
 panel1.add(new JButton("Demo1_1"));

 JPanel panel2=new JPanel();
 panel2.add(new JButton("Demo2_1"));
 panel2.add(new JButton("Demo2_2"));

 JPanel panel3=new JPanel();
 panel3.add(new JButton("Demo3_1"));

 tabbedPane.addTab("First tab",  panel1);
 tabbedPane.addTab("Second tab", panel2);
 tabbedPane.addTab("Third  tab", panel3);

 ...
 add(tabbedPane);
 ...
 pack();
 ...
}

GroupLayout Manager

GroupLayout Manager Für diesen Layout Manager, betrachtet man alle Komponenten von links nach rechts und einmal alle Komponenten von oben nach unten. Für jede Komponente und beide Betrachtungsweise gibt man dann an, welche Komponenten nebeneinander und welche in Reihe zueinander stehen.

public class Example extends JFrame
{
 ...
 JPanel panel=new JPanel();
 GroupLayout layout=new GroupLayout(panel);  
 panel.setLayout(layout);

 layout.setAutoCreateGaps(true);
 layout.setAutoCreateContainerGaps(true);

 JButton       ok=new JButton("OK");
 JButton       ca=new JButton("Cancel");
 JLabel     lName=new JLabel("Name:");
 JTextField fName=new JTextField("Your name? ....");
 JLabel      lAge=new JLabel("Age:");
 JTextField  fAge=new JTextField("Your age? ....");

 /* Layout:
  *
  *    Name: ______  OK
  *    Age:  ______  Cancel
  */


 // horizontal
 {
        GroupLayout.ParallelGroup columnLeft   = layout.createParallelGroup().  addComponent(lName). addComponent(lAge);
        GroupLayout.ParallelGroup columnMiddle = layout.createParallelGroup().  addComponent(fName). addComponent(fAge);
        GroupLayout.ParallelGroup columnRight  = layout.createParallelGroup().  addComponent(ok).    addComponent(ca);
        GroupLayout.SequentialGroup leftToRight= layout.createSequentialGroup().addGroup(columnLeft).addGroup(columnMiddle).addGroup(columnRight);
        layout.setHorizontalGroup(leftToRight);
 }

 // vertical
 {
        GroupLayout.ParallelGroup   rowTop      = layout.createParallelGroup().  addComponent(lName).addComponent(fName).addComponent(ok);
        GroupLayout.ParallelGroup   rowMiddle   = layout.createParallelGroup().  addComponent(lAge). addComponent(fAge). addComponent(ca);
        GroupLayout.SequentialGroup topToBottom = layout.createSequentialGroup().addGroup(rowTop).   addGroup(rowMiddle);
        layout.setVerticalGroup(topToBottom);
 }
 add(panel);
 pack();
 ...
}
Swing GroupLayout

Was in diesem Beispiel erreicht wird:

  • Horizontal
  • die beiden Labels sind parallel zueinander
  • die beiden Eingabefelder sind parallel zueinander
  • die beiden Buttons sind parallel zueinander
  • alle drei sind in Reihe zueinander
  • Vertikal
  • Name Label, Eingabefeld und OK Button sind parallel zueinander
  • Alter Label, Eingabefeld und Cancel Button sind parallel zueinander
  • beide sind in Reihe zueinander

Wenn man nicht aufpasst, kann man versehentlich zu schwache Einschränkungen definieren. Ändern man z.B. im Beispiel den horizontalen Abschnitt so ab:

 // horizontal
 {
        SequentialGroup hs1=layout.createSequentialGroup().addComponent(lName).addComponent(fName).addComponent(ok);
        SequentialGroup hs2=layout.createSequentialGroup().addComponent(lAge). addComponent(fAge). addComponent(ca);
        ParallelGroup   hp1=layout.createParallelGroup(GroupLayout.Alignment.CENTER).addGroup(hs1).addGroup(hs2);
        layout.setHorizontalGroup(hp1);
 }

erreicht man für den horizontalen und den vertikalen Teil die gleichen Einschränkungen:

  • Name Label, Eingabefeld und OK Button sind parallel zueinander
  • Alter Label, Eingabefeld und Cancel Button sind parallel zueinander
  • beide sind in Reihe zueinander

Keiner von beiden erzwingt, dass auch z.B. die beiden Eingabefelder zueinander parallel sind.

Swing Group Layout broken

Das ist vermutlich nicht gewünscht.

SpringLayout Manager

SpringLayout Manager (Für GUI Builder gedacht) Die Komponenten werden relativ zum Rand benachbarter Komponenten angegeben.

public class Example extends JFrame
{
 ...
 JPanel panel=new JPanel();
 SpringLayout layout=new SpringLayout();  
 panel.setLayout(layout);

 JButton       ok=new JButton("OK");
 JButton       ca=new JButton("Cancel");
 JLabel     lName=new JLabel("Name:");
 JTextField fName=new JTextField("Your name? ....");

 /* Layout:
  *
  *    Name: ______  OK
  *                  Cancel
  */

 panel.add(lName);
 panel.add(fName);
 panel.add(ok);
 panel.add(ca);

 // the left edge of lName starts 5 pixel right of the left edge of the surrounding panel
 layout.putConstraint(SpringLayout.WEST, lName,   5, SpringLayout.WEST,  panel);
 // the top edge of the lName starts 20 pixel beneath the top edge of the surrounding panel
 layout.putConstraint(SpringLayout.NORTH,lName,  20, SpringLayout.NORTH, panel);

 // the left edge of fName starts 5 pixel right of the right edge of it neighbour the lName
 layout.putConstraint(SpringLayout.WEST,  fName,  5, SpringLayout.EAST,  lName);
 layout.putConstraint(SpringLayout.NORTH, fName, 20, SpringLayout.NORTH, panel);

 layout.putConstraint(SpringLayout.WEST,  ok,     5, SpringLayout.EAST,  fName);
 layout.putConstraint(SpringLayout.NORTH, ok,    20, SpringLayout.NORTH, panel);

 // the left edge of the ca starts exactly where the left edge of ok does
 layout.putConstraint(SpringLayout.WEST,  ca,     0, SpringLayout.WEST,  ok);
 // the top edge of the ca is 5 pixels beneath the ok's bottom edge
 layout.putConstraint(SpringLayout.NORTH, ca,     5, SpringLayout.SOUTH, ok);

 // now the panel itself needs to be stretched:

 // the right edge of the panel should be 5 pixels right of the end of the right edge of the ca
 layout.putConstraint(SpringLayout.EAST,  panel,  5, SpringLayout.EAST,  ca);
 // the bottom edge of the panel should be 5 pixels beneath the bottom of the ca
 layout.putConstraint(SpringLayout.SOUTH, panel, 20, SpringLayout.SOUTH, ca);

 add(panel);
 pack();
 ...
}
Swing SpringLayout

Was läuft in Swing in welchem Thread

Concurrency in Swing

Es gibt 3 Arten von Threads die man für die Swing Programmierung beachten muss:

Initial threads

Der oder die Threads, die das Top GUI Objekt erzeugen und seine Ausführung (in einem neuen Thread) veranlassen. Das ist im einfachsten Fall die Klassen mit der main Methode, die irgendwo ein SwingUtilities.invokeLater mit einem GUI Objekt aufruft und damit zu Ende ist.

Event Dispatch Thread

Die Swing Klassen sind per se nicht thread sicher. Alle Interaktionen mit den Swing Komponenten (z.B. Klick auf einen Button) laufen daher in einem einzigen Thread ab, dem Event Dispatcher Thread. Daher sollte Code, der durch ein Event ausgelöst wird, nicht lange laufen, sonst wird die GUI träge bzw. scheint gar nicht mehr zu reagieren, da solange keine andere Interaktion (z.B. ein anderer Knopf wird gedrückt) möglich ist. Wenn ein Event eine längere Verarbeitung notwendig macht, sollte man einen Worker Thread starten. Wenn man an der GUI Veränderungen durchführen möchte, sollte man das immer aus dem Event Dispatcher Thread heraus durchführen, weil die Änderungen dann sofort sichtbar werden.

Worker Threads

Hier sollten länger laufenden Arbeitsschritte ablaufen. Seit Java 1.6 gibt es die elegante Möglichkeit lang laufenden Threads aus dem Even Dispatcher Thread über SwingWorker zu starten.

Beispiel:

Eine Klasse mit 3 Buttons und einer ProgressBar:

Swing long running tasks
public class MyGUIWithLongRunningTask extends JFrame
{
       private static final Integer NR_OF_ENTRIES=30000;

       protected JButton      buttonShuffle;
       protected JButton      buttonSort;
       protected JButton      buttonQuit;
       protected JProgressBar progressBar;

       List<Integer> unsortedListData;
       List<Integer> sortedListData;

       private void initGUIElements()
       {
              // Button, action behind the button will block GUI
              buttonShuffle=new JButton("Shuffle");
              buttonShuffle.addActionListener(new ShuffleIt(NR_OF_ENTRIES));

              // Button, action behind the button will not block GUI
              buttonSort=new JButton("Sort");
              buttonSort.setVisible(false);
              buttonSort.addActionListener(new SortIt());

              // Button, immediate action
              buttonQuit=new JButton("Quit");
              buttonQuit.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent event) {
                System.exit(0);
            }});

              // progress bar
              progressBar=new JProgressBar();
              progressBar.setVisible(false);
       }
...

Durch einen Klick auf den Shuffle Button wird ein Event ausgelöst und die Methode actionPerformed in der folgenden Klasse ausgeführt. Der gesamte Code läuft wie der Rest der GUI im Event Dispatch Thread, die GUI "hängt" also, bis der Code abgearbeitet ist:

       /**
        * Inner class to created a random unsorted List of Integers.
        * This class is added as an ActionListener to the Shuffle button
        */

       private class ShuffleIt implements ActionListener
       {
              private int mMaxNrOfEntries;

              public ShuffleIt(int pMaxNrOfEntries)
              {
                     this.mMaxNrOfEntries=pMaxNrOfEntries;
              }

              @Override
              public void actionPerformed(ActionEvent e)
              {
                     List<Integer> data=new ArrayList<Integer>();
                     Random rand=new Random();
                     for(int i=0; i<mMaxNrOfEntries; i++)
                     {
                            int r=rand.nextInt();
                            data.add(r);
                     }
                     unsortedListData=data;

                     // now offer a sort button
                     buttonSort.setVisible(true);
                     pack();
              }
       };
 

Durch einen Klick auf den Sort Knopf wird ebenfalls ein Event ausgelöst und ebenfalls eine actionPerformed Methode ausgeführt. Dort wird allerdings eine SwingWorker Klasse gestartet, die im Hintergrund in einem Worker Thread abläuft.

 private class SortIt implements ActionListener
       {
        @Override
        public void actionPerformed(ActionEvent e)
        {
                if(unsortedListData!=null)
                {
                        SortItWorker s=new SortItWorker(new ArrayList<Integer>(unsortedListData));
                        progressBar.setValue(0);
                        progressBar.setVisible(true);
                        pack();
                        s.execute();
                }                      
        }
       }

Durch den Aufruf von execute() wird die Methode doInBackground() in einem Worker Thread gestartet. Die GUI bleibt für den Benutzer weiterhin benutzbar. Sobald doInBackground() fertig ist, wird die Methode done() aufgefrufen, und zwar wieder im Event Dispatch Thread. Aber auch schon während der Abarbeitung kann mit dem parallel laufenden Worker Thread kommuniziert werden:

  • Mit setProgress() kann der Worker Thread seinen Fortschritt setzen. Über die propertyChange() Methode wird der Event Dispatch Thread bei jeder Änderung des Fortschritts aufgerufen und kann den Fortschritt auslesen und z.B. einen Fortschrittsbalken anzeigen.
  • Über publish() kann der Worker Thread Zwischenergebnisse veröffentlichen, die alle gesammelt und gespeichert werden. Daraufhin wird die Methode process() innerhalb des Event Dispatch Threads aufgerufen. Dort kann dann z.B. das aktuellste Zwischenergebniss dargestellt werden.
  • Solange der SwingWorker ab und zu isCancelled() ausliest, und abbricht sobald das true ist, kann der Event Dispatcher Thread über cancel() den Prozess abbrechen.
  • Der Event Dispatcher Thread kann auch jederzeit mit get() auf das Ergebnis warten. Dann blockiert die GUI aber wieder, bis das Ergebnis geliefert wird. Alternativ kann mit get(5, TimeUnit.SECONDS); auch eine bestimmte Zeit gewartet werden, danach läuft der Event Dispatcher Thread dann wieder parallel zum Worker Thread.

Achtung: Jedes Objekt der SwingWorker Klasse kann nur einmal ablaufen, danach muss das Objekt verworfen werden und ein neues erzeugt werden.

       /**
        * This is another inner class which will sort an unsorted List.
        * This class runs in the background and does not block the GUI
        *
        */

       private class SortItWorker extends SwingWorker<List<Integer>, List<Integer> > implements PropertyChangeListener
       {
              private List<Integer> toBeSorted;

              public SortItWorker()
              {
                     super();
                     addPropertyChangeListener(this);
              }

              @Override
              protected List<Integer> doInBackground() throws Exception
              {
                     this.setProgress(0);

                     // simple bubble sort
                     for(int i=0; i<toBeSorted.size(); i++)
                     {
                            for(int j=i+1; j<toBeSorted.size(); j++)
                            {
                                   if(toBeSorted.get(i)>toBeSorted.get(j))
                                   {
                                          Integer tmp=toBeSorted.get(j);
                                          toBeSorted.set(j, toBeSorted.get(i));
                                          toBeSorted.set(i, tmp);
                                   }
                            }

                            // set the current progress in percent
                            int lCurrentProgress=Math.min(100, ( (int) ( i * 100 / (toBeSorted.size()-1)) ));
                            this.setProgress(lCurrentProgress);

                            // publish what is already finished so the GUI can already display something
                            List<Integer> alreadySortedSubList=toBeSorted.subList(0, i);
                            publish(alreadySortedSubList);

                            // allow others to cancel us
                            if(isCancelled())
                            {
                                   break;
                            }
                     }
                     return toBeSorted;
              }

              /**
               * This method is called within the Event Dispatch Thread when this SwingWorker is finished
               * @see javax.swing.SwingWorker#done()
               */

              @Override
              public void done()
              {
                     progressBar.setVisible(false);
                     pack();
                     try
                     {
                            sortedListData=get();
                     } catch (Exception e)
                     {
                            e.printStackTrace();
                            sortedListData=null;
                     }

              }

              /**
               * This class is called within the Event Dispatch Thread when a new result was published
               * @see javax.swing.SwingWorker#process(java.util.List)
               */

              @Override
              protected void process(List<List<Integer>> soFarProcessed)
              {
                     if(soFarProcessed!=null &amp;&amp; soFarProcessed.size()>0)
                     {
                            sortedListData=soFarProcessed.get(soFarProcessed.size()-1);
                     }
              }              

              @Override
              public void propertyChange(PropertyChangeEvent evt)
              {
                     if ("progress" == evt.getPropertyName())
                     {
                            int lNewProgress = (Integer) evt.getNewValue();
                            progressBar.setValue(lNewProgress);
                     }
              }

       }

...

Events

Welche Taste wurde gedrückt

So kann man z.B. abfragen, ob Return gedrückt wurde:

public class Foo extends JDialog implements KeyListener
{
    ...
    JButton bntOK=new JButton("Foo");
    bntOK.addKeyListener(this);
    ...

    @Override
    public void keyPressed(KeyEvent e)
    {
        // return was pressed
        if(e.getKeyCode() == KeyEvent.VK_ENTER)
        {
            ...
        }
    }
}

Wer hat den Event ausgelöst

Wenn man mit einer Klasse die Events von mehreren Swing Komponenten verarbeiten will, kann man über den ActionCommand unterscheiden was gemacht werden muss:

private final static String CMD_OK="Yes!"
private final static String CMD_NO="No :-(";

...

JButton buttonOK = new JButton("OK");
buttonOK.setActionCommand(CMD_OK);
buttonOK.addActionListener(this);

JButton buttonNo = new JButton("Cancel");
buttonNo.setActionCommand(CMD_NO);
buttonNo.addActionListener(this);

...

@Override
public void actionPerformed(ActionEvent e)
{
        if(CMD_OK.equals(e.getActionCommand()))
        {
         ...
        }
        else if(CMD_NO.equals(e.getActionCommand()))
        {
         ...
        }
}

Oder aber man erzeugt für jedes Element eine anonyme innere Klasse, in der die actionPerformed Methode überschrieben wird und je nach Objekt eine andere Methode aufgerufen wird

buttonOK.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        do_foo();
    }
});

buttonNo.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        do_bar();
    }
});

Focus Reihenfolge

Entweder man möchte einfach nur ein bestimmtes GUI Element Initial mit dem Focus versehen:

...
JButton button=new JButton("Hit me!");
...
// request focus between pack and setVisible!
frame.pack();
// button has the focus
button.requestFocusInWindow();
frame.setVisible(true);

Oder bestimmte Elemente sollen nie den Focus erhalten:

button.setFocusable(false);

Man kann aber auch für alle Elemente eine Reihenfolge bestimmen, in der die Elemente den Fokus erhalten.

FocusTraversalPolicy focusPolicy;
...
frame.setFocusTraversalPolicy(focusPolicy);
frame.pack();
frame.setVisible(true);

FocusTraversalPolicy bei Oracle oder eine von mir

package de.tgunkel.Java.Demo.FocusDemo;

import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.util.List;

/**
 * @author Thorsten Gunkel (GUT)
 *
 * A simple implementation of the FocusTraversalPolicy
 */

public class MyOwnFocusTraversalPolicy extends FocusTraversalPolicy
{
    /**  A list where we store the focus order of the Components */
    private List<Component> orderedCompents;

    /**
     * @param pOrderedCompents A list containing all Components which should ever get the focus in the preferred order
     */

    public MyOwnFocusTraversalPolicy(List<Component> pOrderedCompents)
    {
        if(pOrderedCompents==null || pOrderedCompents.size()<1)
            throw new IllegalArgumentException("This is no valid order");
        this.orderedCompents=pOrderedCompents;
    }

    /**
     * @param component
     * @return
     */

    private int getComponentPosition(Component component)
    {
        int result=this.orderedCompents.indexOf(component);
        return result;
    }

    /* (non-Javadoc)
     * @see java.awt.FocusTraversalPolicy#getComponentAfter(java.awt.Container, java.awt.Component)
     */

    @Override
    public Component getComponentAfter(Container container, Component component)
    {
        Component result=null;
        int position=getComponentPosition(component);
        if(position>=0 &amp;&amp; position+1<this.orderedCompents.size())
        {
            result=this.orderedCompents.get(position+1);
        }
        else
        {
            result=getFirstComponent(container);
        }

        return result;
    }

    /* (non-Javadoc)
     * @see java.awt.FocusTraversalPolicy#getComponentBefore(java.awt.Container, java.awt.Component)
     */

    @Override
    public Component getComponentBefore(Container container, Component component)
    {
        Component result=null;
        int position=getComponentPosition(component);
        if(position>=1)
        {
            result=this.orderedCompents.get(position-1);
        }
        else
        {
            result=getLastComponent(container);
        }

        return result;
    }

    /* (non-Javadoc)
     * @see java.awt.FocusTraversalPolicy#getDefaultComponent(java.awt.Container)
     */

    @Override
    public Component getDefaultComponent(Container container)
    {
        Component result=getFirstComponent(container);
        return result;
    }

    /* (non-Javadoc)
     * @see java.awt.FocusTraversalPolicy#getFirstComponent(java.awt.Container)
     */

    @Override
    public Component getFirstComponent(Container container)
    {
        Component result=this.orderedCompents.get(0);
        return result;
    }

    /* (non-Javadoc)
     * @see java.awt.FocusTraversalPolicy#getLastComponent(java.awt.Container)
     */

    @Override
    public Component getLastComponent(Container container)
    {
        Component result=this.orderedCompents.get(this.orderedCompents.size()-1);
        return result;
    }

}

Panel mit Hintergrundbild

So kann man ein Panel mit einem Hintergrundbild versehen

BackgroundImagePanel  backgroundPanel=new
BackgroundImagePanel("MyPicture.png", new Color(0,0,0));
backgroundPanel.setBounds(0, 0, 640, 480);

JTextArea  textA=new JTextArea("Hello World");
textA.setOpaque(false);

panelAll=new JPanel();
// don't use a layout manager, we want to place or elements on an exact
pixel position of the background image
panelAll.setLayout(null);
panelAll.setBackground(backgroundPanel .getBackgroundColor());

panelAll.add(textA);
panelAll.add(backgroundPanel);

Und so kann so ein BackgroundImagePanel aussehen:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JPanel;
/**
 * @author Thorsten Gunkel
 * Class to have a panel which displays a background image
 */

public class BackgroundImagePanel extends JPanel
{
    private static final long serialVersionUID = 1L;

    private Image     backgroundImage;
    private Dimension backgroundImageDimension;
    private Color     backgroundColor;

    /**
     * @param pBackgroundImage
     * @param pBackgroundColor
     */

    public BackgroundImagePanel(Image pBackgroundImage, Color pBackgroundColor)
    {
        init(pBackgroundImage, pBackgroundColor);
    }

    /**
     * @param pFileNameForImageInClasspath
     * @param pBackgroundColor
     * @throws IOException
     */

    public BackgroundImagePanel(String pFileNameForImageInClasspath, Color pBackgroundColor) throws IOException
    {
        try
        {
            URL imageURL=getClass().getClassLoader().getResource(pFileNameForImageInClasspath);
            if(imageURL==null)
            {
                throw new IOException("Did not found the image in the classpath");
            }
            Image lBackgroundImage=ImageIO.read(imageURL);
            init(lBackgroundImage, pBackgroundColor);
        }
        catch(IOException e)
        {
            throw new IOException("Could not load the filename you provided: "+pFileNameForImageInClasspath, e);
        }        
    }

    /**
     * @param pBackgroundImage
     * @param pBackgroundColor
     * @throws IllegalArgumentException
     */

    private void init(Image pBackgroundImage, Color pBackgroundColor) throws IllegalArgumentException
    {
        this.backgroundColor=pBackgroundColor;
        this.setBackground(this.backgroundColor);
        this.backgroundImage=pBackgroundImage;
        if(this.backgroundImage==null)
        {
            throw new IllegalArgumentException("You need to pass BackgroundImagePanel a valid Image!");
        }
        int heigth=this.backgroundImage.getHeight(null);
        int width= this.backgroundImage.getWidth(null);
        this.backgroundImageDimension=new Dimension(width, heigth);
        this.setSize(backgroundImageDimension);
        this.setPreferredSize(backgroundImageDimension);
    }


    /**
     * @return the backgroundImageDimension
     */

    public Dimension getBackgroundImageDimension()
    {
        return backgroundImageDimension;
    }

    /**
     * @return the backgroundColor
     */

    public Color getBackgroundColor()
    {
        return backgroundColor;
    }

    /*
     * Override the paint method of this JComponent to
     * draw an image instead of the component
     *
     * (non-Javadoc)
     * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
     */

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
    }

}