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

Bruk av JTabbedPane (fanekort)

En JTabbedPane er en type container. Ved å bruke en slik kan vi oppnå at flere komponenter (vanligvis paneler) deler samme område i et vindu. Hver komponent blir tildelt en fane (flik) som kan inneholde navn, ikon og tooltiptekst. (Fanene kan også inneholde andre komponenter, se programeksempel 2 nedenfor.) Bare én av komponentene (ett av panelene) vises om gangen. Hvilken som skal vises, bestemmer vi ved å velge fane (flik) med musa eller tastaturet. Vi kan forestille oss dette på den måten at de forskjellige panelene ligger oppå hverandre som kortene i en kortstokk. Ved hjelp av fanene velger vi hvilket som skal ligge øverst og dermed være synlig.

For å opprette et fanekort, oppretter vi på vanlig måte et objekt av type JTabbedPane:

  JTabbedPane fanekort = new JTabbedPane();

For å legge inn en komponent (vanligvis panel) skriver vi:

  fanekort.addTab( "En fanetekst", komponent );

eller

  fanekort.addTab( "En fanetekst", ikon, komponent, "En tooltiptekst" );

Parameteren for tooltiptekst kan vi eventuelt droppe. Ønsker vi tooltiptekst, men ikke ikon, kan vi bruke verdien null for ikon-parameteren. De panelene vi legger inn i fanekortet kan selvsagt hver for seg inneholde hva som helst av komponenter og ha hvilken som helst layout.

Panelene er indeksert fra 0 og oppover i den rekkefølge som vi har gjort kall på addTab-metoden for å legge dem inn. Ved å bruke indeks kan vi også sette inn en komponent innimellom dem som allerede er plassert ved at vi bruker insertTab-metoden:

  fanekort.insertTab(tittel, ikon, komponent, tooltiptekst, indeks);

Vær imidlertid oppmerksom på at dersom vi legger inn en komponent, så blir den ikke automatisk vist. For å framprovisere det, kan vi velge den ved hjelp av setSelectedIndex-metoden. Dersom vi for eksempel vil tilføye en komponent på slutten og så vil ha denne vist, kan vi skrive

  fanekort.addTab(tittel, ikon, komponent, tooltiptekst);
  fanekort.setSelectedIndex(fanekort.getTabCount() - 1);

Ønsker vi å fjerne en komponent, kan vi skrive

  fanekort.removeTabAt(indeks);

Defaultplassering av fanene (flikene), én for hver komponent som er lagt inn ved bruk av metoden addTab, er på toppen (øverste kant) av JTabbedPane-containeren. Men vi kan endre posisjonen til å bli en av de andre sidene ved bruk av metoden setTabPlacement. Mulige parametre er da en av følgende konstanter:

For å vise de forskjellige panelene klikker vi med musa på fanene, eller bruker tastaturets piltaster. Det er ikke nødvendig å implementere noe lytteobjekt for å oppnå denne funksjonaliteten. For fanene i fanekortet kan vi også tilordne tastaturalternativer ved bruk av metoden setMnemonicAt. Merk deg at denne har et litt annet navn enn den tilsvarende metoden setMnemonic for knapper og menyalternativer. Metoden setMnemonicAt har også andre parametre: Den har to int-parametre. Den første skal angi indeksen for det panel som vi nå vil angi tastaturalternativ for. Den andre parameteren skal spesifisere den tast som skal være tastaturalternativet, brukt sammen med Alt-tasten. Siden også denne parameteren skal være av type int, må vi vi bruke den virtuelle tastaturkoden for vedkommende tast. For eksempel vil parameteren KeyEvent.VK_1 angi den numeriske tasten med tallet 1 på, slik at tastaturalternativet blir Alt-1.

Størrelse på fanekort

En JTabbedPane blir automatisk gjort bred nok til å vise det bredeste av de panelene (komponentene) som blir lagt inn, og høy nok til å vise det høyeste av disse. Alle panelene blir vist med samme størrelse.

Programeksempel 1

