Skansholm: Kapitel 6-8
(GUI = grafiskt användargränssnitt)
Hur går det till när en användare interagerar med ett GUI?
Ett enkelt scenario:
Hur styr användaren kontrollflödet?
Användaren kan utföra olika typer av aktioner (till exempel musklick, musrörelser, tangentnedtryckningar). I Swing kallas sådana aktioner för händelser (engelska: events). Varje händelse representeras med ett objekt. Några exempel på klasser som används för att representera händelser:
AWTEvent
: Abstrakt klass. Gemensam för alla händelser.ActionEvent
: Subklass till AWTEvent
. Genereras till exempel när
användaren klickar på en knapp som visas på skärmen.KeyEvent
: Svarar mot att (exempelvis) en tangent trycks nedMouseEvent
: Alla dessa aktioner kommer att resultera i en händelse:
En musknapp
a) trycks ned,
b) släpps upp,
c) klickas (knappen trycks ned och släpps upp omedelbart),
d) flyttas eller
e) flyttas med en knapp nedtryckt (dragged).
När användaren gör något, tex klickar med musen på ett fönster, skapas ett händelseobjekt. Swing-systemet letar efter en händelselyssnare som kan hantera händelsen.
Händelselyssnare kan kopplas till olika komponenter. Om användaren klickar på en komponent kollar systemet först om den komponenten har en händelselyssnare, sen om den behållare som komponenten ingår i har en lyssnare etc. Till sist kollar systemet om fönstret har en händelselyssnare.
När systemet har hittat en lyssnare anropas händeselyssnaren med ett
meddelande, tex actionPerformed()
,
Några olika typer av händelselyssnare.
ActionListener
— gränssnitt. En klass som hanterar ActionEvents
måste implementera detta gränssnitt.MouseListener
— gränssnitt.
Används om man vill fånga upp mushändelser.MouseAdapter
— abstrakt klass.
Implementerar MouseListener
.
Definierar tomma metoder för alla metoder i MouseListener
.
Notera att ActionListener
och MouseListener
är gränssnitt
(interface). Det innebär att i princip vilken klass som helst kan
deklareras att implementera ActionListener
eller MouseListener
. Därmed
kan instanserna av den klassen agera händelselyssnare. Man kan till
exempel låta en klass som implementerar ett fönster vara sin egen
händelselyssnare.
Så om vi vill hantera händelser gör vi så här:
ActionListener.
ActionListener
är det endast en metod som måste definieras:
actionPerformed
. Metoder av denna typ kallas ibland callbacks.I programmet: program/swing-2/Inmatning2.java låter vi klassen
Inmatning2
ärva av JFrame
och implementera ActionListener
.
Klassen Inmatning2
har två instansvariabler, en knapp pressme
och ett
textfält answer
.
Konstruktorn för Inmatning2
placerar knappen och textfältet på
fönstret. Inget konstigt här.
Sen har vi något nytt. Vi anger att fönstret självt, "this
", ska vara
knappens händelselyssnare: pressme.addActionListener(this)
För att detta ska vara kompilerbart krävs att fönstret implementerar
gränssnittet ActionListener
. Detta kräver i sin tur att det finns en
metod actionPerformed(ActionEvent e)
definierad för fönstret.
Så vi definierar en metod actionPerformed(ActionEvent event)
. Metoden
ändrar texten i textfältet och slänger upp en dialog. Inte så
spännande. Notera också att metoden tar fram den Swing-komponent där
händelsen inträffade med event.getSource()
och jämför den med
knappen. I en större applikation kan man ha många knappar. Om man
låter en händelselyssnare ta hand om flera knappar kan man använda
getSource()
för att avgöra vilken komponent användaren klickade på.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Inmatning2 extends JFrame implements ActionListener { JButton pressme; JTextField answer; Inmatning2() { super("Inmatning 2"); JPanel pane = new JPanel(); JLabel prompt = new JLabel(" Enter a number: "); answer = new JTextField ("", 5); pressme = new JButton("Press Me"); answer.requestFocus(); pressme.addActionListener(this); pane.add(prompt); pane.add(answer); pane.add(pressme); add(pane); setBounds(100,100,300,200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } public void actionPerformed(ActionEvent event) { if (event.getSource() == pressme) { answer.setText("Button pressed!"); JOptionPane.showMessageDialog(null,"I hear you!"); setVisible(true); } } public static void main(String[] args) { new Inmatning2(); } }
Några typiska attribut hos händelser:
Vi har en händelse e
(exempelvis av klassen MouseEvent
)
e.getX()
, e.getY()
: koordinatere.clickCount()
: antal musklicke.getComponent()
: den komponent som musen befinner sig iEn tangenttryckning e
av klassen KeyEvent. Typiska attribut:
e.getKeyCode()
: vilken tangent på tangentbordete.getKeyChar()
: vilket tecken svarar det mote.getComponent()
: vilken komponentLåt oss undersöka hur olika händelser registreras. Programmet program/swing-2/HandelseTest.java listas längre ned.
Här definierar vi händelselysnarna som separata klasser. Klassen
Muslyssnare
ärver av MouseAdapter
och definierar en metod
mouseClicked
. Metoden skriver ut positionen för klicket.
På samma sätt ärver ärver klassen Tangenlyssnare
av KeyAdapter
. Här
definieras en metod keyTyped
som talar om vilken tangent det var.
Och det är väl nästan hela historien. Konstruktorn för klassen
Handelsetest
skapar en instans av klassen Muslyssnare
och adderar den
med anropet addMouseListener
och skapar sen en instans av klassen
TangentLyssnare
och adderar den med addKeyListener
.
import javax.swing.*; import java.awt.event.*; class Muslyssnare extends MouseAdapter { public void mouseClicked(MouseEvent e) { System.out.println("Klick vid ("+e.getX() + "," + e.getY()+")"); } } class Tangentlyssnare extends KeyAdapter { public void keyTyped(KeyEvent e) { System.out.println("Tangenenten "+ e.getKeyChar()); } } class HandelseTest extends JFrame { MouseListener l1 = new Muslyssnare(); KeyListener l2 = new Tangentlyssnare(); HandelseTest (){ addMouseListener(l1); addKeyListener(l2); setSize(400,400); setVisible(true); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main (String[] args ){ HandelseTest h = new HandelseTest(); } }
Om du testkör programmet ser det inte så spännande ut. Prova att klicka på fönstret och observera utskriften. Så här kan det se ut:
$ javac HandelseTest.java $ java HandelseTest Klick vid (9,37) Klick vid (373,42) Klick vid (359,363) Klick vid (24,362) Klick vid (152,192) Tangenenten h Tangenenten e Tangenenten j $
Javas API definierar två gränssnitt för muslyssnare; MouseListener
och
MouseMotionListener
.
Gränssnittet MouseListener
deklarerar följande sex metoder:
mouseClicked void mouseClicked(MouseEvent e)
anropas när en musknapp klickas på en komponent
mousePressed void mousePressed(MouseEvent e)
anropas när en musknapp trycks ner på en komponent
mouseReleased void mouseReleased(MouseEvent e)
anropas när musknappen släpps upp
mouseEntered void mouseEntered(MouseEvent e)
anropas när pekaren kommer in över en komponent
mouseExited void mouseExited(MouseEvent e)
anropas när pekaren lämnar en komponent.
På samma sätt definierar gränssnittet MouseMotionListener
dessa två
metoder:
mouseDragged(MouseEvent e)
anropas när en musknapp tryckts ner över en komponent och muspekaren flyttas med knappen nertryckt
void mouseMoved(MouseEvent e)
anropas när musen rörs över en komponent utan att någon knapp är nertryckt.
jag kräver inte att ni ska hålla alla dessa metoder i minnet. Jag rekommenderar dock att ni tar er tid att reflektera lite över de olika metoderna.
Jag har skrivit ett litet testprogram program/swing-2/MouseTest.java för att göra det lättare att få grepp om musinteraktionen.
import java.awt.*; import java.awt.event.*; import javax.swing.*; class MouseTestListener implements MouseListener, MouseMotionListener { // Metoder ur MouseListener public void mouseClicked(MouseEvent e) { System.out.println("Clicked "+e.getPoint()); } public void mouseEntered(MouseEvent e) { System.out.println("Entered "+e.getPoint()); } public void mouseExited(MouseEvent e) { System.out.println("Exited "+e.getPoint()); } public void mousePressed(MouseEvent e) { System.out.println("Pressed "+e.getPoint()); } public void mouseReleased(MouseEvent e) { System.out.println("Released "+e.getPoint()); } // Metoder ur MouseMotionListener public void mouseDragged(MouseEvent e) { System.out.println("Dragged "+e.getPoint()); } public void mouseMoved(MouseEvent e) { System.out.println("Moved "+e.getPoint()); } } public class MouseTest extends JFrame { public MouseTest() { setTitle("Mouse test"); JPanel canvas = new JPanel(); MouseTestListener listener = new MouseTestListener(); canvas.addMouseListener(listener); canvas.addMouseMotionListener(listener); canvas.setBackground(Color.green); getContentPane().add(canvas); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { int width = 600; int height = 400; JFrame frame = new MouseTest(); frame.setSize(width, height); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); frame.setLocation(screenSize.width/2 - width/2, screenSize.height/2 - height/2); frame.setVisible(true); } }
Som ni ser är programmet mycket enkelt; vi skapar en klass som både
implementerar MouseListener
och MouseMotionListener
. Varje typ av
händelse ger oss en utskrift; tex
System.out.println("Clicked "+e.getPoint());
om användaren klickar med musen på fönstret.
När man testkör programmet måste man vara lite försiktig; en snabb rörelse över fönstret kan ge ett hundratal utskrifter.
Entered java.awt.Point[x=0,y=329] Moved java.awt.Point[x=1,y=329] Moved java.awt.Point[x=2,y=329] Moved java.awt.Point[x=3,y=329] Moved java.awt.Point[x=4,y=329] Moved java.awt.Point[x=4,y=330] Moved java.awt.Point[x=4,y=331] Moved java.awt.Point[x=5,y=331] Moved java.awt.Point[x=5,y=332] Pressed java.awt.Point[x=5,y=332] Dragged java.awt.Point[x=6,y=332] ... Dragged java.awt.Point[x=11,y=336] Dragged java.awt.Point[x=12,y=336] Released java.awt.Point[x=12,y=336] Moved java.awt.Point[x=11,y=336] Moved java.awt.Point[x=10,y=336] Moved java.awt.Point[x=11,y=336] Moved java.awt.Point[x=12,y=336] Moved java.awt.Point[x=13,y=336] Moved java.awt.Point[x=14,y=336] Pressed java.awt.Point[x=14,y=336] Released java.awt.Point[x=14,y=336] Clicked java.awt.Point[x=14,y=336] Pressed java.awt.Point[x=14,y=336] Released java.awt.Point[x=14,y=336] Clicked java.awt.Point[x=14,y=336] Moved java.awt.Point[x=13,y=336] Moved java.awt.Point[x=12,y=336] ... Moved java.awt.Point[x=1,y=341] Moved java.awt.Point[x=0,y=341] Exited java.awt.Point[x=-1,y=341]
Här rörde jag musen försiktigt över nedre vänstra hörnet, ändå blev
utskriften lång. Prova själv! Om du vill kan du ta bort utskriften av
mouseMoved()
, det blir mycket utskrift ändå.