Grafische Benutzeroberflächen mit Swing Sven Hartmeier [email protected] Übersicht ● Historisches – – ● ● ● Applet vs. WebStart vs. Application Sicherheitskonzepte in Java / Sandbox DesignPattern : MVC – ● ● AWT vs. JFC/Swing leicht- und schwergewichtige Elemente Beispiel Swing : Komponenten, Layouts, Ereignisse Threads Historisches (1) ● Java 1.0 (1996) AWT (AbstractWindowToolkit) package java.awt.* ● Java 1.1 (1997) OO- Ereignissteuerung, JFC/Swing als Erweiterung package javax.swing.* ● seit Java 1.2 (1998) JFC/Swing als Standard GUI Historisches (2) AWT ● ● ● ● nur wenige Komponenten Thread-sicher kein MV Prinzip HeavyWeight JFC/Swing ● ● ● ● ● ● unzählige Komponenten erweiterbar nicht Thread-sicher MV Prinzip skinable LightWeight LightWeight vs. HeavyWeight ● ● ● ● ● keine direkte Repräsentation im UI plattformunabhängig aber : keine plattformspezifischen Komponenten Erscheinungsbild kann während der Laufzeit und unabhängig vom System geändert werden (skinable) einfach erweiterbar Applet, Webstart und Applikation Browser javaws java JVM Sandbox Applet WebStart Application Designpattern ModelViewController Model View Controller Modell (Model) ● ● Datenfelder Logik (Funktionen) Model View Controller Repräsentation (View ) ● ● Darstellung des Modells Benutzerinteraktion Model View Controller Steuerung (Controller) ● ● ● Benutzeraktion auswerten Aktion ausführen Präsentation ändern Model View Controller Ein Fenster zur Welt import javax.swing.JFrame; public class HelloSwingFrame { public static void main(String[] args) { JFrame f = new JFrame("Das Fenster zur Welt!"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(300,200); f.setVisible(true); } } Klassenhierachie Canvas Canvas javax.swing.* Panel Panel TextComponent TextComponent Component Component Container Container JTextComponent JTextComponent JComponent JComponent Button Button CheckBox CheckBox JPanel JPanel AbstractButton AbstractButton Window Window JWindow JWindow Frame Frame java.awt.* JFrame JFrame Buttons JButton JButton JToggleButton JToggleButton JCheckBox JCheckBox JRadioButton JRadioButton AbstractButton AbstractButton JMenuItem JMenuItem JMenu JMenu JCheckBoxMenuItem JCheckBoxMenuItem JRadioButtonMenuItem JRadioButtonMenuItem JButton import javax.swing.JButton; import javax.swing.JFrame; public class Beispiel_JButton { public static void main(String [] args){ JFrame f = new JFrame("Das Fenster zur Welt!"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(new JButton("Ich bin ein JButton!")); f.setSize(300,200); f.setVisible(true); } } JTextComponent JTextComponent JTextComponent JTextField JTextField JTextArea JTextArea JEditorPane JEditorPane JTextPane JTextPane JFormattedTextField JFormattedTextField JPasswordField JPasswordField Text Controls Plain Text Areas Styled Text Areas JTextField import javax.swing.JFrame; import javax.swing.JTextField; public class Beispiel_JTextField { public static void main(String[] args) { JFrame f = new JFrame("Das Fenster zur Welt!"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(new JTextField("Ich bin ein JTexfield!",60)); f.setSize(300,200); f.setVisible(true); } } JLabel import javax.swing.JFrame; import javax.swing.JLabel; public class Beispiel_JLabel { public static void main(String[] args) { JFrame f = new JFrame("Das Fenster zur Welt!"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(new JLabel("Ich bin ein JLabel!",60)); f.setSize(300,200); f.setVisible(true); } } JPanel ● ● ● Behälter für andere JComponenten (JPanel, JButton, JTextField, ...) JPanel zeichnet nur seinen Hintergrund benutzt einen LayoutManager um JComponenten im JPanel anzuordnen typische Anwendung : Positionieren von JComponenten Layout : FlowLayout ... public class Beispiel_FlowLayout extends JPanel{ public Beispiel_FlowLayout(){ for(int i = 1; i <= 5; ++i){ add(new JButton("Button "+(Math.pow(10, i)))); } } public static void main(String[] args) { JFrame f = new JFrame("FlowLayout"); f.add(new Beispiel_FlowLayout()); f.pack(); f.setVisible(true); } } Layout : BorderLayout ... public class Beispiel_BorderLayout extends JPanel{ public Beispiel_BorderLayout(){ setLayout(new BorderLayout()); add(new JButton("Norden"),BorderLayout.NORTH); add(new JButton("Westen"),BorderLayout.WEST); add(new JButton("Osten"),BorderLayout.EAST); add(new JButton("Süden"),BorderLayout.SOUTH); add(new JButton("Mitte"),BorderLayout.CENTER); } ... Layout : GridLayout ... public class Beispiel_GridLayout extends JPanel { public Beispiel_GridLayout(){ setLayout(new GridLayout(3,3)); for (int i = 9; i >= 1; --i){ add(new JButton(new Integer(i).toString())); } } ... Layout : BoxLayout ... public class Beispiel_BoxLayout extends JPanel { public Beispiel_BoxLayout(){ this(BoxLayout.X_AXIS); } public Beispiel_BoxLayout(int direction){ setLayout(new BoxLayout(this, direction)); for(int i = 1; i <=5; ++i){ add(new JButton(new Integer(i).toString())); } } ... Ereignissteuerung ● ● ● ● eine Komponente kann ein Ereignis (Event) auslösen jedes Event wird durch eine Klasse repräsentiert (z.B. ActionEvent) ein Event kann durch ein oder mehrere Listener verarbeitet werden JComponents haben ähnliche Methoden zum Hinzufügen und Entfernen von Listenern für ein unterstütztes Event – addXXXListener(XXXEvent) – removeXXXListener(); Beispiel : Das Interface ActionListener import import import import public java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JFrame; class BeispielActionListener implements ActionListener { public void actionPerformed(ActionEvent ae){ //Ausgabe des zum ActionEvent gehörenden ActionEvent System.out.println(“Geklickt: ”+ae.getActionCommand()); } public static void main(String[] args){ JFrame jf = new Jframe(“BeispielActionListener”); //Beschriftung des Jbutton ist gleichzeitig ActionCommand JButton jb = new JButton(“Klick mich”); BeispielActionListener bal = new BeispielActionListener(); jb.addActionListener(bal); jf.setVisible(true); jf.pack(); } } bei Klick > Geklickt: Klick mich Events und Listener Typen ● Es gibt viele weitere, spezialisierte Eventund dazu passende Listener-Typen Mehr dazu unter: http://java.sun.com/docs/books/tutorial/uiswing/events/index.html Was sind Threads? ● ● ● Erlauben Nebenläufigkeit von unterschiedlichen Programmteilen Quasi-parallel, je nach Systemarchitektur auch echt parallel Ermöglichen bessere Ausnutzung von Prozessorzeit und anderen Resourcen, z.B. Bandbreite einer Internetverbindung Threads in Java: Die Klasse java.lang.Thread public class ThreadDemo extends Thread{ //Diese Methode stammt aus Runnable, ist aber in Thread //leer implementiert und muss daher überschrieben werden public void run(){ //Zählen in einer Schleife von 0 bis 9 for(int i=0;i<10;i++){ //Ausgabe jeder Zahl auf der Konsole System.out.println("Zahl "+i); } //fertig! System.out.println("Fertig!"); } public static void main(String[] args){ //Erzeugen einer neuen Instanz, inklusive Thread ThreadDemo td = new ThreadDemo(); //Thread starten td.start(); } } Threads in Java: Das Interface java.lang.Runnable public class RunnableDemo implements Runnable{ //Diese Methode stammt aus Runnable und muss //implementiert werden public void run(){ //Zählen in einer Schleife von 0 bis 9 for(int i=0;i<10;i++){ //Ausgabe jeder Zahl auf der Konsole System.out.println("Zahl "+i); } //fertig! System.out.println("Fertig!"); } public static void main(String[] args){ //Erzeugen einer neuen Instanz RunnableDemo rd = new RunnableDemo(); //Erzeugen eines Thread, um das Runnable auszuführen Thread t = new Thread(rd); //Thread starten t.start(); } } Beispiel: Downloadmanager ● ● ● ● Wir wollen ein Programm zum Download von Dateien schreiben Es sollen mehrere Dateien geladen werden können Eingabe: URLs, die die Dateien auf einem entfernten Rechner addressieren, Zugriff über das http Protokoll Öffnen einer Verbindung pro Datei, herunterladen der Datei auf den lokalen Rechner Beispiel: Downloadmanager public class DownloadManager{ public DownloadManager(URL[] url) { //Vereinfachte For-Schleife for(URL u:url){//sequentiell-> Ausführung hintereinander download(u); } } public File download(URL url) { //Öffne http Verbindung HttpURLConnection connection = (HttpURLConnection) url.openConnection(); //Verbinde mit Server connection.connect(); //öffne url InputStream istream = connection.getInputStream(); //lese von istream und schreibe in datei (ohne Details) return readFile(istream); } ... } Beispiel: Downloadmanager ● ● ● Problem 1: Wir wollen mehr als eine Datei gleichzeitig herunterladen können, um unseren Internetanschluß voll auszulasten Problem 2: Download einer Datei dauert eventuell sehr lang, andere sind vielleicht schneller heruntergeladen Lösung: Jede Datei wird von einem eigenen, unabhängigen Thread heruntergeladen! Beispiel: Downloadmanager Beispiel: Downloadmanager public class Download implements Runnable { private URL url = null; public Download(URL url) { this.url = url; } public void run() { HttpURLConnection connection = null; try {//Verbindung aufbauen connection = (HttpURLConnection) url.openConnection(); connection.connect(); readFile(connection.getInputStream());//einlesen } catch (IOException e) { e.printStackTrace(); } finally { if (connection != null) {//Verbindunng in jedem Fall schliessen connection.disconnect(); } } ... } Beispiel: Downloadmanager public class DownloadManager2{ public DownloadManager2(URL[] url) { //Vereinfachte For-Schleife (ab Java 1.5) for(URL u:url){//sequentiell-> Ausführung hintereinander Thread t = new Thread(new Download(u)); t.start();//Methode kehrt sofort zurück -> Ausführung der //anderen Downloads beginnt unmittelbar } } public static void main(String[] args) { URL[] url = new URL[args.length];//Vorsicht, evtl. ist args==null for(int i=0;i<args.length;i++) { url[i] = new URL(args[i]); } //Download starten DownloadManager2 dm = new DownloadManager2(url); } } Speziallfall Swing: Der Event Dispatch Thread ● ● ● Die meisten Swing Objektmethoden sind nicht synchronisiert => Veränderungen durch verschiedene Threads können zu Inkonsistenzen führen Daher: Synchronisierungen von Änderungen über den Event Dispatch Thread => Events werden dadurch in der Reihenfolge Ihres Eintreffens ausgeführt Code auf dem EDT sollte nicht lange brauchen, sonst => Blockieren der GUI! ActionListener revisited, wie man die GUI blockiert ... public class BeispielActionListener implements ActionListener { ... public void actionPerformed(ActionEvent ae){ //Ausgabe des zum ActionEvent gehörenden ActionEvent System.out.println(“Geklickt: ”+ae.getActionCommand()); //eintausend Wiederholungen for(int i = 0; i< 1000;i++) { if (SwingUtilities.isEventDispatchThread(){ //Ausführung im EventDispatchThread? System.out.println("Running on EDT!"); } try {//Für 100 Millisekunden schlafen Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } ... > Geklickt: Klick } bei Klick und 1000x > Running on EDT! mich Spezialfall Swing: Der Event Dispatch Thread //Erweiterung von Download public class Download implements Runnable { //Jlabel stellt u.a. einzeilige Strings dar, nicht editierbar! Jlabel progressLabel = null; ... public Download(URL url, Jlabel progressLabel) { this.url = url; this.progressLabel = progressLabel; } @Override public void run() { HttpURLConnection connection = null; try {//Verbindung aufbauen updateLabel(“Baue Verbindung auf zu “+url.toString()); connection = (HttpURLConnection) url.openConnection(); connection.connect(); updateLabel(“Verbunden”); readFile(connection.getInputStream());//einlesen updateLabel(“Datei geladen!”); ... } } Spezialfall Swing: Der Event Dispatch Thread //Erweiterung von Download public class Download implements Runnable { ... //message ist als final (unveränderlich) deklariert private void updateLabel(final String message) { Runnable r = new Runnable( public void run() { //Nur Setzen der Nachricht auf dem Jlabel, //keine zeitintensiven Aktionen! progressLabel.setText(message); } ); //Führt r demnächst auf dem EventDispatch aus SwingUtilities.invokeLater(r); } ... } Threads: Zusammenfassung ● ● ● ● Threads sind in Java einfach zu benutzen Für unabhängige Aufgaben in einer GUI: s.h. Beispiel MyWorker im Skript Je unabhängiger ein Programmteil von Interaktion mit anderen ist, desto einfacher ist dessen Auslagerung in einen Thread Mit Threads sind vorhandene Resourcen, z.B. verfügbare Prozessoren eines Rechners besser nutzbar Threads: Zusammenfassung ● ● ● Offene Probleme: Threads, die sich eine Resource teilen, z.B. die Standardausgabe (Konsole), konkurrieren um den Zugriff darauf => Synchronisierung nötig, sonst nicht vorhersagbares Verhalten Nicht behandelt: Der Lebenszyklus eines Threads Dazu und zu vielem mehr http://java.sun.com/docs/books/tutorial/essential/concurrency/ Lesenswertes ... ● Java ist auch eine Insel – Kapitel 15 http://www.galileocomputing.de/openbook/javainsel7/ ● Swing Tutorial http://java.sun.com/docs/books/tutorial/uiswing/