Programmet TabbedPaneDemo.java er koden gjengitt for nedenfor. Det er hentet fra The Java Tutorials. Programmet gjør bruk av bildefila middle.gif som må ligge i en underkatalog images i forhold til programmets class-fil. I programmet blir det som subklasse til JPanel definert et panel. Inn i dette legges det en JTabbedPane som eneste komponent. I denne blir det lagt inn fire paneler som hvert inneholder en label. Fanene for de fire panelene blir utstyrt med både navn, ikon, tooltiptekst og tastaturalternativ. Det siste av panelene blir gitt en foretrukket størrelse på 410 ganger 50 piksler. Dette vil komme til å bestemme størrelsen til fanekortet og dermed til hele vinduet, siden dette gis størrelse ved bruk av metoden pack. Ved kjøring gir programmet vinduet som er vist på følgende bilde:

 1 import javax.swing.*;
 2 import java.awt.*;
 3 import java.awt.event.KeyEvent;
 4
 5 //Gjør bruk av bildefila bilder/middle.gif.
 6 public class TabbedPaneDemo extends JPanel
 7 {
 8   public TabbedPaneDemo()
 9   {
10     super(new GridLayout(1, 1));
11
12     JTabbedPane tabbedPane = new JTabbedPane();
13     ImageIcon icon = new ImageIcon(
14             getClass().getResource("bilder/middle.gif"));
15
16     JComponent panel1 = makeTextPanel("Panel #1");
17     tabbedPane.addTab("Tab 1", icon, panel1, "Does nothing");
18     tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);
19
20     JComponent panel2 = makeTextPanel("Panel #2");
21     tabbedPane.addTab("Tab 2", icon, panel2,
22             "Does twice as much nothing");
23     tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);
24
25     JComponent panel3 = makeTextPanel("Panel #3");
26     tabbedPane.addTab("Tab 3", icon, panel3, "Still does nothing");
27     tabbedPane.setMnemonicAt(2, KeyEvent.VK_3);
28
29     JComponent panel4 = makeTextPanel(
30             "Panel #4 (has a preferred size of 410 x 50).");
31     panel4.setPreferredSize(new Dimension(410, 50));
32     tabbedPane.addTab("Tab 4", icon, panel4, "Does nothing at all");
33     tabbedPane.setMnemonicAt(3, KeyEvent.VK_4);
34
35     tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
36
37     //Add the tabbed pane to this panel.
38     add(tabbedPane);
39   }
40
41   protected JComponent makeTextPanel(String text)
42   {
43     JPanel panel = new JPanel(false);
44     JLabel filler = new JLabel(text);
45     filler.setHorizontalAlignment(JLabel.CENTER);
46     panel.setLayout(new GridLayout(1, 1));
47     panel.add(filler);
48     return panel;
49   }
50
51   /**
52    * Create the GUI and show it.
53    */
54   private static void createAndShowGUI()
55   {
56     //Create and set up the window.
57     JFrame frame = new JFrame("TabbedPaneDemo");
58     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
59     frame.getContentPane().add(new TabbedPaneDemo(), BorderLayout.CENTER);
60
61     //Display the window.
62     frame.pack();
63     frame.setVisible(true);
64   }
65
66   public static void main(String[] args)
67   {
68     createAndShowGUI();
69   }
70 }

Dersom vi legger inn så mange fanekort at det ikke blir plass til alle fanene på én rad, vil det, om vi ikke bestemmer noe annet, automatisk bli begynt på en ny rad. Følgende bilde viser vinduet fra programeksemplet ovenfor med redusert bredde:

Som alternativ til denne virkemåten har vi mulighet til heller å velge skrolling ved å bruke instruksjonen

    tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);

Det andre alternativet, som altså er default-virkemåten som vises på bildet over, er resultat av instruksjonen

    tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);

Følgende bilde viser vinduet til programmet ovenfor når vindusbredden er reduseert og skrollealternativet blir brukt:

Programeksempel 2

