Forrige avsnitt Neste avsnitt  Start på kapittel om spesialiserte komponenter for grafiske brukergrensesnitt

Bruk av Action-objekter

Interface Action er en utvidelse av ActionListener. Et Action-objekt kan derfor brukes som et lytteobjekt av type ActionListener. Action-objekter er nyttige å bruke som lytteobjekter når det skal være to eller flere forskjellige måter å aktivere én og samme kommando på: menyalternativ, knapp (som kan være på en verktøylinje), popp-opp-meny, tastaturalternativ. Alle disse kan bruke et felles Action-objekt for kommandoen. Dette kan da inneholde data som navn (til menyalternativ og knapp), ikon og tooltip-tekst. Et Action-objekt kan aktiveres og deaktiveres. Alle gui-komponenter som er knyttet til Action-objektet blir da samtidig aktivert eller deaktivert. Vi slipper altså å utføre instruksjoner for dette for hver enkelt gui-komponent: Action-objektet gjør jobben for oss. For å knytte et Action-objekt til en gui-komponent, kan vi enten gjøre kall på komponentens setAction-metode med Action-objektet som aktuell parameter, eller vi kan bruke Action-objektet som konstruktørparameter når komponenten blir opprettet.

Klassen AbstractAction implementerer interface Action med standardoppførsel for metodene. For å definere et Action-objekt er det derfor greiest å definere en subklasse til AbstractAction og redefinere metoden actionPerformed. Som nevnt ovenfor, kan Action-objektet inneholde hva som skal brukes av navn, ikon og tooltip-tekst for de komponentene som objektet skal være tilknyttet og lytte på. Verdier for disse kan vi få tilført Action-objektet i form av konstruktørparametre etter dette mønster:

class EksempelAction extends AbstractAction
{
  public EksempelAction(String tekst, ImageIcon ikon,
                         String tooltiptekst, Integer mnemonic)
  {
    super(tekst, ikon); //gir tekst og ikon for knapper og menyalternativer
    putValue(Action.SHORT_DESCRIPTION, tooltiptekst);
    putValue(Action.MNEMONIC_KEY, mnemonic); //tastaturalternativ
  }

  public void actionPerformed(ActionEvent e)
  {
    ...
  }
}

Obs:

Det er mer ressurskrevende å bruke Action-objekter enn å bruke ActionListener-objekter. De bør derfor ikke brukes dersom man ikke har bruk for den tilleggsfunksjonaliteten som de gir.

Vi skal nedenfor ta for oss et programeksempel der det blir brukt Action-objekter som felles lytteobjekter for knapper, menyalternativer og popp-opp-menyer. I programmet brukes det dessuten et lytteobjekt av type CaretListener. Vi må derfor først se litt nærmere på slike.

Lytteobjekt av type CaretListener

Subklasser av klasse JTextComponent kaller vi med en felles betegnelse for tekstkomponenter. (Blant annet vil objekter av typene JTextField, JTextArea og JEditorPane være tekstkomponenter.) Når markøren flytter seg i en tekstkomponent, eller når det er endringer i hvilken tekst som er markert, så blir det generert hendelser av type CaretEvent. Lytteobjekter av type CaretListener kan lytte på slike hendelser. Vi kan knytte et lytteobjekt av denne type til en tekstkomponent ved kall på dens addCaretListener-metode. For å definere en CaretListener, må vi implementere metoden caretUpdate:

class Markeringslytter implements CaretListner
{
  public void caretUpdate(CaretEvent e)
  {
    ...
  }
}

For det CaretEvent-objekt som er parameter til caretUpdate, kan vi gjøre kall på to nyttige metoder:

int getDot():
Returnerer aktuell markørposisjon, lik antall tegn fra tekststart. Dersom tekst er merket, vil dette være posisjonen for et av endepunktene til den markerte teksten.
int getMark():
Returnerer posisjonen til det andre endepunktet til markert tekst. Dersom ingen tekst er merket, vil den returnere samme verdien som getDot().
Obs: Det er ingen garanti for at getMark() >= getDot().

Programeksempel: Redigere tekst i et tekstområde

Programmet Actiondemo.java som er gjengitt nedenfor, har et brukervindu som ved oppstart ser ut som vist på følgende bilde.

I det tomme tekstområdet kan det skrives eller kopieres inn tekst. De tre knappene på vinduets verktøylinje er programmert til å kunne utføre følgende tekstredigeringsoperasjoner:

