javabog.dk  |  << forrige  |  indhold  |  næste >>  |  programeksempler  |  om bogen

13 Hændelser i grafiske brugergrænseflader

Indhold:

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.

13.1 Eksempel: LytTilMusen

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:

  1. Opretter et muselytter-objekt.

  2. 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]

13.2 Eksempel: Linjetegning

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 (Linjelytter) giver informationer om klik videre til appletten (Linjetegning), sådan at en blå linje 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 Linjetegning extends Applet
{
  public Point trykpunkt;
  public Point slippunkt;

  public Linjetegning()
  {
    Linjelytter lytter = new Linjelytter();
    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 Linjelytter nødt til at have en reference (af type Linjetegning) til appletten:

import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class Linjelytter implements MouseListener
{
  public Linjetegning 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 linjen

    appletten.repaint();

fortæller vi Linjetegning-appletten, at den skal gentegne sig selv. Det forårsager kort efter et kald til dens paint()-metode.

13.2.1 Linjetegning i én klasse

Herunder er Linjetegning igen, men nu som en applet, der selv implementerer MouseListener. Det er linjen:

    this.addMouseListener(this);

der registrerer applet-objektet selv som lytter.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Linjetegning2 extends Applet implements MouseListener
{
  private Point trykpunkt;
  private Point slippunkt;

  public Linjetegning2()
  {
    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.

13.3 Ekstra eksempler

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).

13.3.1 Lytte til musebevægelser

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 linje 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().

13.3.2 Lytte til en knap

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.

13.3.3 Lytte efter tastetryk

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. 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
}

13.4 Appendiks

13.4.1 Lyttere og deres metoder

Det følgende er en oversigt over lytter-interfaces og deres hændelser (alle i pakken java.awt.event).

ActionListener

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); 
}

ComponentListener

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);
}

FocusListener

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);
}

ItemListener

Sendes af afkrydsningsfelter og radioknapper, når en mulighed bliver krydset af eller fravalgt.

public interface ItemListener {
    void itemStateChanged(ItemEvent e); 
}

KeyListener

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);
}

MouseListener

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);
}

MouseMotionListener

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);
}

TextListener

Sendes af tekstfelter (TextField og TextArea), når brugeren ændrer teksten.

public interface TextListener {
    public void textValueChanged(TextEvent e); 
}

WindowListener

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() );

13.5 Avanceret

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.

13.5.2 Adaptere

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.

1Man kan anmode om fokus på en komponent ved at kalde requestFocus() på den.

javabog.dk  |  << forrige  |  indhold  |  næste >>  |  programeksempler  |  om bogen
http://javabog.dk/ - af Jacob Nordfalk.
Licens og kopiering under Åben Dokumentlicens (ÅDL) hvor intet andet er nævnt (82% af værket).

Ønsker du at se de sidste 18% af dette værk (199974 tegn) skal du købe bogen. Så får du pæne figurer og layout, stikordsregister og en trykt bog med i købet.