Vi skal lage et program som kan vise filer under hver sin fane på et fanekort på liknende måte som vi kjenner til for eksempel fra TextPad, og fra mange andre programmer. For å begrense programmets størrelse, skal vi droppe nesten all annen funksjonalitet. Det eneste vi vil ha, er en filmeny som gjør det mulig å velge fil som vi ønsker å vise på et fanekort for å se på den. Der vil det for øvrig være mulig å redigere den, men det er ikke lagt til noe funksjonalitet for å lagre den reviderte versjonen. (Dette er det imidlertid lett å gjøre, i tilfelle vi skulle ønske det.) Men vi vil på den andre side utstyre hver fane med et ikon eller en avkryssingsboks som virker på den måten at når vi klikker på ikonet eller krysser av i avkryssingsboksen, så blir vedkommende fane fjernet. Bildet nedenfor viser programvinduet etter at tre filer er åpnet.

Det er her på fanene brukt ikon istedenfor avkryssingsboks til å klikke på for fjerning. Avkryssingsboks skal programmet legge inn dersom det ikke finner bildefil for ikonet.

Det er mulig å legge inn hva vi vil av komponenter i en fanetittel. Først tilføyer vi fanen til fanekortet ved bruk av en av metodene som er omtalt i begynnelsen av notatet. Deretter foretar vi et kall etter dette mønster:

    fanekort.setTabComponentAt(indeks, komponent);

I dette programmet er det i fanetitlene lagt inn et lite panel som inneholder filnavn for den fil som vises, samt ikon eller avkryssingsboks til å klikke på for fjerning av fanen. Klassen Fanepanel som er gjengitt nedenfor definer fanepanelet.

Dersom programmet finner bildefil for ikonet, er det ikon som blir brukt istedenfor avkryssingsboks. For å kunne knytte muselytter til ikonet, blir det lagt inn i et eget panel. (Det er ikke mulig å registrere muselytter direkte for et ikonbilde.) Muselytteren sørger for å fjerne fanen når det blir klikket på ikonet. For å få til dette, er det opprettet kommunikasjon med fanekortet.

Til avkryssingsboksen er det knyttet ActionListener som har samme funksjonalitet som muselytteren. Til avkryssingsboksen er det dessuten knyttet en ChangeListener i form av en anonym indre klasse. Det er gjort for å hindre at det blir vist avkryssingsmerke i avkryssingsboksen uten at brukeren har klikket med musa i den. (Prøvekjøring av programmet viste at dette skjedde uten dette lytteobjektet.) Lytteren blir aktivert så snart det skjer en eller annen tilstandsendring for avkryssingsboksen.

 1
 2 import javax.swing.*;
 3 import java.awt.event.*;
 4 import java.net.URL;
 5 import java.awt.*;
 6 import javax.swing.event.*;
 7
 8 public class Fanepanel extends JPanel
 9 {
10
11   private JTabbedPane fanekort;
12   private JComponent kortkomponent;
13   private ImageIcon lukkeikon;
14   private final JToggleButton lukkeknapp = new JCheckBox();
15
16   public Fanepanel(JTabbedPane t, JComponent k, String tekst)
17   {
18     fanekort = t;
19     kortkomponent = k;
20     add(new JLabel(tekst));
21     URL kilde = Fanepanel.class.getResource("bilder/Remove16.gif");
22
23     if (kilde != null)
24     {
25       lukkeikon = new ImageIcon(kilde);
26       Ikonpanel ikonpanel = new Ikonpanel(lukkeikon);
27       ikonpanel.addMouseListener(new Muselytter());
28       add(ikonpanel);
29     }
30     else
31     {
32       lukkeknapp.addActionListener(new Lukkelytter());
33       lukkeknapp.addChangeListener(
34               new ChangeListener()
35               {
36
37                 public void stateChanged(ChangeEvent e)
38                 {
39                   lukkeknapp.setSelected(false);
40                 }
41               });
42       add(lukkeknapp);
43     }
44   }
45
46   private class Ikonpanel extends JPanel
47   {
48
49     private ImageIcon ikon;
50
51     public Ikonpanel(ImageIcon bilde)
52     {
53       ikon = bilde;
54       setToolTipText("Fjern fane");
55     }
56
57     public void paintComponent(Graphics g)
58     {
59       super.paintComponent(g);
60       ikon.paintIcon(this, g, 0, 0);
61     }
62
63     public Dimension getPreferredSize()
64     {
65       return new Dimension(ikon.getIconWidth(), ikon.getIconHeight());
66     }
67   }
68
69   private class Lukkelytter implements ActionListener
70   {
71
72     public void actionPerformed(ActionEvent e)
73     {
74       fanekort.remove(kortkomponent);
75       fanekort.setSelectedIndex(fanekort.getTabCount() - 1);
76     }
77   }
78
79   private class Muselytter extends MouseAdapter
80   {
81
82     public void mousePressed(MouseEvent e)
83     {
84       fanekort.remove(kortkomponent);
85       fanekort.setSelectedIndex(fanekort.getTabCount() - 1);
86     }
87   }
88 }