For de samme tre tekstredigeringsoperasjoner finnes det menyalternativer i menyen Editer, samt i en popp-opp-meny som kommer opp ved å høyreklikke musa mens markøren er i tekstområdet. I tillegg virker de vanlige tastaturalternativene Ctrl-c, Ctrl-x og Ctrl-v for disse operasjonene. Disse tastaturalternativene virker automatisk på alle editerbare tekstkomponenter, uten at vi trenger å foreta noe ekstra programmering. For å kopiere, klippe ut, eller lime inn tekst ved bruk av utklippsbordet, bruker vi tekstkomponentmetodene copy, cut og paste.

For hver av de tre nevnte tekstredigeringsoperasjonene er det i programmet definert et Action-objekt:

Hvert av Action-objektene brukes som felles lytteobjekt for de tre aktiveringsalternativene for vedkommende operasjon (knapp på verktøylinje, vanlig meny-alternativ, alternativ i popp-opp-meny). Når det gjelder kopiering og utklipp, er vedkommende Action-objekt bare aktivert i tilfelle det er merket noe tekst i tekstområdet. Ved aktivering og deaktivering av Action-objekt, vil da automatisk de knapper og meny-alternativer som er knyttet til dette, også bli aktivert eller deaktivert.

For å finne ut om det er merket noe tekst i tekstområdet, brukes en CaretListener. Denne er definert av den indre klassen Markeringslytter. Tekst vil være merket i tilfelle CaretEvent-metodene getDot og getMark returnerer forskjellig verdi.

Programmet gjør bruk av tre bildefiler som gir ikoner til knappene. Slik programmet er skrevet, må bildefilene ligge i en underkatalog bilder i forhold til programfilene. De tre bildefilene er copy.gif, cut.gif og paste.gif.

