Java Swing
Eine API und Grafikbibliothek zum Programmieren von grafischen Benutzeroberflächen in Java.
Links
- Java ist eine Insel: Swing
- Swing: A Quick Tutorial for AWT Programmers
- Java Swing Tutorial
- The Java Tutorials: Using Swing Components
- Wikipedia: Swing
- The Guidebook: Swing
- Java Tutorials
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 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
Eine ComboBox erzeugen und mit Werten füllen
JComboBox aComboBox = new JComboBox(petStrings);
Wenn man mehr Kontrolle über die Box haben möchte:
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) && myData.contains((String) anItem) )
{
selectedItem=(String) anItem;
}
}
@Override
public Object getSelectedItem()
{
return selectedItem;
}
}
Und dann
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.
{
...
setLayout(null);
JButton demo = new JButton("OK");
demo.setBounds(posX, posY, sizeX, sizeY);
add(demo);
pack();
...
}
FlowLayout Manager
Die Komponenten werden reihenweise hinzugefügt, sobald die Reihe voll ist, wird die nächste Reihe angefangen.
{
...
LayoutManager layManager=new FlowLayout(FlowLayout.LEFT);
setLayout(layManager);
JButton demo = new JButton("OK");
add(demo);
...
pack();
...
}
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.
{
...
LayoutManager layManager=new GridLayout(5, 4);
setLayout(layManager);
JButton demo = new JButton("OK");
add(demo);
...
pack();
...
}
GridBagLayout Manager
Wie der GridLayout Manager, man kann aber über sogenannte GridBagConstraints direkten Einfluss darauf nehmen, wo die Komponenten platziert werden.
{
...
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
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.
{
...
LayoutManager layManager=new BorderLayout();
setLayout(layManager);
JToolBar toolbar = new JToolBar();
...
add(toolbar, BorderLayout.NORTH);
...
}
BoxLayout Manager
Ernöglicht es, Komponenten in eine Reihe oder einer Spalte anzuordnen. Die Komponenten werden dabei nicht größer dargestellt als unbedingt notwendig.
{
...
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
Beinhaltet mehrere alternative Container, die jeweils über einen String identifiziert werden und zwischen denen dann über diesen String hin und her geschaltet werden kann.
{
...
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.
{
...
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.
{
...
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();
...
}
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:
{
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.
Das ist vermutlich nicht gewünscht.
SpringLayout Manager
SpringLayout Manager (Für GUI Builder gedacht) Die Komponenten werden relativ zum Rand benachbarter Komponenten angegeben.
{
...
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();
...
}
Was läuft in Swing in welchem Thread
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:
{
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.
{
@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 && 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:
{
...
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_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
@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:
Man kann aber auch für alle Elemente eine Reihenfolge bestimmen, in der die Elemente den Fokus erhalten.
...
frame.setFocusTraversalPolicy(focusPolicy);
frame.pack();
frame.setVisible(true);
FocusTraversalPolicy bei Oracle oder eine von mir
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 && 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("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.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);
}
}