![]() |
![]() |
Start på kapittel om spesialiserte komponenter for grafiske brukergrensesnitt |
Dialogvinduer eller dialogbokser, som de også kalles, har vi hittil
opprettet ved å bruke diverse
static
-metoder i klassen
JOptionPane
.
Vinduene har vært av diverse standardtyper og har
til en viss grad latt seg tilpasse våre ønsker. De har alle sammen vært objekter
av type
JDialog
.
For å få et dialogvindu helt etter eget ønske,
er det greiest selv å definere en subklasse til JDialog
.
Denne utstyrer vi med grafiske komponenter, lytteobjekter, etc. akkurat som
et annet vindu. Det er imidlertid et par ting som er spesielle for dialogvinduer:
Det er i foreldervinduet vi oppretter et dialogvindu. Peker (referanse) til foreldervinduet setter vi som en konstruktørparameter når dialogvinduet blir opprettet. Denne parameteren bruker vi i klassen for dialogvinduet til å gi verdi til et datafelt av type lik foreldervinduets type. Dette datafeltet kan brukes til å gjøre kall på metoder i foreldervinduet. På den måten får vi i stand den nødvendige kommunikasjonen mellom vinduene. For å definere et dialogvindu som oppfyller de nevnte kravene, kan vi derfor skrive kode etter dette mønster:
class Dialogvindu extends JDialog { private Foreldervindu f; // kan brukes til å gjøre kall på metoder // i foreldervindu .... // andre datafelter etter behov public Dialogvindu( Foreldervindu forelder ) { super( forelder, "Tittel", true ); // true gir modalt dialogvindu f = forelder; ... } ... // øvrig innhold etter behov og ønsker }
I foreldervinduet skriver vi følgende for å opprette et dialogvindu av den definerte typen:
Dialogvindu d = new Dialogvindu( this );
Et dialogvindu bør bare være synlig mens det er behov for det. Dersom
det er modalt (angis ved konstruktørparameteren
true
),
vil det ikke være mulig å gjøre aksess på foreldervinduet mens dialogvinduet
er åpent. Vi kan gjøre dialogvinduet synlig/usynlig på vanlig måte ved
setVisible( true/false )
.
Et vindusobjekt legger beslag på en del ressurser. For å frigjøre disse,
kan vi slette hele vindusobjektet når vi ikke trenger det. Det får vi til
ved kall på vinduets metode dispose
.
Som for vinduer ellers, kan vi programmere lukkeknappen til denne
funksjonaliteten ved å skrive
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
Når det eventuelt blir
behov for dialogvinduet på nytt, kan vi opprette et nytt objekt.
Default-funksjonaliteten er at vinduet kun lukker seg når lukkeknappen blir
brukt. Det vil da fortsatt bli lagt beslag på de ressurser som vindusobjektet representerer.
Klassen
Navnevalg
definerer følgende vindu:
Det navnet, Marie i dette tilfelle, som blir vist som det valgte navn i programvinduet, er blitt overført fra et dialogvindu som er knyttet til programvinduet. Når man klikker på knappen Velg et nytt navn ..., vil dialogvinduet bli vist. Dersom programmet startet med å vise Marie som de valgte navnet, som på bildet, og man så klikker på knappen Velg et nytt navn ..., så vil følgende dialogvindu komme opp:
Dialogvinduet er modalt, slik at det ikke er mulig å gjøre aksess på foreldervinduet mens dialogvinduet er åpent. For at dialogvinduet skal kunne overføre navn tilbake til programvinduet, som er dialogvinduets foreldervindu, må vi i dialogvinduet ha en referanse til foreldervinduet, slik at det er mulig å kommunisere. Referansen legger vi inn som et datafelt i klassen for dialogvinduet:
24 private Navnevalg forelder;
Datafeltet blir tilordet verdi via konstruktørparameter ved at vi i klassen for dialogvinduet har følgende kode:
28 public Listedialog(Navnevalg f) 29 { 30 super(f, "Navnevelger", true); //modalt dialogvindu 31 forelder = f;
Konstruktørparameteren blir tilordnet verdi i klassen for programvinduet samtidig som dialogvinduet blir opprettet:
29 navnevelger = new Listedialog(this);
Referansen til foreldervinduet kan brukes til å gjøre kall på metoder i dette. Data kan da overføres fra dialogvinduet til foreldervinduet i form av parametre til disse metodene.
Ved programstart blir det valgt et tilfeldig navn i dialogvinduets navneliste og dette blir overført til foreldervinduet. Det skjer ved at følgende kode blir utført i dialogvinduets konstruktør:
36 Random velger = new Random(); 37 int start = velger.nextInt(navn.length); 38 navneliste.setSelectedIndex(start); 39 String startnavn = navneliste.getSelectedValue(); 40 forelder.settNavn(startnavn);
Dialogvinduet skal ellers virke slik at alltid når det blir åpnet, så skal det navnet som er valgt for øyeblikket være synlig i dets navneliste. For at dette også skal skje første gang dialogvinduet blir åpnet, er følgende instruksjon lagt inn på slutten av dets konstruktør:
80 navneliste.ensureIndexIsVisible(start);
Programmet skal for øvrig virke på den måten at når vi velger et navn i dialogvinduets listeboks og klikker på OK-knappen, så skal dialogvinduet lukke seg og det valgte navnet skal settes som nytt, valgt navn i foreldervinduet. For å oppnå dette, må OK-knappen være tilordnet lytteobjekt som henter det valgte navn og overfører det til foreldervinduet. Programkoden som definerer foreldervinduet er gjengitt nedenfor.
1 import javax.swing.*; 2 import java.awt.*; 3 import java.awt.event.*; 4 5 public class Navnevalg extends JFrame 6 { 7 private JLabel valginfo, valgtNavn; 8 private Listedialog navnevelger; 9 private JButton velg; 10 11 public Navnevalg() 12 { 13 super("Velg et navn!"); 14 JPanel c = new JPanel(); 15 c.setLayout(new BoxLayout(c, BoxLayout.PAGE_AXIS)); 16 c.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); 17 valginfo = new JLabel("Det valgte navn: "); 18 c.add(valginfo); 19 valgtNavn = new JLabel(); 20 valgtNavn.setForeground(Color.black); 21 c.add(valgtNavn); 22 c.add(Box.createRigidArea(new Dimension(0, 10))); 23 velg = new JButton("Velg et nytt navn ..."); 24 velg.addActionListener(new Knappelytter()); 25 c.add(velg); 26 valginfo.setAlignmentX(JComponent.CENTER_ALIGNMENT); 27 valgtNavn.setAlignmentX(JComponent.CENTER_ALIGNMENT); 28 velg.setAlignmentX(JComponent.CENTER_ALIGNMENT); 29 navnevelger = new Listedialog(this); 30 setContentPane(c); 31 pack(); 32 setVisible(true); 33 } 34 35 public void settNavn(String nyttNavn) 36 { 37 valgtNavn.setText(nyttNavn); 38 } 39 40 private class Knappelytter implements ActionListener 41 { 42 public void actionPerformed(ActionEvent e) 43 { 44 if (e.getSource() == velg) 45 { 46 //Ønsker at dialogvinduet skal legge seg midt over 47 //sitt foreldervindu, uansett hvor på skjermen 48 //foreldervinduet befinner seg: 49 navnevelger.setLocationRelativeTo(Navnevalg.this); 50 navnevelger.setVisible(true); 51 } 52 } 53 } 54 }
Lytteobjektet for foreldervinduets knapp Velg et nytt navn ... blir programmert til å gjøre dialogvinduet synlig.
Koden som definerer dialogvinduet finnes i fila
Listedialog.java
som har følgende innhold:
1 import java.awt.BorderLayout; 2 import java.awt.Container; 3 import java.awt.Dimension; 4 import java.awt.event.ActionEvent; 5 import java.awt.event.ActionListener; 6 import java.awt.event.MouseAdapter; 7 import java.awt.event.MouseEvent; 8 import java.util.Random; 9 import javax.swing.*; 10 11 public class Listedialog extends JDialog 12 { 13 private JList<String> navneliste; 14 private String[] navn = 15 { 16 "Albertine", "Babette", "Cecilie", "Denise", 17 "Emanuelle", "Fantine", "Gilberte", "Helene", 18 "Irene", "Juliette", "Kine", "Lise", "Marie", 19 "Natalie", "Odette", "Pauline", "Regine", "Sofie", 20 "Therese", "Ursuline", "Violette", "Xantippe", 21 "Yvonne", "Zélie" 22 }; 23 private JButton ok, dropp; 24 private Navnevalg forelder; 25 private Knappelytter kLytter; 26 private Muselytter mLytter; 27 28 public Listedialog(Navnevalg f) 29 { 30 super(f, "Navnevelger", true); //modalt dialogvindu 31 forelder = f; 32 navneliste = new JList<>(navn); 33 //skal bare kunne velge ett navn om gangen: 34 navneliste.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 35 //velger et tilfeldig startnavn i lista: 36 Random velger = new Random(); 37 int start = velger.nextInt(navn.length); 38 navneliste.setSelectedIndex(start); 39 String startnavn = navneliste.getSelectedValue(); 40 forelder.settNavn(startnavn); 41 42 JPanel listepanel = new JPanel(); 43 listepanel.setLayout(new BoxLayout(listepanel, 44 BoxLayout.PAGE_AXIS)); 45 listepanel.add(new JLabel("Jentenavn som slutter på e:")); 46 listepanel.add(Box.createRigidArea(new Dimension(0, 5))); 47 JScrollPane listeskroller = new JScrollPane(navneliste); 48 listeskroller.setPreferredSize(new Dimension(250, 80)); 49 listeskroller.setMinimumSize(new Dimension(250, 80)); 50 listeskroller.setAlignmentX(LEFT_ALIGNMENT); 51 //ønsker venstrejustering 52 listepanel.add(listeskroller); 53 listepanel.setBorder( 54 BorderFactory.createEmptyBorder(10, 10, 10, 10)); 55 56 JPanel knappepanel = new JPanel(); 57 knappepanel.setLayout(new BoxLayout(knappepanel, 58 BoxLayout.LINE_AXIS)); 59 knappepanel.setBorder( 60 BorderFactory.createEmptyBorder(0, 10, 10, 10)); 61 knappepanel.add(Box.createHorizontalGlue()); 62 ok = new JButton("OK"); 63 dropp = new JButton("Dropp å velge"); 64 knappepanel.add(dropp); 65 knappepanel.add(Box.createRigidArea(new Dimension(10, 0))); 66 knappepanel.add(ok); 67 68 kLytter = new Knappelytter(); 69 ok.addActionListener(kLytter); 70 dropp.addActionListener(kLytter); 71 mLytter = new Muselytter(); 72 navneliste.addMouseListener(mLytter); 73 74 Container c = getContentPane(); 75 c.add(listepanel, BorderLayout.CENTER); 76 c.add(knappepanel, BorderLayout.PAGE_END); 77 pack(); 78 //sikrer at det valgte navnet er synlig i navnelista når 79 //dialogvinduet blir åpnet: 80 navneliste.ensureIndexIsVisible(start); 81 } 82 83 private class Knappelytter implements ActionListener 84 { 85 public void actionPerformed(ActionEvent e) 86 { 87 if (e.getSource() == ok) 88 { 89 String navn = navneliste.getSelectedValue(); 90 forelder.settNavn(navn); 91 setVisible(false); 92 } 93 else if (e.getSource() == dropp) 94 { 95 setVisible(false); 96 } 97 } 98 } 99 100 //Lytter på dobbeltklikk i navnelista 101 private class Muselytter extends MouseAdapter 102 { 103 public void mouseClicked(MouseEvent e) 104 { 105 if (e.getSource() == navneliste && e.getClickCount() == 2) 106 { 107 ok.doClick(); //samme effekt som å klikke på ok-knappen 108 } 109 } 110 } 111 }
Vi merker oss her spesielt hvordan kommunikasjonen med foreldervinduet er programmert. Referanse til dette får vi via konstruktørparameter. Når det blir klikket på dialogvinduets ok-knapp, får vi overført valgt navn til foreldervinduet ved at følgende kode blir utført:
89 String navn = navneliste.getSelectedValue(); 90 forelder.settNavn(navn); 91 setVisible(false);
Med den siste instruksjonen får vi lukket dialogvinduet. Klikkes det på den andre knappen, er det bare dette som skjer.
Noe annet som vi her kan merke oss, er hvordan vi kan programmere for å bestemme hva som skal skje dersom det blir dobbeltklikket med musa. I dette tilfelle skal dobbeltklikk med musa på et navn i dialogvinduets listeboks ha samme virkning som om vi først hadde klikket på navnet for å velge det og deretter klikket på OK-knappen for å overføre det til foreldervinduet. For å få til denne funksjonaliteten for dobbeltklikk med musa, blir det for listeboksen registrert en muselytter:
71 mLytter = new Muselytter(); 72 navneliste.addMouseListener(mLytter);
I muselytteren er metoden mouseClicked
implementert på
følgende måte:
103 public void mouseClicked(MouseEvent e) 104 { 105 if (e.getSource() == navneliste && e.getClickCount() == 2) 106 { 107 ok.doClick(); //samme effekt som å klikke på ok-knappen 108 } 109 }
Vi sjekker altså om det er klikket to ganger på museknappen. I så fall
gjøres det kall på ok-knappens metode doClick
. Dette
metodekallet har samme virkning som om vi skulle klikket på knappen.
For øvrig ser vi både i klassen for hovedvinduet og dialogvinduet
eksempler på hvordan vi kan bruke BoxLayout
og rammer for
å få layouten slik vi måtte ønske å ha den.
For å kjøre programmet behøves også driverfila
Navnevelger.java
som oppretter et hovedvindu.
Copyright © Kjetil Grønning og Eva Hadler Vihovde, revidert 2014
![]() |
![]() |
Start på kapittel om spesialiserte komponenter for grafiske brukergrensesnitt |