For å få opp den nevnte popp-opp-meny ved høyreklikk av musa i tekstområdet, trenger vi en muselytter. Denne er definert av den indre klassen Muselytter. Det blir sjekket om høyre museknapp er trykket ned eller sluppet opp, og i så fall vises popp-opp-menyen på det stedet der musepekeren er. Fullstendig programkode er gjengitt nedenfor.

  1 import java.awt.*;
  2 import java.awt.event.*;
  3 import javax.swing.*;
  4 import javax.swing.text.*;
  5 import javax.swing.event.*;
  6
  7 //Hendelsesobjekt for å klippe ut merket tekst og 
  8 //legge den på klippebordet.
  9 class CutAction extends AbstractAction
 10 {
 11   private JTextComponent kilde;
 12
 13   public CutAction(String navn, Icon ikon, String tiptekst,
 14           JTextComponent komp)
 15   {
 16     super(navn, ikon);
 17     putValue(Action.SHORT_DESCRIPTION, tiptekst); //gir tooltip-tekst
 18                                                   //til knapper
 19     kilde = komp;
 20   }
 21
 22   public void actionPerformed(ActionEvent e)
 23   {
 24     kilde.cut();
 25   }
 26 }
 27
 28 //Hendelsesobjekt for å kopiere merket tekst til klippebordet.
 29 class CopyAction extends AbstractAction
 30 {
 31   private JTextComponent kilde;
 32
 33   public CopyAction(String navn, Icon ikon, String tiptekst,
 34           JTextComponent komp)
 35   {
 36     super(navn, ikon);
 37     putValue(Action.SHORT_DESCRIPTION, tiptekst);
 38     kilde = komp;
 39   }
 40
 41   public void actionPerformed(ActionEvent e)
 42   {
 43     kilde.copy();
 44   }
 45 }
 46
 47 //Hendelsesobjekt for å lime inn tekst fra klippebordet.
 48 class PasteAction extends AbstractAction
 49 {
 50   private JTextComponent kilde;
 51
 52   public PasteAction(String navn, Icon ikon, String tiptekst,
 53           JTextComponent komp)
 54   {
 55     super(navn, ikon);
 56     putValue(Action.SHORT_DESCRIPTION, tiptekst);
 57     kilde = komp;
 58   }
 59
 60   public void actionPerformed(ActionEvent e)
 61   {
 62     kilde.paste();
 63   }
 64 }
 65
 66 public class Actiondemo extends JFrame
 67 {
 68   private JTextArea testområde;
 69   private JToolBar verktøylinje;
 70   private JPopupMenu poppopp;
 71   private Action kopier, klippUt, limInn;
 72
 73   public Actiondemo()
 74   {
 75     super("Action-demonstrasjon");
 76     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 77
 78     testområde = new JTextArea(10, 30);
 79     testområde.addMouseListener(new Muselytter());
 80     testområde.addCaretListener(new Markeringslytter());
 81
 82     //Oppretter Action-objekter som skal brukes av meny, 
 83     //verktøylinje og pop-opp-meny:
 84     kopier = new CopyAction(
 85             "Kopier", new ImageIcon(
 86             getClass().getResource("bilder/copy.gif")),
 87             "Kopier merket tekst til utklippsbord",
 88             testområde);
 89     kopier.setEnabled(false); //Aktiveres bare når noe tekst er merket.
 90     klippUt = new CutAction(
 91             "Klipp ut", new ImageIcon(
 92             getClass().getResource("bilder/cut.gif")),
 93             "Klipp ut merket tekst til utklippsbord",
 94             testområde);
 95     klippUt.setEnabled(false); //Aktiveres bare når noe tekst er merket.
 96     limInn = new PasteAction(
 97             "Lim inn", new ImageIcon(
 98             getClass().getResource("bilder/paste.gif")),
 99             "Lim inn tekst fra utklippsbord", testområde);
100
101     //Knapper for verktøylinje:
102     JButton kopi = new JButton();
103     kopi.setAction(kopier); //vil gi knappen både tekst, 
104                             //tooltip-tekst, ikon og lytteobjekt
105     kopi.setText(""); //ønsker bare ikon og tooltip-tekst på knappen
106     JButton klipp = new JButton();
107     klipp.setAction(klippUt);
108     klipp.setText("");
109     JButton lim = new JButton();
110     lim.setAction(limInn);
111     lim.setText("");
112
113     verktøylinje = new JToolBar();
114     verktøylinje.add(kopi);
115     verktøylinje.add(klipp);
116     verktøylinje.add(lim);
117
118     //Oppretter meny
119     JMenu editering = new JMenu("Editer");
120     JMenuItem kopivalg = new JMenuItem();
121     kopivalg.setAction(kopier);
122     editering.add(kopivalg);
123     JMenuItem klippvalg = new JMenuItem();
124     klippvalg.setAction(klippUt);
125     editering.add(klippvalg);
126     JMenuItem limvalg = new JMenuItem();
127     limvalg.setAction(limInn);
128     editering.add(limvalg);
129
130     //Oppretter pop-opp-meny
131     poppopp = new JPopupMenu();
132     JMenuItem kopiering = new JMenuItem();
133     kopiering.setAction(kopier);
134     poppopp.add(kopiering);
135     JMenuItem utklipp = new JMenuItem();
136     utklipp.setAction(klippUt);
137     poppopp.add(utklipp);
138     JMenuItem innliming = new JMenuItem();
139     innliming.setAction(limInn);
140     poppopp.add(innliming);
141
142     //Lager layout
143     Container c = getContentPane();
144     c.add(new JScrollPane(testområde), BorderLayout.CENTER);
145     c.add(verktøylinje, BorderLayout.PAGE_START);
146     JMenuBar menylinje = new JMenuBar();
147     menylinje.add(editering);
148     setJMenuBar(menylinje);
149     pack();
150     setVisible(true);
151   }
152
153   //Brukes til å lytte på høyre-klikk av mus for å vise poppopp-meny.
154   private class Muselytter extends MouseAdapter
155   {
156     public void mousePressed(MouseEvent e)
157     {
158       visPoppopp(e);
159     }
160
161     public void mouseReleased(MouseEvent e)
162     {
163       visPoppopp(e);
164     }
165
166     private void visPoppopp(MouseEvent e)
167     {
168       if (e.isPopupTrigger())
169       {
170         poppopp.show(e.getComponent(), e.getX(), e.getY());
171       }
172     }
173   }
174
175   //Bruker denne for å sjekke om det er markert noe tekst.
176   private class Markeringslytter implements CaretListener
177   {
178     public void caretUpdate(CaretEvent e)
179     {
180       int pos = e.getDot();  //henter markørposisjon
181       int start = e.getMark(); //henter posisjon for andre ende 
182       //av merket område
183       boolean merket = (pos != start); //sjekker om noe tekst er merket
184       klippUt.setEnabled(merket);
185       kopier.setEnabled(merket);
186     }
187   }
188
189   public static void main(String[] args)
190   {
191     EventQueue.invokeLater(new Runnable()
192     {
193       public void run()
194       {
195         new Actiondemo();
196       }
197     });
198   }
199 }

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

Forrige avsnitt Neste avsnitt  Start på kapittel om spesialiserte komponenter for grafiske brukergrensesnitt