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

13 Hændelser i grafiske brugergrænseflader

Indhold:

Forudsættes af kapitel Fejl: Henvisningskilde ikke fundet, Fejl: Henvisningskilde ikke fundet.

Forudsætter kapitel Fejl: Henvisningskilde ikke fundet, Fejl: Henvisningskilde ikke fundet og Fejl: Henvisningskilde ikke fundet, Fejl: Henvisningskilde ikke fundet.
I eksemplerne anvendes appletter, beskrevet i kapitel Fejl: Henvisningskilde ikke fundet, Fejl: Henvisningskilde ikke fundet.

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: Paneler (JPanel) kan udsende hændelser af typen MouseEvent. Klassen JPanel 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.

Ramme32

import javax.swing.*;
public class LytTilMusen extends JPanel
{
  public LytTilMusen()
  {
    Muselytter lytter = new Muselytter();
    this.addMouseListener(lytter);  // this er panelet selv
  }
}

Her er et eksempel på uddata fra programmet:

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 panelet besked om, at der er sket en hændelse. Det har man brug for, hvis man f.eks. vil tegne noget på panelet.

Herunder er et eksempel, hvor lytter-objektet (Linjelytter) giver informationer om klik videre til panelet (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 javax.swing.*;

public class Linjetegning extends JPanel
{
  public Point trykpunkt;
  public Point slippunkt;

  public Linjetegning()
  {
    Linjelytter lytter = new Linjelytter();
    lytter.panelet = this; // initialiserer lytterens reference til panelet
    this.addMouseListener(lytter);
  }

  public void paintComponent(Graphics g)
  {
    super.paintComponent(g);            // tegn først baggrunden på panelet
    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 panelet besked om klik vha. panelets to variabler, trykpunkt og slippunkt. Derfor er Linjelytter nødt til at have en reference (af type Linjetegning) til panelet:

import java.awt.event.*;

public class Linjelytter implements MouseListener
{
  public Linjetegning panelet;                 // Reference til panelet

  public void mousePressed(MouseEvent hændelse)  // kræves af MouseListener
  {
    panelet.trykpunkt = hændelse.getPoint();
  }

  public void mouseReleased(MouseEvent hændelse) // kræves af MouseListener
  {
    panelet.slippunkt = hændelse.getPoint();
    panelet.repaint(); // Gentegn panelet 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

    panelet.repaint();

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

Her er en klasse der viser dette og alle de andre paneler i dette kapitel, i hvert sit faneblad (beskrevet i afsnit Fejl: Henvisningskilde ikke fundet).

import javax.swing.*;
public class BenytAltKapitel13
{
  public static void main(String[] arg)
  {
    JTabbedPane faneblade = new JTabbedPane();
    faneblade.add("1 LytTilMusen", new LytTilMusen());
    faneblade.add("2 Linjetegning", new Linjetegning());
    faneblade.add("3 Linjetegning2", new Linjetegning2());
    faneblade.add("4 Kruseduller", new Kruseduller());
    faneblade.add("5 LytTilKnap", new LytTilKnap());
    faneblade.add("6 Tastetryk", new Tastetryk());
    faneblade.setRequestFocusEnabled(false);

    JFrame vindue = new JFrame("BenytAltKapitel13");
    vindue.add( faneblade );
    vindue.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // reagér på luk
    vindue.pack();                  // lad vinduet selv bestemme sin størrelse
    vindue.setVisible(true);                                  // åbn vinduet
  }
}

13.2.1 Linjetegning i én klasse

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

    this.addMouseListener(this);

der registrerer panel-objektet selv som lytter.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

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

  public Linjetegning2()
  {
    this.addMouseListener(this);
  }

  public void paintComponent(Graphics g)
  {
    super.paintComponent(g);            // tegn først baggrunden på panelet
    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 trykpunkt og slippunkt 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.

Herunder er et panel, man kan tegne kruseduller på. 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.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Kruseduller extends JPanel
                         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 paintComponent().

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.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class LytTilKnap extends JPanel implements ActionListener
{
  private JTextArea t1, t2;
  private JButton kopierKnap;

  public LytTilKnap()
  {
    setLayout(new FlowLayout());
    String s = "Her er en tekst.\nMarkér noget af\nden og tryk\nKopier...";
    t1 = new JTextArea(s, 5,15);
    add(t1);
    kopierKnap = new JButton("Kopiér>>");
    kopierKnap.addActionListener(this);
    add(kopierKnap);
    t2 = new JTextArea( 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 panel-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.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Tastetryk extends JPanel implements KeyListener
{
  String tekst = "tast noget - pil op/ned rykker teksten ";
  Point pos = new Point(20,20);

  public Tastetryk()
  {
    addKeyListener(this);
    setFocusable(true);
    requestFocus();
  }

  public void paintComponent(Graphics g)
  {
    super.paintComponent(g);            // tegn først baggrunden på panelet
    g.drawString(tekst, pos.x, pos.y);
  }

  public void keyPressed(KeyEvent e)
  {
    if (e.getKeyCode() == KeyEvent.VK_ENTER) tekst = "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 (de ligger 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

Hændelsen ComponentEvent sendes af alle grafiske komponenter (JButton, JTextField, JCheckBox osv. og JFrame, JApplet, JPanel,...), 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

Hændelsen FocusEvent 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

Hændelsen ItemEvent sendes af afkrydsningsfelter og radioknapper, når en mulighed bliver krydset af eller fravalgt.

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

KeyListener

Hændelser af typen KeyEvent sendes af komponenten, der har tastaturfokus. 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

Hændelsen MouseEvent 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 (JTextField og JTextArea), når brugeren ændrer teksten.

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

WindowListener

Hændelsen WindowEvent 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() );

Dette svarer således til at kalde setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE).

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/ - Forord af Jacob Nordfalk.
Licens og kopiering under Åben Dokumentlicens (ÅDL) hvor intet andet er nævnt (79% af værket).

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