GUI-Programmierung

Werbung
GUIs in Java und ihre
Programmierung
Einführung
●
GUI = Graphical User Interface
–
●
Java Tutorial
–
●
●
graphische Benutzerschnittstelle
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 WindowManager 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 (BrailleDisplays, ...)
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 Lookand-Feel setzen
–
●
JFrame.setDefaultLookAndFeelDecorated(true);
Beim Schließen des Fensters auch das JavaProgramm beenden
–
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Das komplette Programm
import 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
●
Label
JLabel label = new JLabel("Hello World");
frame.getContentPane().add(label);
●
Button
JButton button = new JButton("Press Me");
frame.getContentPane().add(button);
●
CheckBox
JCheckBox cb = new JCheckBox("Choose Me");
frame.getContentPane().add(cb);
●
TextField
JTextField 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, welches
diese Pane enthält) hat ein eigenes Aussehen,
über das der implementierte Layout-Manager
entscheidet.
BorderLayout
default für alle neuen Frames; 5 Felder, in
denen die Components angezeigt werden
FlowLayout
die Components werden in einer Reihe angeordnet
(default für alle neuen Panel)
GridLayout
alle Components bekommen dieselbe Größe
und liegen in Reihen und Spalten
Weitere Layouts: BoxLayout, CardLayout, GridBagLayout,
SpringLayout (siehe Java-Tutorial)
Verwendung BorderLayout
●
Importiere awt:
●
Die definierten Components ...
import java.awt.*;
JLabel
JCheckBox
JRadioButton
JTextField
JTextArea
●
lab
cb
rb
tf
ta
=
=
=
=
=
new
new
new
new
new
JLabel("Message");
JCheckBox("Choose Me");
JRadioButton("Choose Radio");
JTextField("Einzeilig");
JTextArea("Return erlaubt");
... mit der überladenen add-methode einfügen
frame.getContentPane().add(lab,
frame.getContentPane().add(cb,
frame.getContentPane().add(rb,
frame.getContentPane().add(tf,
frame.getContentPane().add(ta,
●
BorderLayout.CENTER);
BorderLayout.LINE_START);
BorderLayout.LINE_END);
BorderLayout.PAGE_START);
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ügen
panel.add(new JLabel(“Read me”));
panel.add(new JButton(“Click me”));
panel.add(new JRadioButton(“Mark me”));
●
Das Panel dem Frame übergeben
frame.getContentPane().add(panel);
●
Note: Panels können zu Panels hinzugefügt
werden
JLabel
JTextField
JLabel
JTextArea
JScrollPane
JCheckBox
JCheckBox
JButton
JButton
JButton
lab1
tf
lab2
ta
sp
cb1
cb2
but1
but2
but3
=
=
=
=
=
=
=
=
=
=
new
new
new
new
new
new
new
new
new
new
JLabel("Insert one line of text:");
JTextField();
JLabel("Insert some lines of text");
JTextArea();
JScrollPane(ta);
JCheckBox("Use sinus");
JCheckBox("Use square root");
JButton("Cancel");
JButton("Compute");
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
User clicks a button, presses Enter while typing
in a text field, or chooses a menu item
User closes a frame (main window)
User presses a mouse button while the cursor is
over a component
User moves the mouse over a component
Component becomes visible
Component gets the keyboard focus
Table or list selection changes
Any property in a component changes such as
the text on a label
Listener Type
ActionListener
WindowListener
MouseListener
MouseMotionListener
ComponentListener
FocusListener
ListSelectionListener
PropertyChangeListener
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);
}
●
Koordinatensystem
zum Zeichnen
Wichtige Methoden zum Zeichnen
●
●
Aus JComponent:
getInsets()
getWidth()
getHeight()
liefert die Breiten der Rahmenseiten (Border)
liefert die zeichenbare Breite inkl. Border
liefert die zeichenbare Höhe inkl. Border
Aus Graphics:
drawLine(x1,y1,x2,y2)
drawRect(x,y,w,h)
draw...
fillRect(x,y,w,h)
fill...
setColor(color)
zeichnet eine Linie
zeichnet eine Rechteck
Methoden zum Malen (auch Text)
füllt das Rechteck
Methoden zum Füllen
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
●
●
●
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-paintingcode laufen in einem Thread ab, dem
Event-dispatching thread
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 eventdispatching 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();
}
}
); }
Herunterladen