Innholdsoversikt for programutvikling

Programmering av vinduslytter

Programmering av lukkeknappen til et vindu

Programmering av lukkeknappen til et vindu ble omtalt i notatet Vindusbaserte programmer, Programeksempel 1. Vi lærte der at vi for vindusobjektet må utføre instruksjonen

  setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

for at programmet skal avslutte når vi klikker på lukkeknappen. (Den forhåndsprogrammerte virkningen er at vinduet bare lukker seg, uten at programmet blir avsluttet.) I noen situasjoner ønsker vi imidlertid at programmet skal virke på den måten at før det blir avsluttet, så skal programmet lagre på fil de data det har brukt. For å få til dette, er det nødvendig å foreta en mer omfattende programmering av lukkeknappen. Vi skal nå gjøre det nødvendige forarbeidet til dette, eksempler på at det virkelig blir foretatt lagring på fil i forbindelse med bruk av lukkeknappen får vi ikke tatt før vi har lært om filbehandling. For et eksempel på dette, se programeksemplet under Serialisering av objekter i notatet Filbehandling.

Når vi klikker på lukkeknappen til et vindu, blir det generert en hendelse av type WindowEvent. Denne kan vi fange opp ved at vi definerer og oppretter et lytteobjekt av type WindowListener, samt registrerer dette objektet som lytteobjekt for vinduet vårt ved å bruke det som parameter i et kall på metoden addWindowListener. Når vi klikker på lukkeknappen, vil det da bli gjort kall på lytteobjektets metode windowClosing. Det er derfor i denne vi må legge inn de instruksjonene vi ønsker utført når det blir klikket på lukkeknappen. Instruksjonen for normal programavslutning er

  System.exit( 0 );

Parameteren til metodekallet er en statuskode. En verdi forskjellig fra null signaliserer unormal programavslutning.

Det vi ønsker utført av andre instruksjoner i forbindelse med programavslutning, for eksempel lagring av data på fil, må plasseres før denne avslutningsinstruksjonen.

Interface WindowListener inneholder i tillegg til windowClosing seks andre metoder. I enkle programmer er det ikke nødvendig å putte noen instruksjoner inni dem. For at vi i definisjonen av vårt lytteobjekt skal slippe å skrive opp alle disse metodene med tomt innhold, er det i javas klassebibliotek definert en klasse WindowAdapter. Denne implementerer interface WindowListener, men gir alle de sju metodene tomt innhold. For å definere lytteobjekt kan vi derfor, istedenfor å definere en klasse som implementerer interface WindowListener, definere en subklasse til WindowAdapter der vi redefinerer metoden windowClosing. Denne framgangsmåten skal vi bruke i følgende eksempel der vi omprogrammerer vinduet til et tidligere programeksempel, slik at det til vinduets lukkeknapp blir knyttet et lytteobjekt som kan brukes til programavslutning.

Eksempel

Som eksempel på det opplegget som er skissert ovenfor, skal vi omarbeide programmet for bokliste som er brukt som eksempel på Datatype for sammenkjedet liste i notatet om datastrukturer. Det ble der definert et vindu Bokhylle. Programmets klasser Bok og Bokliste kan vi bruke uendret. I vindusklassen Bokhylle legger vi inn en indre klasse som definerer vinduslytter. Denne klassen ser ut slik:

  private class Vinduslytter extends WindowAdapter
  {
    public void windowClosing( WindowEvent e )
    {
      System.exit( 0 );
    }
  }

I vindusklassens konstruktør registrerer vi en vinduslytter for vinduet ved følgende instruksjon:

    addWindowListener( new Vinduslytter() );

Du kan hente fullstendig kildekode for den reviderte vindusklassen fra følgende link: Bokhylle2.java. I programmets main-metode, som finnes i klassen Boksamling2 gjør vi ikke noe annet enn å opprette et vindusobjekt av den definerte typen:

public class Boksamling2
{
  public static void main( String[] args )
  {
    Bokhylle2 hylle = new Bokhylle2();
  }
}

Opprett for deg selv et program med de nye klassene Bokhylle2 og Boksamling2 og sjekk at både program og lukkeknapp virker riktig!

Anonyme indre klasser

I forrige programeksempel definerer den indre klassen Vinduslytter et lytteobjekt for programvinduets lukkeknapp. Klassen er subklasse til WindowAdapter. Vi trenger navnet på klassen bare én gang: Når vi i vindusklassens konstruktør registrerer et lytteobjekt ved å skrive

    addWindowListener( new Vinduslytter() );

I slike tilfeller tillater java at vi lar være å gi klassen et navn. Vi kunne derfor isteden ha skrevet

    addWindowListener(
      new WindowAdapter() {
        public void windowClosing( WindowEvent e )
        {
          System.exit( 0 );
        }
      } );

Dette resulterer imidlertid i ganske kryptisk kode. Vi må oppfatte det slik at vi oppretter et objekt av en klasse som er subklasse til klassen WindowAdapter, men vi lar være å gi subklassen noe navn.

