Indhold:
Forstå hændelser og lyttere
Abonnere på hændelser
Forudsættes af kapitel 21, Avancerede klasser.
Forudsætter kapitel 11, Grafiske standardkomponenter
og 12, Interfaces.
I eksemplerne anvendes appletter, beskrevet i
kapitel 10, Appletter.
Hændelser (eng.: events) spiller en stor rolle i programmering af grafiske brugergrænseflader. Når brugeren foretager en handling, f.eks. bevæger musen, klikker, trykker en knap ned, ændrer i et tekstfelt osv., opstår der en hændelse. I Java er alle hændelser objekter (af typen Event) med metoder til at undersøge de præcise detaljer omkring hændelsen.
Hændelser udsendes af de grafiske komponenter (knapper, vinduer osv.), og hvis man vil behandle en bestemt type hændelser fra en bestemt grafisk komponent, skal man "lytte" efter den hændelse. Det gøres ved at registrere en lytter (eng.: listener) på komponenten.
Når en lytter til en bestemt slags hændelser er registreret hos en komponent, bliver der kaldt en metode på lytteren, når den pågældende slags hændelser indtræffer (f.eks. kaldes mouseClicked(), når der klikkes med musen). For at sikre, at lytteren har den pågældende metode, skal lytter-objektet implementere et interface, der garanterer, at det har metoden.
Eksempel: Grafiske vinduer (Frame) kan udsende hændelser af typen MouseEvent. Klassen Frame har derfor metoden addMouseListener(MouseListener lytter), der kan bruges til at registrere lytter-objekter i vinduet. Det er kun objekter af typen MouseListener, der kan registreres som lyttere. MouseListener er et interface, så man skal lave en klasse, der implementerer MouseListener, og skabe lytter-objekter ud fra dette. Når brugeren f.eks. klikker med musen i vinduet, udsender det en MouseEvent-hændelse til alle lytter-objekter, der er blevet registreret vha. addMouseListener(). Det gør vinduet ved at kalde metoden mouseClicked(MouseEvent hændelse) på lytter-objekterne.
Herunder definerer vi klassen Muselytter, der implementerer MouseListener og skriver ud til skærmen, hver gang der sker noget med musen.
import java.awt.*; import java.awt.event.*; public class Muselytter implements MouseListener { public void mousePressed(MouseEvent hændelse) // kræves af MouseListener { Point trykpunkt = hændelse.getPoint(); System.out.println("Mus trykket ned i "+trykpunkt); } public void mouseReleased(MouseEvent hændelse) // kræves af MouseListener { Point slippunkt = hændelse.getPoint(); System.out.println("Mus sluppet i "+slippunkt); } public void mouseClicked(MouseEvent hændelse) // kræves af MouseListener { System.out.println("Mus klikket i "+hændelse.getPoint()); } //-------------------------------------------------------------------- // Ubrugte hændelser (skal defineres for at implementere MouseListener) //-------------------------------------------------------------------- public void mouseEntered (MouseEvent event) {} // kræves af MouseListener public void mouseExited (MouseEvent event) {} // kræves af MouseListener }
Lad os nu lave et grafisk objekt, der:
Opretter et muselytter-objekt.
Registrerer lytter-objektet, så det får kaldt sine metoder, når der sker noget med musen.
For kortheds skyld bruger vi appletter i de følgende eksempler. Vi kunne lige så godt have lavet grafiske vinduer (arvet fra Frame) og defineret main()-metoder, der åbner vinduerne.
import java.applet.*; public class LytTilMusen extends Applet { public LytTilMusen() { Muselytter lytter = new Muselytter(); this.addMouseListener(lytter); // this er appletten selv } }
Uddata fra appletten kan ses i Java-konsolvinduet i netlæseren (i Netscape findes den under: Tasks/Tools/Java Console, i Opera under: Windows/Other/Java):
Mus trykket ned i java.awt.Point[x=132,y=209] Mus sluppet i java.awt.Point[x=139,y=251] Mus trykket ned i java.awt.Point[x=101,y=199] Mus sluppet i java.awt.Point[x=101,y=199] Mus klikket i java.awt.Point[x=101,y=199]
Det foregående eksempel giver ikke appletten besked om, at der er sket en hændelse. Det har man brug for, hvis man f.eks. vil tegne noget i appletten.
Herunder er et eksempel, hvor lytter-objektet (Linielytter) giver informationer om klik videre til appletten (Linietegning), sådan at en blå linie tegnes mellem det punkt, hvor man trykkede museknappen ind, og det punkt, hvor man slap museknappen.
import java.awt.*; import java.awt.event.*; import java.applet.*; public class Linietegning extends Applet { public Point trykpunkt; public Point slippunkt; public Linietegning() { Linielytter lytter = new Linielytter(); lytter.appletten = this; // initialiserer lytterens reference til appletten this.addMouseListener(lytter); } public void paint(Graphics g) { g.drawString("1:"+trykpunkt+" 2:"+slippunkt,10,10); if (trykpunkt != null && slippunkt != null) { g.setColor(Color.blue); g.drawLine(trykpunkt.x, trykpunkt.y, slippunkt.x, slippunkt.y); } } }
Lytteren skal give appletten besked om klik vha. applettens to variabler, trykpunkt og slippunkt.
Derfor er Linielytter nødt til at have en reference (af type Linietegning) til appletten:
import java.awt.*; import java.awt.event.*; import java.applet.*; public class Linielytter implements MouseListener { public Linietegning appletten; // Reference til appletten public void mousePressed(MouseEvent hændelse) // kræves af MouseListener { appletten.trykpunkt = hændelse.getPoint(); } public void mouseReleased(MouseEvent hændelse) // kræves af MouseListener { appletten.slippunkt = hændelse.getPoint(); appletten.repaint(); // Gentegn appletten lige om lidt. } //-------------------------------------------------------------------- // Ubrugte hændelser (skal defineres for at implementere interfacet) //-------------------------------------------------------------------- public void mouseClicked(MouseEvent event) {} // kræves af MouseListener public void mouseEntered (MouseEvent event) {} // kræves af MouseListener public void mouseExited (MouseEvent event) {} // kræves af MouseListener }
Med linien
appletten.repaint();
fortæller vi Linietegning-appletten, at den skal gentegne sig selv. Det forårsager kort efter et kald til dens paint()-metode.
Herunder er Linietegning igen, men nu som en applet, der selv implementerer MouseListener.
Det er linien
this.addMouseListener(this);
der registrerer applet-objektet selv som lytter.
import java.applet.*; import java.awt.*; import java.awt.event.*; public class Linietegning2 extends Applet implements MouseListener { private Point trykpunkt; private Point slippunkt; public Linietegning2() { this.addMouseListener(this); } public void paint(Graphics g) { g.drawString("1:"+trykpunkt+" 2:"+slippunkt,10,10); if (trykpunkt != null && slippunkt != null) { g.setColor(Color.blue); g.drawLine(trykpunkt.x, trykpunkt.y, slippunkt.x, slippunkt.y); } } public void mousePressed(MouseEvent hændelse) // kræves af MouseListener { trykpunkt = hændelse.getPoint(); } public void mouseReleased(MouseEvent hændelse) // kræves af MouseListener { slippunkt = hændelse.getPoint(); repaint(); } //-------------------------------------------------------------------- // Ubrugte hændelser (skal defineres for at implementere interfacet) //-------------------------------------------------------------------- public void mouseClicked(MouseEvent event) {} // kræves af MouseListener public void mouseEntered (MouseEvent event) {} // kræves af MouseListener public void mouseExited (MouseEvent event) {} // kræves af MouseListener }
Bemærk, at nu kan vores trykpunkt og slippunkt-variabler være private i stedet for public, fordi de ikke behøver at være tilgængelige udefra.
Ovenfor har vi brugt MouseListener som illustration. Her vil vi give eksempler på brug af de andre typer lyttere (beskrevet i appendiks senere i kapitlet).
Med MouseMotionListener får man adgang til hændelserne mouseMoved og mouseDragged. Det kan bruges til at tegne grafiske figurer ved at hive musen hen over skærmen.
Her er en applet til at tegne kruseduller. Vi husker punktet, når musen trykkes ned (mousePressed()), og tegner en linie fra forrige punkt til musen, når den trækkes med nedtrykket knap (mouseDragged()).
import java.applet.*; import java.awt.*; import java.awt.event.*; public class Kruseduller extends Applet implements MouseListener, MouseMotionListener { public Kruseduller() { this.addMouseListener(this); this.addMouseMotionListener(this); } Point punkt; public void mousePressed(MouseEvent hændelse) // kræves af MouseListener { punkt = hændelse.getPoint(); } public void mouseDragged(MouseEvent hændelse) // kræves af MouseMotionListener { Point gammeltPunkt = punkt; punkt = hændelse.getPoint(); Graphics g = getGraphics(); g.drawLine(gammeltPunkt.x, gammeltPunkt.y, punkt.x, punkt.y); } public void mouseReleased (MouseEvent hændelse){} // kræves af MouseListener public void mouseClicked (MouseEvent event) {} // kræves af MouseListener public void mouseEntered (MouseEvent event) {} // kræves af MouseListener public void mouseExited (MouseEvent event) {} // kræves af MouseListener public void mouseMoved (MouseEvent hændelse){} //kræves af MouseMotionListener }
Her sker tegningen af grafikken direkte i håndteringen af hændelsen. Da vi ikke husker de gamle punkter, kan vi ikke gentegne krusedullen, hvis systemet kalder paint().
Det vigtigste interface til programmering af grafiske brugergrænseflader er ActionListener med metoden actionPerformed(). Den bruges bl.a. til at lytte til, om knapper bliver trykket på. Her er et eksempel, hvor den tekst, der er valgt med musen i et tekstområde, bliver kopieret til det andet tekstområde, når man trykker på knappen.
import java.applet.*; import java.awt.*; import java.awt.event.*; public class LytTilKnap extends Applet implements ActionListener { private TextArea t1, t2; private Button kopierKnap; public LytTilKnap() { String s = "Her er en tekst.\nMarkér noget af\nden og tryk\nKopier..."; t1 = new TextArea(s, 5,15); add(t1); kopierKnap = new Button("Kopiér>>"); kopierKnap.addActionListener(this); add(kopierKnap); t2 = new TextArea( 5,15); t2.setEditable(false); add(t2); } public void actionPerformed(ActionEvent e) // kræves af ActionListener { t2.setText(t1.getSelectedText() ); } }
Læg mærke til, at vi registrerer lytteren (som er applet-objektet selv) hos knappen.
Vores sidste eksempel er med KeyListener-interfacet, der tillader at lytte efter tastetryk.
Programmet herunder viser en tekst. Hver gang der tastes et bogstav, bliver det tilføjet teksten. Med piletasterne kan man rykke teksten op og ned, og retur-tasten sletter teksten.
import java.applet.*; import java.awt.*; import java.awt.event.*; public class Tastetryk extends Applet implements KeyListener { String tekst = "tast noget - pil op/ned rykker teksten "; Point pos = new Point(20,20); public Tastetryk() { addKeyListener(this); requestFocus(); } public void paint(Graphics g) { g.drawString(tekst, pos.x, pos.y); } public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) tekst = ":"; else if (e.getKeyCode() == KeyEvent.VK_UP) pos.y = pos.y - 10; else if (e.getKeyCode() == KeyEvent.VK_DOWN) pos.y = pos.y + 10; else tekst = tekst + e.getKeyChar(); repaint(); } public void keyReleased(KeyEvent e) {} // kræves af KeyListener public void keyTyped(KeyEvent e) {} // kræves af KeyListener }
Det følgende er en oversigt over lytter-interfaces og deres hændelser (alle i pakken java.awt.event).
Hændelsen ActionEvent sendes af den pågældende komponent, når brugeren klikker på en knap, trykker retur i et tekstfelt, vælger noget i et afkrydsningsfelt, radioknap, menu eller lignende.
public interface ActionListener { public void actionPerformed(ActionEvent e); }
Sendes af alle grafiske komponenter (Button, TextField, Checkbox osv., og Frame, Applet, Panel,...), når de hhv. ændrer størrelse, position, bliver synlige eller usynlige.
public interface ComponentListener { public void componentResized(ComponentEvent e); public void componentMoved(ComponentEvent e); public void componentShown(ComponentEvent e); public void componentHidden(ComponentEvent e); }
Sendes af komponenter, når de får fokus (dvs. hvis brugere trykker på en tast, vil det påvirke netop denne komponent). Kun en komponent har fokus ad gangen1.
public interface FocusListener { public void focusGained(FocusEvent e); public void focusLost(FocusEvent e); }
Sendes af afkrydsningsfelter og radioknapper, når en mulighed bliver krydset af eller fravalgt.
public interface ItemListener { void itemStateChanged(ItemEvent e); }
Sendes af komponenten, der har fokus. keyPressed() kaldes, når en tast bliver trykket ned (bemærk, at der godt kan være flere taster trykket ned samtidig, f.eks. Ctrl og C) og keyReleased(), når den bliver sluppet. Er man mere overordnet interesseret i, hvad brugeren taster ind, bør man benytte keyTyped(), der svarer til, at brugeren har trykket en tast ned og sluppet den igen.
public interface KeyListener { public void keyTyped(KeyEvent e); public void keyPressed(KeyEvent e); public void keyReleased(KeyEvent e); }
Kan sendes af alle grafiske komponenter. mousePressed() kaldes, når en museknap bliver trykket ned, og mouseReleased(), når den bliver sluppet igen. Er man mere overordnet interesseret i at vide, om brugeren har klikket et sted (trykket ned og sluppet på det samme sted), bør man benytte mouseClicked(). mouseEntered() og mouseExited() sendes, når musen går ind over hhv. væk fra komponenten.
public interface MouseListener { public void mousePressed(MouseEvent e); public void mouseReleased(MouseEvent e); public void mouseClicked(MouseEvent e); public void mouseEntered(MouseEvent e); public void mouseExited(MouseEvent e); }
Kan sendes af alle grafiske komponenter. mouseDragged() kaldes, når en museknap er trykket ned og hives (bevæges, mens museknappen forbliver trykket ned). mouseMoved() svarer til, at musen flyttes (uden nogle knapper trykket ned).
public interface MouseMotionListener { public void mouseDragged(MouseEvent e); public void mouseMoved(MouseEvent e); }
Sendes af tekstfelter (TextField og TextArea), når brugeren ændrer teksten.
public interface TextListener { public void textValueChanged(TextEvent e); }
Sendes af vinduer (Frame og Dialog), når de åbnes, forsøges lukket, lukkes, minimeres, gendannes, får fokus og mister fokus.
public interface WindowListener { public void windowOpened(WindowEvent e); public void windowClosing(WindowEvent e); public void windowClosed(WindowEvent e); public void windowIconified(WindowEvent e); public void windowDeiconified(WindowEvent e); public void windowActivated(WindowEvent e); public void windowDeactivated(WindowEvent e); }
Det er dette interface, der skal implementeres, hvis man vil fange, når brugeren vil lukke vinduet, og sørge for, at programmet stopper. Eksempel:
import java.awt.event.*; public class LukProgram implements WindowListener { public void windowOpened(WindowEvent e) {}; public void windowClosing(WindowEvent e) { System.exit(0); } public void windowClosed(WindowEvent e) {}; public void windowIconified(WindowEvent e) {}; public void windowDeiconified(WindowEvent e) {}; public void windowActivated(WindowEvent e) {}; public void windowDeactivated(WindowEvent e) {}; }
Klassen kan bruges fra dine egne programmer ved at tilføje et LukProgram-objekt til et vindue, som lytter:
vindue.addWindowListener( new LukProgram() );
1Man kan anmode om fokus på en komponent ved at kalde requestFocus() på den.