Klassen Filpanel som er gjengitt nedenfor definerer panelene som blir knyttet til fanene og som skal vise filinnhold. De inneholder for dette et tekstområde utstyrt med skrollefelt. Hvilken fil som skal vises mottas det opplysninger om via konstruktørparameter.

 1
 2 import java.io.*;
 3 import javax.swing.*;
 4 import java.awt.*;
 5
 6 public class Filpanel extends JPanel
 7 {
 8
 9   private String filnavn;
10   private JTextArea utskrift;
11
12   public Filpanel(File fil)
13   {
14     setLayout(new BorderLayout());
15     utskrift = new JTextArea(20, 50);
16     add(new JScrollPane(utskrift), BorderLayout.CENTER);
17     if (fil != null)
18     {
19       filnavn = fil.getName();
20     }
21   }
22
23   public void visFil(String filsti)
24   {
25     try (BufferedReader fil = new BufferedReader(new FileReader(filsti)))
26     {
27       String linje;
28       while ((linje = fil.readLine()) != null)
29       {
30         utskrift.append(linje + "\n");
31       }
32       utskrift.setCaretPosition(0);
33     }
34     catch (FileNotFoundException fnf)
35     {
36       visMelding("Finner ikke fil " + filsti);
37     }
38     catch (IOException ioe)
39     {
40       visMelding("Problem med å lese fra fil" + filsti);
41     }
42   }
43
44   public Dimension getPreferredSize()
45   {
46     Toolkit verktøykasse = Toolkit.getDefaultToolkit();
47     Dimension skjermdimensjon = verktøykasse.getScreenSize();
48     int bredde = skjermdimensjon.width;
49     int høyde = skjermdimensjon.height;
50     return new Dimension(bredde - 50, høyde - 100);
51   }
52
53   public void visMelding(String melding)
54   {
55     JOptionPane.showMessageDialog(null, melding, "Filproblem",
56             JOptionPane.ERROR_MESSAGE);
57   }
58
59   public String getFilnavn()
60   {
61     return filnavn;
62   }
63 }

Vindusklasse for programmet er definert i Filvisningsvindu.java. For å få valgt en ny fil, er følgende menyalternativ definert:

22     JMenuItem visFil = new JMenuItem("Velg fil");
23     visFil.setMnemonic('V');
24     visFil.addActionListener(
25             new ActionListener()
26             {
27               public void actionPerformed(ActionEvent e)
28               {
29                 File fil = velgFil();
30                 if (fil != null)
31                   visFil(fil);
32                 else
33                   JOptionPane.showMessageDialog(Filvisningsvindu.this,
34                           "Ingen vil valgt!", "Advarsel",
35                           JOptionPane.ERROR_MESSAGE);
36               }
37             });

Metoden velgFil bruker på vanlig måte en JFileChooser for å velge fil. Metoden visFil ser ut som følger:

79   public void visFil(File f)
80   {
81     Filpanel filviser = new Filpanel(f);
82     filsamling.addTab(f.getName(), filviser);
83     Fanepanel fane = new Fanepanel(filsamling, filviser, f.getName());
84     filsamling.setTabComponentAt(filsamling.getTabCount() - 1, fane);
85     filsamling.setSelectedIndex(filsamling.getTabCount() - 1);
86     filviser.visFil(f.getPath());
87   }

Som vi ser, tilføyer den en ny fane med tilhørende fanepanel og innhold til fanekortet. Den gjør også kall på metoden som åpner fila og viser dens innhold. Filnavnet blir tilordnet som fanetekst.

Driverklasse for programmet finnes i Filvisningstest.java.

Copyright © Kjetil Grønning, 2012

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