Istedenfor å ha metodekallet som er vist ovenfor stående inne i konstruktøren til klassen som definerer programvinduet, kan vi flytte det til main-metoden der vindusobjektet blir opprettet. Vi bruker da vindusobjektet til å foreta metodekallet. På denne måten blir det gjort i den versjonen av programmet som står nedenfor. Det er bare vindusklassen og applikasjonsklassen som må endres. De nye filene Bokhylle3.java og Boksamling3.java er gjengitt nedenfor. I den siste fila blir det nå nødvendig å importere pakken java.awt.event.

 1 //Test av enkel bokliste.
 2 import java.awt.*;
 3 import java.awt.event.*;
 4 import javax.swing.*;
 5
 6 public class Bokhylle3 extends JFrame
 7 {
 8   private Bokliste bøkene;
 9   private JTextField tittel;
10   private JButton skrivUt;
11   private JTextArea utskrift;
12   private Kommandolytter lytter;
13
14   public Bokhylle3()
15   {
16     super( "Test av bokliste" );
17     bøkene = new Bokliste();
18     Container c = getContentPane();
19     c.setLayout( new FlowLayout() );
20     c.add( new JLabel( "Ny boktittel: " ) );
21     tittel = new JTextField( 25 );
22     c.add( tittel );
23     skrivUt = new JButton( "Skriv bokliste" );
24     c.add( skrivUt );
25     utskrift = new JTextArea( 10, 30 );
26     utskrift.setEditable( false );
27     c.add( new JScrollPane( utskrift ) );
28     lytter = new Kommandolytter();
29     tittel.addActionListener( lytter );
30     skrivUt.addActionListener( lytter );
31   }
32
33   public void settInnBok()
34   {
35     Bok ny = new Bok( tittel.getText() );
36     bøkene.settInn( ny );
37     tittel.setText( "" );
38   }
39
40   public void skrivBokliste()
41   {
42     utskrift.setText( bøkene.toString() );
43   }
44
45   private class Kommandolytter implements ActionListener
46   {
47     public void actionPerformed( ActionEvent e )
48     {
49       if ( e.getSource() == tittel )
50         settInnBok();
51       else if ( e.getSource() == skrivUt )
52         skrivBokliste();
53     }
54   }
55 }

 1 import java.awt.event.*;
 2
 3 public class Boksamling3
 4 {
 5   public static void main( String[] args )
 6   {
 7     Bokhylle3 hylle = new Bokhylle3();
 8     hylle.setSize( 400, 300 );
 9     hylle.addWindowListener(
10         new WindowAdapter() {
11           public void windowClosing( WindowEvent e )
12           {
13             System.exit( 0 );
14           }
15         } );
16     hylle.setVisible( true );
17   }
18 }

Sjekk for deg selv at programmet virker riktig også med de nye klassene!

Når du definerer et programvindu for et applikasjonsprogram, slik vi har gjort i de siste eksemplene, bør du alltid programmere vinduets lukkeknapp slik at den gir programavslutning, for dette er en funksjonalitet som en bruker vil forvente at programmet har. De to måtene å gjøre det på som er vist i de to siste eksemplene må imidlertid bare oppfattes som likeverdige alternativer. Det er ikke slik å forstå at det å bruke anonyme indre klasser er "bedre" enn å bruke navngitte klasser. Tvert imot resulterer anonyme klasser i ganske kryptisk kode. Du bør derfor være svært tilbakeholden med å bruke slike klasser.

Når det gjelder programmering av lukkeknappen til et vindu, vil begge de to alternativene som er beskrevet ovenfor være litt omstendelige for bare å få oppfylt den ene tingen at programmet blir avsluttet når brukeren klikker på lukkeknappen. I slike tilfelle er det enklere å bruke instruksjonen

  setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

for vinduet vårt, som er nevnt i begynnelsen av dette notatet. Til JFrame-metoden setDefaultCloseOperation finnes det for øvrig flere valg av aktuell parameter. En av følgende konstanter kan brukes:

JFrame.EXIT_ON_CLOSE
(avslutter applikasjonen med System.exit(0))
WindowConstants.DISPOSE_ON_CLOSE
(skjuler vinduet og destruerer vindusobjektet. Dersom vinduet er det siste av dem som applikasjonen kan vise på skjermen, vil som regel også programmet bli avsluttet.)
WindowConstants.HIDE_ON_CLOSE
(default-verdien, vinduet gjøres usynlig, men vindusobjektet eksisterer fortsatt og kan gjøres synlig igjen seinere)
WindowConstants.DO_NOTHING_ON_CLOSE
(det skjer ingenting ved klikk på lukkeknappen. Det bør da være registrert en WindowListener isteden.)

Dersom en for eksempel ønsker at programmet skal lagre data på fil før det blir avsluttet, er en nødt til å definere en WindowListener. (Hvordan en får lagret data på fil er beskrevet i notatet Filbehandling.) Videre er det slik at dersom det for vinduet vårt er registrert en WindowListener, så vil denne bli aktivert før behandlingen av lukkehendelsen definert av konstantene ovenfor.

Innholdsoversikt for programutvikling

Copyright © Kjetil Grønning og Eva Hadler Vihovde, revidert 2015