CS1005 Objektorientierte Programmierung Bachelor of Science (Informatik) Th Letschert Graphische Benutzerschnittstellen I GUI erzeugen Seite 1 © Th Letschert GUI GUI: Graphical User Interface Graphische Benutzeroberfläche Seite 2 Th Letschert Beispiel: ein leeres Fenster import javax.swing.JFrame; public class SimpleGUIDemo { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setVisible(true); System.out.println(“Main endet!“); } } main Erzeugt ein Fenster und macht es sichtbar. erzeugt GUI Erweitertes Konzept von „Programm-Ausführung“: Das Programm endet nicht mit dem Ende von main ... und auch nicht mit dem „Wegklicken“ des Fensters ! GUI-Code endet geht weiter Seite 3 Th Letschert Beispiel: ein leeres Fenster Hier Fenster wegklicken ! Programm endet damit nicht. Programm ist aktiv ! Seite 4 Th Letschert Threads: GUI ist unabhängig von main aktiv import javax.swing.JFrame; public class SimpleGUIDemo { public static void main(String[] args) { JFrame frame = new Jframe(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); System.out.println(“Main endet!“); Wegklicken des } Wegklicken des Fensters stoppt jetzt } Fensters stoppt jetzt das Programm. das Programm. Das Programm besteht aus 2 Handlungssträngen (Threads): - main und alle Methoden die von main direkt oder indirekt aktiviert werden - die GUI Um das Gesamt-Programm zu beenden, muss main und „die GUI“ enden. Seite 5 Th Letschert Mein-Thread und Event-Dispatcher Benutzeraktionen verarbeiten EventDispatchingThread import java.awt.Container; import javax.swing.JButton; import javax.swing.JFrame; pub lic cla ss SimpleGUIDemo { p ubli c s tatic void main(String[] args) { JFrame frame = new JFrame(); GUIObjekte Programmcode, beginnend mit main, abarbeiten Auf GUI-Objekte können zwei unabhängige Handelnde (Threads) zugreifen. Das kann zu Konfliktsituationen und Problemen führen. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOS E); // Knopf (ein Widget) erzeugen JBu tton button = new JBu tton("KlickMich!"); // Container des Fensters holen Con tainer container = frame.getContentPane(); main Thread // Knpof in Fenster setzen container.add(button); //Fenstergroesse bestimmen frame.setSize(400,500); // Breite Hoehe frame.setVisible(true); Programm: Objekte werden von 2 Threads (Aktivitäten) bearbeitet Seite 6 } } Th Letschert Threads Thread = (Handlungs-) Faden Handlungsfaden / Thread: zusammenhängende Folge von Aktionen ( Zuweisungen / Schleifen / Methodenaufrufe, etc. ) wobei eine Aktion die nächste bedingt / verursacht / beinflusst. public class Test { public static void main(String[] args) { .... } } main-Thread: bearbeitet main und alle von dort aufgerufenen Methoden Main-Thread: alle Programme haben einen Handlungsfaden, der mit main beginnt (mainThread) Event-Dispatcher: GUI-Programme haben zusätzlich einen Handlungsfaden, der die Benutzeraktionen beobachtet (Event-DispatchingThread / Event-Dispatcher) Event-Dispatcher: bearbeitet Maus-Klicks, ... Garbage-Collector: Im Hintergrund ist immer noch ein weiterer Thread aktiv, der GarbageCollector (-Thread) Java-Programm mit seinen Threads Seite 7 Garbage-Collector: sammelt Speicher-Müll Th Letschert Ein Fenster mit einem Knopf Widget ~ Window Gadget (Fenster-Dings): Knopf, etc. KlickMich import javax.swing.JButton; import javax.swing.JFrame; public class SimpleGUIDemo { der Knopf wird sehr groß sein! public static void createGUI() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Knopf (ein Widget) erzeugen JButton button = new JButton("KlickMich!"); Fenster erzeugen Widget erzeugen // Knpof in Fenster setzen frame.add(button); Widget einfügen } } //Fenstergroesse bestimmen frame.setSize(400,500); // Breite Hoehe frame.setVisible(true); Größe bestimmen sichtbar machen public static void main(String[] args) { createGUI(); } Seite 8 Th Letschert Einfache GUI: Übersicht Fenster erzeugen Komponenten erzeugen [ contentPane holen Komponenten hinzufügen Größe bestimmen wegklickbar machen sichtbar machen frame = new JFrame.. button = new JButton... getContentPane() nur bis Version 1.4] frame.add(button); frame.setSize frame.setDefaultCloseOperation frame.setVisible Seite 9 Th Letschert So soll es aussehen! GUI mit 2 Widgets ein Label import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; public class SimpleGUIDemo { public static void createGUI() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Knopf und Label erzeugen JButton button = new JButton("KlickMich!"); JLabel label = new JLabel("ein Label"); // Knopf und Label in Fenster setzen frame.add(button); frame.add(label); frame.setSize(400,500); // Breite, Hoehe frame.setVisible(true); } } public static void main(String[] args){ createGUI(); } Seite 10 Th Letschert GUI mit 2 Widgets import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; public class SimpleGUIDemo { public static void createGUI() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Knopf und Label erzeugen JButton button = new JButton("KlickMich!"); JLabel label = new JLabel("ein Label"); // Knopf und Label in Fenster setzen frame.add(button); frame.add(label); } } frame.setSize(400,500); // Breite, Hoehe frame.setVisible(true); public static void main(String[] args){ createGUI(); } Man sieht nur das Label! Es füllt das gesamte Fenster aus und deckt den Knopf ab. Layout-Management notwendig! so sieht es aus – Igitt ! Seite 11 Th Letschert Layout-Management Layout-Management Layout (Position/Größe) von Komponenten festlegen Notwendig wenn in einem Fenster mehr als eine Komponente (Widget) platziert wird. Layout-Manager Objekt, das dies übernimmt Layout festlegen Zuordnen eines Layout-Managers Verschiedene Arten von Layout-Managern Legen selbständig das Layout nach verschiedenen Strategien entsprechend der Benutzervorgaben fest: Was – wie groß – wohin platzieren Absolute Größen und Positionen der Komponenten müssen (und sollten in der Regel) nicht angegeben werden Seite 12 Th Letschert Beispiel: Layout-Manager FlowLayout import import import import java.awt.FlowLayout; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; public class SimpleGUIDemo { Flow-Layout positioniert von links nach rechts public static void createGUI() { und zeilenweise. JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Knopf und Label erzeugen JButton button = new JButton("KlickMich!"); JLabel label = new JLabel("ein Label"); // Layout-Manager setzen frame.setLayout( new FlowLayout() ); // Knopf und Label in Fenster setzen frame.add(button); frame.add(label); frame mit Flow-Layout ausstatten frame.setSize(400,100); // Breite Hoehe frame.setVisible(true); } } public static void main(String[] args){ createGUI(); } Seite 13 Th Letschert einige wichtige Layout-Manager GridLayout Ordnet die Komponenten in einem Gitter (Grid) an. Die Reihenfolge des Einfügens der Komponenten bestimmt ihre Anordnung. BorderLayout Positioniert jeweils eine Komponente in 5 Regionen jeweils maximal eine Komponente Rand-Regionen: North, East, South, West (so klein wie möglich) Zentral-Region: CENTER (restlicher Platz) FlowLayout: Positioniert Komponenten links-nach-rechts / oben-nach-unten zeilenweise: Zeile voll, nächste Komponente in die nächste Zeile BoxLayout Ähnlich wie FlowLayout aber Komponente können explizit in eine neue Zeile positioniert werden Seite 14 Th Letschert Layout-Manager : BoarderLayout positioniert in eine von 5 Regionen: NORTH BorderLayout. NORTH BorderLayout. SOUTH BorderLayout. EAST BorderLayout. WEST BorderLayout. CENTER Beispiel: Frame im BoarderLayout mit 5 Labels. JLabel labelN = JLabel labelS = JLabel labelE = JLabel labelW = JLabel labelC = JLabel("CENTER"); WEST CENTER EAST SOUTH new new new new new JLabel("NORTH"); JLabel("SOUTH"); JLabel("EAST"); JLabel("WEST"); frame.setLayout(new BorderLayout()); frame.add(labelN,BorderLayout.NORTH); frame.add(labelS,BorderLayout.SOUTH); frame.add(labelE,BorderLayout.EAST); frame.add(labelW,BorderLayout.WEST); frame.add(labelC); Seite 15 CENTER ist default-Wert. Th Letschert JPanel: GUIs hierarchisch aufbauen JPanel: ein Container als Komponente JFrame f = new JFrame( "BorderLayout" ); f.setLayout(new BorderLayout() ); f:JFrame (BorderLayout) jp:JPanel (FlowLayout) JPanel jp = new JPanel( new FlowLayout() ); f.add( jp, BorderLayout.NORTH ); jp.add( new JButton( "NORD-1" ) ); jp.add( new JButton( "NORD-2" ) ); f.add( f.add( f.add( f.add( new new new new JButton( JButton( JButton( JButton( "OST" ), BorderLayout.EAST ); "SUED" ), BorderLayout.SOUTH ); "WEST" ), BorderLayout.WEST ); "ZENTRUM" ), BorderLayout.CENTER ); JButton NORD-1 JButton NORD-2 JButton OST JButton WEST f.setSize( 300, 200 ); f.setVisible( true ); JButton SUED JButton ZENTRUM Hierarchischer Aufbau Graphischer Aufbau Seite 16 Th Letschert BoxLayout BoxLayout: Komponenten stapeln f: Jframe (BoarderLayout) public class SimpleGUIDemo { public static void createGUI() { JFrame f = new JFrame( "BoxLayout" ); f.setLayout( new BorderLayout() ); jp:JPanel (BoxLayout) JButton NORD-1 JPanel jp = new JPanel(); jp.setLayout(new BoxLayout(jp, BoxLayout. Y_AXIS )); jp.add(new JButton("NORD-1")); jp.add(new JButton("NORD-2")); f.add(jp, BorderLayout.NORTH); f.add(new f.add(new f.add(new f.add(new JButton("OST"), BorderLayout.EAST ); JButton("SUED"), BorderLayout.SOUTH ); JButton("WEST"), BorderLayout.WEST ); JButton("ZENTRUM"),BorderLayout.CENTER); NORD-2 JButton OST JButton WEST JButton SUED JButton f.pack(); f.setVisible(true); } JButton ZENTRUM Komponente mit BoxLayout in } Komponente mit public static void main(String[] args){ createGUI(); } BoarderLayout Seite 17 Th Letschert Größe bestimmen: pack / setSize Fenstergröße setzen entweder explizit einJFrame.setSize(Breite, Höhe); oder implizit berechnet einJFrame.pack(); pack bestimmt die Größe selbständig aus der bevorzugten Größe der Komponenten Alle Komponenten möchten in einer bestimmten Größe erscheinen. Layout-Manager entscheiden über Anordnung und tatsächlich zugestandene Größe der Komponenten. Verschiedene Layout-Manager haben unterschiedliche Strategien zur Bestimmung von Layout, Größe der Komponenten und Einfluss des Programmcodes auf beides. Seite 18 JFrame f = new Jframe( ... ); f.setLayout( ... ); f.add(...); ... f.add(...); f.setSize( 200, 300 ); f.setVisible( true ); JFrame f = new Jframe( ... ); f.setLayout( ... ); f.add(...); ... f.add(...); f.pack(); f.setVisible( true ); Th Letschert Beispiel: Addier-GUI (1) Addierer: Komponenten Planung: Zwei Textfelder zur Eingabe Ein Knopf um die Addition zu starten Ein Textfeld zur Ausgabe JFrame JPanel Operand 1 Operand 2 + Ergebnis JTextField JButton Seite 19 Th Letschert Beispiel: Addier-GUI (2) Addierer / Layout-Plan Operand 1 Operand 2 + Ergebnis North JFrame: BorderLayout Center JFrame (BoarderLayout) South jp:JPanel (FlowLayout) JTextField JPanel FlowLayout op1TF JTextField op2TF op1TF resultTF JTextField op2TF resultTF JButton plusB plusB Komponenten / Layout Komponenten / hierarchische Struktur Seite 20 Th Letschert Beispiel: Addier-GUI (3) public class AddiererGui { public static void createGUI() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); JButton JTextField JTextField JTextField plusB op1TF op2TF resultTF = = = = new new new new JButton("+"); JTextField(10); JTextField(10); JTextField(10); North Center JPanel opPanel = new JPanel(); opPanel.setLayout( new FlowLayout() ); opPanel.add(op1TF); opPanel.add(op2TF); South frame.add(opPanel,BorderLayout.NORTH); frame.add(plusB,BorderLayout.CENTER); frame.add(resultTF,BorderLayout.SOUTH); resultTF frame.pack(); frame.setVisible(true); } op1TF } public static void main(String[] args){ createGUI(); } Seite 21 plusB op2TF Der Knopf macht sich im gesamten Zentrum breit. Igitt! Th Letschert Layout beeinflussen mit Labels und Panels Formatieren mit Labels und Panels Komponenten füllen ihren Platz aus: manchmal hässlich Mit zusätzlichem JPanel und (leeren) Labels kann formatiert werden JPanel JPanel JPanel JPanel op1TF op2TF resultTF op1TF resultTF plusB JPanel JPanel op2TF Knopf füllt das Zentrum völlig aus. Igitt! JLabel Seite 22 plusB Panel füllt das Zentrum völlig aus. OK! JLabel Th Letschert Layout beeinflussen mit Labels und Panels JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); JButton JTextField JTextField JTextField plusB op1TF op2TF resultTF = = = = new new new new JButton(" + "); JTextField(10); JTextField(10); JTextField(10); JPanel opPanel = new JPanel(); opPanel.setLayout( new FlowLayout() ); opPanel.add(op1TF); opPanel.add(op2TF); JPanel plusPanel = new JPanel(); plusPanel.setLayout( new FlowLayout() ); plusPanel.add( new JLabel() ); plusPanel.add( plusB ); plusPanel.add( new JLabel() ); frame.add(opPanel,BorderLayout.NORTH); frame.add(plusPanel,BorderLayout. CENTER ); frame.add(resultTF,BorderLayout.SOUTH); Zentrum: leerers Label, Knopf, leeres Label frame.pack(); frame.setVisible(true); Seite 23 Th Letschert Komponenten gestalten / Addier-GUI (4) Aussehen und Verhalten der Komponenten kann vielfältig beeinflusst werden ● ● ● Formatiertierte Textfelder Schriftart .... JButton JTextField plusB= new JButton(" + "); op1TF= new JFormattedTextField( new java.text.DecimalFormat()); JTextField op2TF= new JFormattedTextField( new java.text.DecimalFormat()); op1TF.setColumns(5); op2TF.setColumns(5); op1TF.setHorizontalAlignment(JTextField.RIGHT); op2TF.setHorizontalAlignment(JTextField.RIGHT); op1TF.setFont(new Font("monspaced", Font.PLAIN, 20)); op2TF.setFont(new Font("monspaced", Font.PLAIN, 20)); JTextField resultTF = new JTextField(10); resultTF.setFont(new Font("monspaced", Font.PLAIN, 20)); resultTF.setEditable(false); Seite 24 Nur Zahleingaben annehmen Schriftstil und Ausrichtung festlegen Eingaben in Ausgabefeld verhindern Swing-Tutorial / API-Doku (Sun) für solche „Tricks“ konsultieren! Th Letschert Quellcode-Organisation : GUI-Klasse definieren Klassen für GUI-Objekte definieren • statt ... main (...) { JFrame einFenster = new .... einFenster.add(eineKomponente); ..... • besser: JFrame-Klasse definieren, organisiert sich selbst class MeineGUIKlasse extends JFrame { private meineKomponenten... .... add(eineKomponente); ( oder this.add(eineKomponente); ) .... } ... MeineGUIKlasse einGUIObjekt = new MeineGUIKlasse(); Seite 25 Th Letschert Quellcode-Organisation / Addier-GUI (5/1) public class AddiererGUI extends JFrame { private JButton plusB; private JTextField op1TF; JFrame frame = new JFrame(); private JTextField op2TF; frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); private JTextField resultTF; frame.setLayout(new BorderLayout()); ... public AddiererGUI() { super("Addierer"); initView(); } kein JFrame erzeugen, Objekt selbst ist JFrame Konstruktor ruft initView initView erzeugt GUI-Elemente private void initView() { this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLayout(new BorderLayout()); plusB = new JButton(" + "); op1TF = new JFormattedTextField(new java.text.DecimalFormat()); op2TF = new JFormattedTextField(new java.text.DecimalFormat()); op1TF.setColumns(5); op2TF.setColumns(5); op1TF.setHorizontalAlignment(JTextField.RIGHT); op2TF.setHorizontalAlignment(JTextField.RIGHT); op1TF.setFont(new Font("monspaced", Font.PLAIN, 20)); op2TF.setFont(new Font("monspaced", Font.PLAIN, 20)); ···> Seite 26 Th Letschert Quellcode-Organisation / Addier-GUI (5/2) resultTF = new JTextField(10); resultTF.setFont(new Font("monspaced", Font.PLAIN, 20)); resultTF.setEditable(false); JPanel opPanel = new JPanel(); opPanel.setLayout( new FlowLayout() ); opPanel.add(op1TF); opPanel.add(op2TF); JPanel plusPanel = new JPanel(); plusPanel.setLayout( new FlowLayout() ); plusPanel.add( new JLabel() ); plusPanel.add( plusB ); plusPanel.add( new JLabel() ); this.add(opPanel,BorderLayout.NORTH); this.add(plusPanel,BorderLayout.CENTER); this.add(resultTF,BorderLayout.SOUTH); this.pack(); }// Ende initView }// Ende AddiererGUI Seite 27 Th Letschert Quellcode-Organisation / Addier-GUI (6) public class Addierer { public static void main(final String[] args){ AddiererGUI addierGUI = new AddiererGUI(); addierGUI.setVisible(true); } } Eine Hauptklasse erzeugt in main ein GUI-Objekt und aktiviert es (macht es sichtbar). Code-Organisation: Paket mit 2 Klassen: Addierer AddiererGUI Seite 28 addierer Hauptklasse Erzeugen, Kontrollieren GUI-Elemente Th Letschert