Upload
doanque
View
230
Download
1
Embed Size (px)
Citation preview
GUIs in Java und ihre Programmierung
Einführung
● GUI = Graphical User Interface – graphische Benutzerschnittstelle
● Java Tutorial– java.sun.com/docs/books/tutorial/index.html
● Java Documentation (in Version 1.4.2 oder 1.5)– java.sun.com/j2se/1.4.2/docs/api/index.html
– java.sun.com/j2se/1.5/docs/api/index.html
● Oder als Link auf der Java-Homepage – java.sun.com
Java-Documentation
AWT
● AWT = Abstract Window Toolkit● import java.awt.*;
● alle wichtigen Komponenten enthalten (Frames, Buttons, ...)
● Benutzen des Event-Handling-Konzepts, wie es der Window-Manager anbietet
● aber: viele Bugs, Zusammenarbeit mit Window-Manager mangelhaft, langsam
JFC/Swing
● JFC = Java Foundation Classes
● wird hier ausschließlich benutzt
● import javax.swing.*;
● Bietet mehr Komponenten als AWT, z.B. PopUps, ToolTips, Tables
● Bietet zusätzliche Konzepte
– Look-And-Feel
– Drag-And-Drop
– Internationalization
● unterstützt Computertechnik für behinderte Menschen (Braille-Displays, ...)
Der Einstieg – das erste Fenster
import javax.swing.*;
class FirstWindow {
public static void main( String args[] ) {
} }
JFrame frame = new JFrame("OurFirstWindow");
frame.pack(); frame.setVisible(true);
Aussehen und Verbesserungen
● Jedes Fenster hat i.A. einen Rahmen (Border), eine Titelleiste und eine Zeichenfläche (Pane)
● Verbesserungen:– Look-and-feel
– Java-Programm beim Schließen des Fensters auch beenden
Erweiterung/Verbesserung
● Das Aussehen aller Fenster auf das javainterne Look-and-Feel setzen– JFrame.setDefaultLookAndFeelDecorated(true);
● Beim Schließen des Fensters auch das Java-Programm beenden– frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Das komplette Programmimport javax.swing.*;
class FirstWindow {
public static void main( String args[] ) {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("OurFirstWindow");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
...
frame.pack();frame.setVisible(true);
} }
Design von awt und swing
● Container vs. Components
● 3 top-level Container existieren:
– Frame
– Dialog
– Applet
● Alle anderen Window-Elemente sind low-level und heißen Components
– Componenten sind nicht stand-alone, sondern werden in die Container eingefügt und angezeigt
– Panel (kann andere Components aufnehmen)
– Label, Button, Checkbox, ...
– TextArea, TextField, ...
Beispiele für Components
● LabelJLabel label = new JLabel("Hello World");frame.getContentPane().add(label);
● ButtonJButton button = new JButton("Press Me");frame.getContentPane().add(button);
● CheckBoxJCheckBox cb = new JCheckBox("Choose Me");frame.getContentPane().add(cb);
● TextFieldJTextField tf = new JTextField("Write Me");frame.getContentPane().add(tf);
Problem
● Nachfolgende Componenten überschreiben schon vorhandene
● 2 Lösungen möglich: – Layout-Manager
– Panel● Ist eine besondere Componente, weil sie andere Components
aufnehmen kann● Dieses Panel kann dann dem Frame hinzugefügt werden,
weil es selbst eine Componente ist.
Layout-Manager
● Jedes Pane (und damit das Frame, welchesdiese Pane enthält) hat ein eigenes Aussehen,über das der implementierte Layout-Manager entscheidet.
● BorderLayoutdefault für alle neuen Frames; 5 Felder, indenen die Components angezeigt werden
● FlowLayoutdie Components werden in einer Reihe angeordnet (default für alle neuen Panel)
● GridLayoutalle Components bekommen dieselbe Größe und liegen in Reihen und Spalten
● Weitere Layouts: BoxLayout, CardLayout, GridBagLayout, SpringLayout (siehe Java-Tutorial)
Verwendung BorderLayout● Importiere awt: import java.awt.*;
● Die definierten Components ... JLabel lab = new JLabel("Message");
JCheckBox cb = new JCheckBox("Choose Me");JRadioButton rb = new JRadioButton("Choose Radio");JTextField tf = new JTextField("Einzeilig");JTextArea ta = new JTextArea("Return erlaubt");
● ... mit der überladenen add-methode einfügen frame.getContentPane().add(lab, BorderLayout.CENTER);
frame.getContentPane().add(cb, BorderLayout.LINE_START);frame.getContentPane().add(rb, BorderLayout.LINE_END);frame.getContentPane().add(tf, BorderLayout.PAGE_START);frame.getContentPane().add(ta, BorderLayout.PAGE_END);
● Zum Ausprobieren (setzt Abstände zwischen Components) void setHgap(int)
void setVgap(int)
Weiteres Problem
● Wie kann ich mehr als 5 Components in das BorderLayout einfügen?
● Lösung:– Layout verändern, z.B. durch
frame.getContentPane().setLayout(new GridLayout(0, 1, 10, 10) );
bzw. Container pane = frame.getContentPane();
pane.setLayout( new BoxLayout( pane, BoxLayout.Y_AXIS ) );
– Panel
Arbeiten mit Panels
● Panel erstellen (hat als Default ein FlowLayout)JPanel panel = new JPanel();
● In das Panel die Components einfügenpanel.add(new JLabel(“Read me”));panel.add(new JButton(“Click me”));panel.add(new JRadioButton(“Mark me”));
● Das Panel dem Frame übergebenframe.getContentPane().add(panel);
● Note: Panels können zu Panels hinzugefügt werden
JLabel lab1 = new JLabel("Insert one line of text:");JTextField tf = new JTextField();JLabel lab2 = new JLabel("Insert some lines of text");JTextArea ta = new JTextArea();JScrollPane sp = new JScrollPane(ta);JCheckBox cb1 = new JCheckBox("Use sinus");JCheckBox cb2 = new JCheckBox("Use square root");JButton but1 = new JButton("Cancel");JButton but2 = new JButton("Compute");JButton but3 = new JButton("Exit");
frame.getContentPane().setLayout( new GridLayout( 0, 1 ));
JPanel panel1 = new JPanel( new FlowLayout() );JPanel panel2 = new JPanel( new FlowLayout() );
panel1.add(cb1);panel1.add(cb2);
panel2.add(but1);panel2.add(but2);panel2.add(but3);
frame.getContentPane().add(lab1);frame.getContentPane().add(tf);frame.getContentPane().add(lab2);frame.getContentPane().add(sp);frame.getContentPane().add(panel1);frame.getContentPane().add(panel2);
GUI-Events
● Problem: wie bemerke ich, dass der User einen Button betätigt, oder eine bestimmte Taste drückt, oder dass die Mouse sich über einem Icon befindet, dass ich daraufhin ändern möchte?
● Lösung 1: unser Programm fragt ständig alle möglichen Eingabequellen (Maus, Tastatur, ...) ab, und sucht sich die Ereignisse heraus, auf die es reagieren will
– Zeitaufwendig
– Codeaufwendig
● Lösung 2: im Hintergrund läuft das Betriebssystem und der Window-Manager, die sich sowieso schon um alle Eingabegeräte kümmern, sowie die GUI verwalten, also sollen die uns doch sagen, was wir wissen wollen
– Einfach, praktikabel
– Wir registrieren uns bei der GUI für bestimmte Events
Was für Events gibt es?
Act that Results in the Event Listener Type
ActionListener User closes a frame (main window) WindowListener
MouseListener User moves the mouse over a component MouseMotionListener Component becomes visible ComponentListener Component gets the keyboard focus FocusListener Table or list selection changes ListSelectionListener
PropertyChangeListener
User clicks a button, presses Enter while typing in a text field, or chooses a menu item
User presses a mouse button while the cursor is over a component
Any property in a component changes such as the text on a label
Wie benutze ich Events?
● Alle “Listener” sind Interfaces. Ein Listener kann mehrere Events enthalten, mit je einer Methode.
● Bsp: MouseListener enthält– MouseClicked über einer Component wurde eine Maustaste betätigt
– MouseEntered der User bewegt den Mauscursor über eine Componente
– MouseExited der Mauscursor verläßt die Componente
– MousePressed ein Mausbutton wurde über einer Componente niedergedrückt
– MouseReleased ein Mausbutton wurde über einer Componente losgelassen
● Ich brauche also eine “Class”, die das Interface (und die Methoden daraus) implementiert
● Dann kann diese Class sich bei der Componente registrieren, welche die entsprechenden Methoden aufruft
Event-Beispiel
● Ich will auf einen Button-click reagieren, also brauche ich den ActionListener in meiner “Class”: import java.awt.event.*;
class FirstWindow implements ActionListener ...
● Zum Interface ActionListener gehört nur eine Methode: public void actionPerformed(ActionEvent e) {
System.out.println(“Button clicked!”);}
● Und ich muss die Class beim Button registrieren, damit der Button auch die entsprechende Methode aufruft: z.B. button.addActionListener( new FirstWindow() );
button.addActionListener( this );button.addActionListener( someClassVariable );
import javax.swing.*;import java.awt.*;import java.awt.event.*;
class FirstWindow implements ActionListener {
JButton button1; JButton button2; public void actionPerformed(ActionEvent e) {
JButton button = (JButton)e.getSource();if (button == button1) button = button2;else button = button1;int i = 1+Integer.parseInt(button.getText());button.setText( ""+i );
} public void createGUI() {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("OurFirstWindow");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button1 = new JButton("1");button2 = new JButton("10");button1.addActionListener( this );button2.addActionListener( this );
frame.getContentPane().add( button1, BorderLayout.PAGE_START );frame.getContentPane().add( button2, BorderLayout.PAGE_END );
frame.pack();frame.setVisible(true);
}
public static void main( String args[] ) {FirstWindow fw = new FirstWindow();fw.createGUI();
}}
Minimale Registrierung
● Wenn ich in meiner Class kein Interface implementieren will, kann ich auch beim Button eine minimale Class, die ich adhoc erstelle, registrieren: button.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent e) { ...button.setText(“You have clicked me”);...
} } );
● Sollte nur bei wenig Programmcode für den ActionListener benutzt werden, weil schnell unübersichtlich
● Vorteil: jede Component ruft ihren eigenen ActionListener auf (kann man auch erreichen, indem man bei jeder Component eine eigene Class registriert)
Selbst zeichnen leichgemacht
● In einem bestimmten Ausschnitt unseres Panel wollen wir selbst zeichnen
● In Panel's können nur Components eingefügt werden, also definieren wir unsere eigene Component: class MyComp extends JComponent {
}
● Dann überschreiben wir die Methode paintComponent, über die alle Components gezeichnet werden: protected void paintComponent(Graphics g) {
}
● Jetzt können wir alle Methoden von JComponent und Graphics nutzen, um unser Object zu zeichnen
Dimensionen
● Unser Object braucht noch eine bevorzugte Breite (height) und Höhe (width), damit es gleich von Anfang an dargestellt wird:
public Dimension getPreferredSize() {return new Dimension(400,200);
}
● Koordinatensystemzum Zeichnen
Wichtige Methoden zum Zeichnen
● Aus JComponent:getInsets() liefert die Breiten der Rahmenseiten (Border)getWidth() liefert die zeichenbare Breite inkl. BordergetHeight() liefert die zeichenbare Höhe inkl. Border
● Aus Graphics:drawLine(x1,y1,x2,y2) zeichnet eine LiniedrawRect(x,y,w,h) zeichnet eine Rechteckdraw... Methoden zum Malen (auch Text)fillRect(x,y,w,h) füllt das Rechteckfill... Methoden zum FüllensetColor(color) setzt die Farbe für das nächste
zu zeichnende Element
import javax.swing.*;import java.awt.*;
class FirstWindow {
class MyComp extends JComponent {
public Dimension getPreferredSize() {return new Dimension(400,200);
}
protected void paintComponent(Graphics g) {g.setColor( Color.RED );g.drawLine( 0, 0, getWidth(), getHeight() );g.setColor( Color.WHITE );g.drawLine( 0, getHeight(), getWidth(), 0 );
} } public void createGUI() {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("OurFirstWindow");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add( new MyComp() );
frame.pack();frame.setVisible(true);
}
public static void main( String args[] ) {FirstWindow fw = new FirstWindow();fw.createGUI();
}}
● Threads sind Prozesse, die zeitgleich (konkurrierend) neben dem main-Programm ausgeführt werden
● Sie sind nicht stand-alone, sondern immer vom Mutterprozess abhängig, der sie starten und kontrollieren muss.
● Wichtig: Swing-event- und Component-painting-code laufen in einem Thread ab, dem
Event-dispatching thread
Threads
Thread-Safety
● Beispiel für einen Konflikt: eine Componente wurde noch nicht gezeichnet, wird aber zur selben Zeit von Außerhalb (alles was nicht der event-dispatching thread ist) so verändert, dass ein Update fällig wäre, obwohl doch noch nichts geupdated werden kann
● Probleme zwar sehr selten, sind trotzdem zu vermeiden
● Programmcode, wie die Probleme vermieden werden können: public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {createAndShowGUI();
}}
); }