Swing oder AWT? Was ist das: Swing bzw. AWT Beides sind Klassenbibliotheken mit fertig programmierten Klassen zur Erstellung von graphischen Benutzeroberflächen mit Fenstern, Textfeldern, Buttons etc.. AWT wurde zuerst programmiert und ist ziemlich begrenzt. Swing wurde später hinzugefügt und baut teilweise auf AWT auf. Swing ist wesentlich umfangreicher und flexibler als AWT. Es gibt heutzutage eigentlich keinen Grund eine reine AWT Anwendung zu schreiben, da es inzwischen auch möglich ist Applets mit Swing zu schreiben. Haupterkennungsmerkmal von Swing Source-Code: Man erkennt Swing-Programme daran, dass die Klassen der Komponenten alle mit 'J' anfangen, während das bei AWT nicht so ist: Beispiel für einen Button in AWT: Button b1=new Button("bitte klicken"); Swing: JButton b1 = new JButton("bitte klicken"); Ausserdem benötigen Swing-Programme folgende Import Anweisung: import javax.swing.* oder Ähnliches. FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Ein erstes Beispiel: Zunächst ein sehr einfaches Demoprogramm welches ein Fenster mit dem typischen Erscheinungsbild darstellt: AWT: Swing: Was fällt auf? Beide Anwendungen schliessen nicht korrekt, wenn man auf den Schliessen Knopf 'x' klickt! FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Ab jetzt nur noch Swing JFrame korrekt schliessen: Diese Zeile einfügen Von JFrame erben: Bisher haben wir in der main(...) Methode ein Objekt von JFrame angelegt, besser ist es aber von JFrame zu erben: JFrame Swing_Frame1 FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss In einen Frame zeichnen Man sollte nicht direkt in einen JFrame zeichnen, statt dessen wird eine neue Klasse 'DrawPanel' von JPanel abgeleitet, welche paint(...) überschreibt: public class DrawPanel extends JPanel { public void paint(Graphics g) { super.paint(g); g.setFont(new Font("Arial",Font.PLAIN,24)); g.drawString("Drawing with Java is easy!", 10, 20); g.setColor(Color.red); g.drawRoundRect(20, 40, 60, 40, 20, 10); g.setColor(Color.blue); g.drawLine(50, 50, 160, 200); } } g.setColor(Color.green); g.fillOval(150, 50, 60, 100); public class Swing_Frame extends JFrame{ //Our Custom JFrame needs a constructor public Swing_Frame4(String title) throws HeadlessException { super(title); this.add(new DrawPanel()); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(300, 250); } public static void main(String[] args) { JFrame frame1 = new Swing_Frame("Frame with Drawingspace"); } frame1.setVisible(true); } FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Mechanismus: Warum braucht man eine paint(...) Methode zum Zeichnen? Ein Programm in einer GUI-Umgebung arbeitet ereignissgesteuert. Immer wenn das Fenster neu gezeichnet werden muss, weil es z.B. gerade minimiert war und jetzt wieder maximiert wird, ruft das Betriebssystem die Methode paint(...) auf. Alles was in der Methode paint(...) programmiert ist, wird gezeichnet. Man sollte immer in einen eigenen JPanel zeichnen, nicht in das JFrame! Den graphischen Kontext Graphics, welcher der 'paint(Graphics g)' Methode übergeben wird, kann man als eine Art Zeichenblatt betrachten. Statt eines Malstiftes hat dieser Methoden welche die unterschiedlichsten graphischen Figuren wie Kreise, Rechtecke und vieles mehr zeichnen können. FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Übung: Schreiben Sie in die paint(...) Methode von DrawPanel ein Programm, welches untenstehendes Ergebnis erzeugt. Die Schneeflocken sind gefüllte Kreise mit zufälliger Grösse und Position. Benutzen Sie dazu die Methode Math.random(). Viel Spass beim Ausprobieren! FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Ausschnitt aus der Swing /AWT Klassenhierarchie JComponent Object Component JLabel Button Label AbstractButton JPanel Container Choice Canvas JText Window List JList JText JTextArea JComboBox JScrollBar ScrollBar JMenuBar CheckBox JOptionPane Frame Dialog JFrame JDialog FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss JToggleButton JCheckBox JMenu JMenuItem JRadioButton JButton Elementare GUI-Komponenten hinzufügen Bisher haben wir dem Hauptfenster (JFrame) nur einen JPanel hinzugefügt. Dazu wurde die Methode add(...) von JFrame aufgerufen. Mit dieser Methode können einem "Container" beliebige GUI-Komponenten hinzugefügt werden. Ein Container ist eine GUI-Komponente, welche andere GUI-Komponenten aufnehmen kann. Die meisten Swing-Komponenten sind solche Container -> Folie7 Jetzt werden wir einen JButton und ein JTextField hinzufügen: Das Ergebnis schaut wohl nicht so aus wie wir uns das vorgestellt hatten! Der Button ist unter dem Textfeld verschwunden. FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Elementare GUI-Komponenten hinzufügen Ganz so einfach ist es wohl doch nicht, deshalb änderen wir ein paar Kleinigkeiten, was ein besseres Ergebnis liefert: public class Swing_Frame5 extends JFrame { private JButton b1,b2; private JTextField tf; public Swing_Frame5(String title) throws HeadlessException { super(title); //get reference of the ContentPane //and set to FlowLayout Container main=this.getContentPane(); main.setLayout(new FlowLayout()); main.add(new JTextField(" 0")); main.add(new JButton("click me")); } Diese Zeile einfügen this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(300, 250); public static void main(String[] args) { JFrame frame1 = new Swing_Frame5("Frame with Button and Textfield"); } } frame1.setVisible(true); FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Elementare GUI-Komponenten hinzufügen Was wurde geändert?: Komponenten werden nicht direkt in einen JFrame gepackt, sondern in das Contenpane des JFrames, welches man mit der Methode:getContentPane() holen kann. Dem Contentpane wird ein FlowLayout zugewiesen. Layouts sind Klassen, welche die Anordnung von GUIKomponenten in Containern steuern, aber dazu mehr im Kapitel LayoutManager. FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Was passiert beim Klicken auf den Button ? So wie das Programm jetzt ist, passiert noch gar nichts wenn man auf den Button klickt. Wir erweitern jetzt das Programm so, dass beim Klicken auf den Button im Textfeld der Text: "you clicked 1 times..." erscheint. Bei jedem erneuten Klick wird die Zahl im Text um eins erhöht, somit haben wir einen Zähler welcher die Anzahl Klicks zählt. Für diese einfach scheinende Erweiterung muss man zunächst verstehen wie in Java Ereignisse behandelt werden. Dafür ist wiederum das Verständnis von Interfaces erforderlich, deshalb im Folgenden ein kurzer theoretischer Ausflug in die objektorientierte Programmierung: FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Interfaces und ActionListener Das UML-Diagramm auf der rechten Seite zeigt 2 Klassen, wobei die Klasse Swing_Frame von JFrame abgeleitet ist und stellt damit unser Programmbeispiel dar. Angenommen die Klasse Swing_Frame müsste noch von einer weiteren Klasse erben, dann wäre das in Java nicht möglich. JFrame Swing_Frame Mehrfachvererbung ist in Java nicht erlaubt! An Stelle von Mehrfachvererbung werden in Java Interfaces verwendet! In der UML-Darstellung sieht das dann so aus: <<interface>> ActionListener JFrame So etwas ist in Java nicht erlaubt! ActionListener implementiert JFrame Swing_Frame Swing_Frame FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Interfaces und ActionListener In der objektorientierten Sprache würde man hier sagen: Swing_Frame implementiert das Interface ActionListener! Ein Interface ist so etwas Ähnliches wie eine Klasse, mit dem Unterschied dass davon kein Objekt erzeugt werden kann und dass es dort nur leere Methodendeklarationen gibt. Hier ein Beispiel für ein Interface aus der Java Standartbibliothek: Die Definition ist gleich wie bei einer Klasse nur, dass anstatt class, interface geschrieben wird. In diesem Beispiel gibt es nur eine Methode die leer ist, also keinen Sourcecode beinhaltet. FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Interfaces und ActionListener Eine Klasse erbt von einer Basisklasse mit dem Schlüsselwort extends, während eine Klasse ein Interface implementiert mit dem Schlüsselwort implements. Das UML-Diagramm weiter oben sieht dann als Programmcode folgendermassen aus: Programmcode der Klasse . . . . Diese Methode muss zwingend vorhanden sein, da sie im Interface definiert ist! Nochmal: Anstatt von ActionListener zu erben wird hier das Interface implementiert und das kann im Gegensatz zur Vererbung von beliebig vielen Interfaces gemacht werden! FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Auf Button Click reagieren mit ActionListener Damit bei einem Click auf den Button das passiert was der Programmierer gern möchte, muss dem Button gesagt werden, wer auf ihn hört. Dies wird mit folgender Programmzeile gemacht: wobei b ein Objekt von JButton ist. Da diese Programmzeile in unserer Klasse Swing_Frame steht und wir this übergeben, sendet der Button die Klick-Nachricht an den Frame, in dem er sich befindet, also an Swing_Frame. Dies geht aber nur, wenn Swing_Frame vom Typ ActionListener ist. Diese Vorraussetzung wurde geschaffen wie in der vorigen Folie zu sehen ist. JFrame extends Swing_Frame "click me" sendet KlickEreignis an ActionListner Swing_Frame und ruft dort actionPerformed(...) auf <<ActionListener>> implements Swing_Frame implementiert ActionListener und ist somit vom Typ ActionListener! + actionPerformed(ActionEvent e) FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss actionPerformed(...) auscodieren: Hier nun die komplette Klassendefinition: public class Swing_Frame6 extends JFrame implements ActionListener { private JButton b; private JTextField tf; private int nClicks=0; //counts the number of buttonclicks public Swing_Frame6(String title) throws HeadlessException { super(title); //get Contentpane and set Layoutmanager Container contPane=getContentPane(); contPane.setLayout(new FlowLayout()); //create GUI-Elements... b=new JButton("click me"); tf=new JTextField("you clicked 0 times"); //...and add them to Contentpane contPane.add(b); contPane.add(tf); Nach dem ersten klicken auf 'click me': //Button notifies a click to his own JFrame b.addActionListener(this); } this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(300, 250); //actionPerformed(...) is called whenever an //action in this Frame is done. //In this case when the Button is clicked, it sends a //message to this JFrame which is an ActionListener public void actionPerformed(ActionEvent e) { if(e.getActionCommand()=="click me") { nClicks++; tf.setText("you clicked "+nClicks+" times "); } } Nach dem zweiten klicken auf 'click me': public static void main(String[] args) { JFrame frame1 = new Swing_Frame6( "Frame with Button and Textfield"); } } FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss frame1.setVisible(true); Übersicht der meisten Java Listener FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Beispiel mit MouseListener public class DrawPanelWithMouseListener extends JPanel implements MouseListener { private Rectangle r; private Color baseColor=Color.gray; private Color currentColor; public DrawPanelWithMouseListener() { super(); r=new Rectangle(100,100,50,50); currentColor=baseColor; this.addMouseListener(this); } Bei einem Klick in das Rechteck wird es rot, ausserhalb davon grau public void paint(Graphics g) { super.paint(g); g.setColor(currentColor); g.fillRect(r.x, r.y, r.width, r.height); } public void mouseClicked(MouseEvent e) { if(r.contains(e.getPoint())) { this.currentColor=Color.red; }else { this.currentColor=baseColor; } this.repaint(); } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseEntered(MouseEvent e) { public void mouseReleased(MouseEvent e) { } } } FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Beschreibung: Das DrawPanelWithMouseListener implementiert einen MouseListener dafür muss es die Methoden 'mouseClicked(...)','mousePressed(...)','mouseEntered(...), etc. enthalten, insgesamt sind es fünf. Da wir für das Programm vorläufig nur einen MouseClick abfragen müssen, sind alle Methoden ausser 'mouseClicked(...)' leer. In 'mouseClicked(MouseEvent e)' kann aus dem Parameter e mit der Methode e.getPoint() die aktuelle Position des Mausezeigers ermittelt werden. In der Zeile if(r.contains(e.getPoint()) wird geprüft, ob der Mauszeiger innerhalb des Rechtecks ist. Demnach wird dann die Farbe auf rot oder grau gesetzt. FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Interface MouseListener Hier der Code von MouseListener wie er in der Java Standard-Klassenbibliothek definiert ist: public interface MouseListener extends EventListener { Wie man sieht, können sogar Interfaces vererbt werden, sämtliche Listener sind in Java von EventListener abgeleitet. public void mouseClicked(MouseEvent e); public void mousePressed(MouseEvent e); public void mouseReleased(MouseEvent e); public void mouseEntered(MouseEvent e); public void mouseExited(MouseEvent e); } FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Übersichtlicher mit Adapter Ein Listener kann auch in einer anderen Klasse implementiert werden, als in derjenigen in der das Event erzeugt wird: public class MouseListenerExtern implements MouseListener { DrawPanelWithMouseAdapter sender; public MouseListenerExtern(DrawPanelWithMouseAdapter sender) { this.sender = sender; } public void mouseClicked(MouseEvent e) { if(sender.getRectangle().contains(e.getPoint())) { sender.setColor(Color.red); }else { sender.resetColor(); } Der Methode addMouseListener in DrawPanel... muss sender.repaint(); } jetzt allerdings dieser Listener übergeben werden!: public void mouseEntered(MouseEvent e) { } addMouseListener(new MouseListenerExtern(this)); public void mouseExited(MouseEvent e) { } Der sender wird gebraucht, da der Listener auf das Rechteck und die Farbe von DrawPanel... zugreifen können muss. public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } } FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss Übersichtlicher mit Adapter Wenn man nun den MouseListener nicht implementiert, sondern als Klasse von MouseAdapter ableitet, müssen nur die Methoden geschrieben werden die man braucht: public class MouseHandler extends MouseAdapter{ DrawPanelWithMouseAdapter sender; public MouseHandler(DrawPanelWithMouseAdapter sender) { this.sender=sender; } public void mouseClicked(MouseEvent e) { } } if(sender.getRectangle().contains(e.getPoint())) { sender.setColor(Color.red); }else { sender.resetColor(); } sender.repaint(); Dies funktioniert mit einem einfachen Trick: MouseAdapter implementiert MouseListener und damit alle seine Methoden. In der von MouseAdapter abgeleiteten Klasse wird einfach die mouseClicked(...) Methode überschrieben. MouseAdapter ist in der Java-Standardbibliothek vordefiniert! Diese Adapter gibt es für viele andere Listener auch. siehe Folie 17 FH Biel, Mikro -und Medizintechnik, Java GUI-Programmierung, Peter Füss