![]() |
![]() |
Start på kapittel om grafiske brukergrensesnitt |
FlowLayout
BorderLayout
GridLayout
Layout-managere bruker vi i og på containere for å plassere komponenter på
ønsket måte. Som container har vi hittil bare brukt vinduets
contentPane, men mer vanlig er det å gruppere komponenter i andre containere,
og så til slutt plassere disse i contentPane. En mye brukt containertype er
panel, det vil si objekt av type JPanel
. Vinduets forhåndssatte contentPane
er også av denne typen.
Når det gjelder layout-managere, finnes det i klassebibliotekets pakke
java.awt
følgende fem klasser som definerer slike:
FlowLayout BorderLayout CardLayout GridLayout GridBagLayout
I tillegg finnes i pakken javax.swing
klassene
BoxLayout SpringLayout GroupLayout
Det å skrive for hånd kode for å lage en god og fleksibel layout kan være ganske krevende.
Programutviklingsverktøy som Eclipse og NetBeans har innebygget designverktøy
som kan brukes for å lage layout. Javakode for layout'en blir da generert automatisk
av det verktøyet en bruker. Layouttypen GroupLayout
er spesielt laget
for slike designverktøy, men det går også an å bruke den når en skriver layoutkode selv.
Dersom en skriver layoutkode selv og ikke bruker GroupLayout
, er det typen
GridBagLayout
som er den kraftigste og mest fleksible, men den er
også, ved sida av GroupLayout
, den som er mest komplisert å sette seg inn i
og skrive kode for. Mye av de effektene og den fleksibiliteten som en kan få til
ved å bruke GroupLayout
eller GridBagLayout
, kan en imidlertid
få til på en enklere måte ved å bruke BoxLayout
på flere forskjellige
paneler og bygge et hierarki av slike paneler ved å legge dem inni hverandre.
Vi skal i denne omgang se litt nærmere på hvordan vi kan skrive kode for å bruke
de enkle typene FlowLayout, BorderLayout
og GridLayout
.
FlowLayout
FlowLayout
er default-layout for paneler. Det
er den enkleste typen layout og er den eneste
vi har brukt hittil. Komponentene blir plassert etter hverandre på rader
i den rekkefølge vi gjør kall på containerens add
-metode for å
plassere komponentene. Når en rad er full, blir neste komponent automatisk
plassert på en ny rad. Hvor mange komponenter det er plass til på en rad er
bestemt av komponentenes bredde og containerens bredde. Når vi oppretter
layout-objektet ved å bruke default-konstruktøren (uten parametre), vil vi
få default-visning. I denne er hver komponentrad horisontalt sentrert innenfor
containeren og det er en avstand på 5 piksler mellom komponentene.
Vi har imidlertid mulighet for å tilpasse default-visningen. Den horisontale
plasseringen innenfor hver rad kan vi bestemme ved hjelp av de tre konstantene
FlowLayout.LEFT, FlowLayout.CENTER
og FlowLayout.RIGHT
som gir
henholdsvis venstrejustering, sentrering (default-plassering) og høyrejustering.
Konstantene kan brukes enten som konstruktørparametre når vi oppretter
FlowLayout
-objektet, eller de kan brukes som parametre i kall
på layout-objektets metode setAlignment
. Avstanden mellom
komponentene kan vi bestemme enten ved å bruke to ekstra konstruktørparametre
(for horisontal og vertikal avstand i antall piksler) i tillegg til parameteren
for horisontal justering, eller ved kall på layout-objektets to metoder
setHgap
og setVgap
med ønsket avstand som parameter.
Programmet
FlowLayoutFrame.java
som er gjengitt nedenfor er en omarbeiding av eksemplet som finnes i Fig. 14.39
i 9. utgave av læreboka til Deitel & Deitel. I programvinduet er det lagt
inn tre knapper ved bruk av FlowLayout
. Når man klikker på de
tre knappene, veksler layouten mellom venstrejustering, sentrering og
høyrejustering som vist på bildene nedenfor, der det første bildet viser sentrering.
For at vinduet skal bli tegnet
ut på nytt med annen horisontal justering som følge av at man klikker på en
av disse knappene, er det nødvendig å gjøre kall på layout-objektets metode
layoutContainer
med vedkommende container som parameter, i vårt
tilfelle variabelen container
som er en referanse til contentPane.
Driverklasse for programmet finnes i fila
FlowLayoutDemo.java
.
1 import java.awt.FlowLayout; 2 import java.awt.Container; 3 import java.awt.event.ActionListener; 4 import java.awt.event.ActionEvent; 5 import javax.swing.JFrame; 6 import javax.swing.JButton; 7 8 //Demonstrating FlowLayout alignments. 9 public class FlowLayoutFrame extends JFrame 10 { 11 private JButton leftJButton; // button to set alignment left 12 private JButton centerJButton; // button to set alignment center 13 private JButton rightJButton; // button to set alignment right 14 private FlowLayout layout; // layout object 15 private Container container; // container to set layout 16 17 // set up GUI and register button listeners 18 public FlowLayoutFrame() 19 { 20 super( "FlowLayout Demo" ); 21 22 layout = new FlowLayout(); 23 container = getContentPane(); 24 setLayout( layout ); 25 Knappelytter lytter = new Knappelytter(); 26 27 leftJButton = new JButton( "Left" ); 28 add( leftJButton ); 29 leftJButton.addActionListener( lytter ); 30 31 centerJButton = new JButton( "Center" ); 32 add( centerJButton ); 33 centerJButton.addActionListener( lytter ); 34 35 rightJButton = new JButton( "Right" ); 36 add( rightJButton ); 37 rightJButton.addActionListener( lytter ); 38 } // end FlowLayoutFrame constructor 39 40 private class Knappelytter implements ActionListener 41 { 42 public void actionPerformed( ActionEvent event ) 43 { 44 if ( event.getSource() == leftJButton ) 45 layout.setAlignment( FlowLayout.LEFT ); 46 else if ( event.getSource() == centerJButton ) 47 layout.setAlignment( FlowLayout.CENTER ); 48 else if ( event.getSource() == rightJButton ) 49 layout.setAlignment( FlowLayout.RIGHT ); 50 51 // realign attached components 52 layout.layoutContainer( container ); 53 } 54 } // end Knappelytter 55 } // end class FlowLayoutFrame
BorderLayout
BorderLayout
er default-layout for vinduers forhåndssatte contentPane.
Den deler inn container-feltet i fem posisjoner som vist på følgende bilde
der det er plassert en knapp i hver posisjon:
Posisjonene angis med de fem BorderLayout
-konstantene som
er skrevet på knappene ovenfor. I java-versjoner før versjon 1.4 ble
posisjonene angitt med konstantene NORTH, SOUTH, WEST, CENTER, EAST
.
Disse kan fortsatt brukes, men de som er vist på knappene blir nå anbefalt fordi de er
standard. De blir dessuten av java-systemet brukt slik at de tilpasser seg språkområder
som har andre regler for hva som er starten og slutten på sider og linjer.
Når vi bruker BorderLayout
, trenger vi ikke ta i bruk alle
de fem posisjonene. Vi kan velge hvor mange vi vil bruke, men har altså
maksimalt fem tilgjengelige posisjoner, og vi kan bare plassere én
komponent i hver posisjon. Det er imidlertid ingen ting i
veien for å plassere paneler (eller andre typer containere) i en eller flere
av posisjonene, og i disse panelene kan vi legge inn mange komponenter.
På det viste bildet ser vi at de fem knappene fyller ut hele containeren
som de er lagt inn i. Dette er alltid tilfelle når vi bruker
BorderLayout
, uavhengig av hvor mange av de fem posisjonene vi tar
i bruk, med unntak av ett tilfelle: Dersom CENTER
-posisjonen er
den eneste som ikke er i bruk, så kan det i denne bli et tomt felt. Dersom
vi endrer størrelsen på vinduet (eller på containeren som bruker
BorderLayout
), så vil de plasserte komponentene strekke seg slik
at tilgjengelig plass igjen blir fylt opp. Nord- og sør-komponentene kan strekke
seg horisontalt, vest- og øst-komponentene kan strekke seg vertikalt, mens
senter-komponenten kan strekke seg både horisontalt og vertikalt.
Dersom vi ikke spesifiserer noe annet, vil komponentene bli lagt tett
inntil hverandre. Men det er anledning til å spesifisere horisontal og
vertikal avstand mellom komponentene, målt i antall piksler. Vi kan enten
gjøre det ved hjelp av konstruktørparametre, eller vi kan på layout-objektet
bruke metodene setHgap, setVgap
. For layouten på det viste bildet
er det spesifisert en avstand på 5 piksler mellom komponentene, både
horisontalt og vertikalt. Men vi ser at komponentene ikke blir plassert med
denne avstanden fra kanten av vinduet, den gjelder bare for avstanden mellom
komponentene. (Avstand mot kanten av vinduet er det mulig å få til ved å legge
inn en ramme, se notatet Litt om bruk av rammer.)
Når vi skal legge inn en komponent i en container som har
BorderLayout
, må vi ved kall på containerens
add
-metode angi hvilken posisjon komponenten skal plasseres i.
Det gjør vi ved at vi i tillegg til parameteren for vedkommende komponent
har en parameter for posisjonen. Det vil være
BorderLayout.CENTER
i tilfelle senter-posisjon, og tilsvarende
for de andre posisjonene.
Bildet som er vist ovenfor, er hentet fra programmet
BorderLayoutFrame.java
som er gjengitt nedenfor. Knappene er programmert til å virke på den måten at
når man klikker på en knapp, så blir vinduet tegnet ut på nytt, og
den knappen som man klikket på blir ikke vist i den nye uttegningen, slik at
det bare er fire av de fem plassene i vinduets BorderLayout
som
er i bruk. Man kan derfor få sett hvordan vinduets utseende endrer seg med
hvilken posisjon som ikke er i bruk. Dersom man for eksempel klikker på
knappen i posisjon LINE_START
, så får man et vindu som ser ut
slik:
Programmet er en modifikasjon av det som
finnes i Fig. 14.42 i 9. utgave av læreboka til Deitel & Deitel.
Driverklasse for programmet finnes i fila
BorderLayoutDemo.java
.
1 import java.awt.BorderLayout; 2 import java.awt.event.ActionListener; 3 import java.awt.event.ActionEvent; 4 import javax.swing.JFrame; 5 import javax.swing.JButton; 6 import java.awt.Container; 7 8 //Demonstrating BorderLayout. 9 public class BorderLayoutFrame extends JFrame implements ActionListener 10 { 11 private JButton buttons[]; 12 private final String names[] = { "Skjul posisjon PAGE_START", 13 "Skjul posisjon PAGE_END", "Skjul posisjon LINE_END", 14 "Skjul posisjon LINE_START", "Skjul posisjon CENTER" }; 15 private BorderLayout layout; 16 private Container c; 17 18 // set up GUI and event handling 19 public BorderLayoutFrame() 20 { 21 super( "BorderLayout Demo" ); 22 23 layout = new BorderLayout( 5, 5 ); // 5 pixel gaps 24 c = getContentPane(); 25 c.setLayout( layout ); 26 buttons = new JButton[ names.length ]; 27 28 // create JButtons and register listeners for them 29 for ( int count = 0; count < names.length; count++ ) 30 { 31 buttons[ count ] = new JButton( names[ count ] ); 32 buttons[ count ].addActionListener( this ); 33 } 34 35 c.add( buttons[ 0 ], BorderLayout.PAGE_START ); 36 c.add( buttons[ 1 ], BorderLayout.PAGE_END ); 37 c.add( buttons[ 2 ], BorderLayout.LINE_END ); 38 c.add( buttons[ 3 ], BorderLayout.LINE_START ); 39 c.add( buttons[ 4 ], BorderLayout.CENTER ); 40 } // end BorderLayoutFrame constructor 41 42 // handle button events 43 public void actionPerformed( ActionEvent event ) 44 { 45 // check event source and layout content pane correspondingly 46 for ( int i = 0; i < buttons.length; i++ ) 47 { 48 if ( event.getSource() == buttons[ i ] ) 49 buttons[ i ].setVisible( false ); // hide button clicked 50 else 51 buttons[ i ].setVisible( true ); // show other buttons 52 } 53 54 layout.layoutContainer( c ); // layout content pane 55 } // end method actionPerformed 56 } // end class BorderLayoutFrame
Et vindus contentPane er som allerede nevnt en container av type
JPanel
, som altså har FlowLayout
som default-layout.
Den metoden (createContentPane
i klassen JRootPane
)
som oppretter contentPane, setter BorderLayout
på den. For øvrig
står vi fritt i selv å bestemme hvilken container som skal være contentPane (ved kall på
vinduets metode setContentPane
). Det er krav om at contentPane må
være en ugjennomsiktig (opaque) JComponent
.
Som vi så av programeksemplet ovenfor, vil knapper som blir lagt direkte inn i en
posisjon i en BorderLayout
strekke seg slik at de fyller ut hele plassen
for vedkommende posisjon i layouten. I mange tilfelle vil knappene da få en form og
størrelse som ikke tar seg så godt ut. En bedre løsning vil det da være å legge knappen
(eventuelt flere knapper) inn i et panel som vi så plasserer i vedkommende posisjon
i BorderLayout
'en. Da vil det være panelet som strekker seg, og ikke
knappen. På panelet kan vi sette akkurat den layout vi ønsker. I utgangspunktet
har det FlowLayout
.
GridLayout
GridLayout
plasserer komponentene i rader og kolonner som i
en tabell. Det vanlige er at vi i konstruktøren spesifiserer hvor mange
rader og kolonner vi vil ha:
new GridLayout( antRader, antKolonner );
Det virker imidlertid på den måten at dersom begge parametre har fått verdier forskjellig fra 0, så blir det spesifiserte antall kolonner ignorert. Antall kolonner vil da bli bestemt av antall rader og det totale antall komponenter som blir lagt inn i layouten. Dersom det for eksempel er spesifisert 3 rader og 2 kolonner, mens 9 komponenter blir lagt inn, så vil disse bli lagt ut i 3 rader og 3 kolonner. Dersom vi ønsker et bestemt antall kolonner, så må parameteren for antall rader settes til 0.
Alle cellene (og dermed alle komponentene som blir lagt inn) i en
GridLayout
blir gitt samme størrelse. Når vi legger inn
komponenter ved bruk av containerens add
-metode, vil radene bli
fylt opp fra venstre mot høyre rad for rad nedover. Det er tillatt å ha
ledige plasser på slutten. Det er også mulig å få tomme plasser innimellom de
plasserte komponentene. For å få til det, legger vi inn "fyllkomponenter"
som vi gjør usynlige ved en instruksjon av type
komponent.setVisible( false );
Som i tilfelle BorderLayout
vil komponentene ligge tett
inntil hverandre, med mindre vi bestemmer noe annet. Det kan vi gjøre enten ved
å bruke to ekstra konstruktørparametre for horisontal og vertikal avstand,
eller ved bruk av layout-objektets metoder setHgap, setVgap
.
Programmet
GridLayoutFrame.java
som er gjengitt nedenfor, er som eksemplet i Fig. 14.43 i 9. utgave av
læreboka til Deitel & Deitel, bortsett fra at noen kommentarer er fjernet.
Det blir for vinduet opprettet to forskjellige layouter av type
GridLayout
, den ene med to rader og tre kolonner, mens det for
den andre er omvendt. For den første er det dessuten bestemt en avstand på
5 piksler mellom komponentene, både horisontalt og vertikalt, mens det for
den andre brukes default-visning med ingen avstand mellom komponentene.
I vinduet blir det lagt inn seks knapper. Knappene er
programmert til å virke på den måten at hver gang man klikker på en knapp, så
blir vinduet tilordnet den layouten som ikke brukes for øyeblikket, og så blir
vinduet tegnet ut på nytt. Det oppnås ved kall på containerens (i dette tilfelle
contentPane) metode validate
. (Legg merke til at denne metoden
er en container-metode, mens metoden layoutContainer
som ble
brukt i eksemplene på FlowLayout
og BorderLayout
er
en layout-metode.) Veksling mellom de to layoutene får man til ved å bruke
den logiske variabelen toggle
som en av/på-bryter: Hver gang det
blir klikket på en knapp blir variabelens verdi brukt til å avgjøre hvilken
layout som nå skal tilordnes og vises, og så blir variabelen
gitt sin motsatte verdi, slik at det neste gang blir den andre layouten. De
to layoutene er vist i vindusbildene nedenfor. Driverklasse for programmet
finnes i fila
GridLayoutDemo.java
.
1 import java.awt.GridLayout; 2 import java.awt.Container; 3 import java.awt.event.ActionListener; 4 import java.awt.event.ActionEvent; 5 import javax.swing.JFrame; 6 import javax.swing.JButton; 7 8 //Demonstrating GridLayout. 9 public class GridLayoutFrame extends JFrame implements ActionListener 10 { 11 private JButton buttons[]; 12 private final String names[] = 13 { "one", "two", "three", "four", "five", "six" }; 14 private boolean toggle = true; // toggle between two layouts 15 private Container container; 16 private GridLayout gridLayout1; // first gridlayout 17 private GridLayout gridLayout2; // second gridlayout 18 19 public GridLayoutFrame() 20 { 21 super( "GridLayout Demo" ); 22 gridLayout1 = new GridLayout( 2, 3, 5, 5 ); // 2 by 3; gaps of 5 23 gridLayout2 = new GridLayout( 3, 2 ); // 3 by 2; no gaps 24 container = getContentPane(); 25 container.setLayout( gridLayout1 ); 26 buttons = new JButton[ names.length ]; 27 28 for ( int count = 0; count < names.length; count++ ) 29 { 30 buttons[ count ] = new JButton( names[ count ] ); 31 buttons[ count ].addActionListener( this ); 32 container.add( buttons[ count ] ); 33 } 34 } // end GridLayoutFrame constructor 35 36 // handle button events by toggling between layouts 37 public void actionPerformed( ActionEvent event ) 38 { 39 if ( toggle ) 40 container.setLayout( gridLayout2 ); // set layout to second 41 else 42 container.setLayout( gridLayout1 ); // set layout to first 43 44 toggle = !toggle; // set toggle to opposite value 45 container.validate(); // re-layout container 46 } // end method actionPerformed 47 } // end class GridLayoutFrame
Et panel er definert av klassen
JPanel
,
som er subklasse til
JComponent
.
Den er i sin tur subklasse til
Container
.
Det betyr at et panel både er en container og en komponent. Det er derfor
tillatt å plassere paneler inni paneler. På paneler kan vi sette både de tre
layoutene som vi har sett nærmere på ovenfor, og flere andre. Og på de
forskjellige panelene som vi legger inni hverandre kan vi bruke forskjellige
layouter og ha forskjellig antall komponenter i hvert panel. Dette gir stor
fleksibilitet for plassering av komponenter.
Som allerede nevnt, så har paneler FlowLayout
som default-layout.
Det vil si at når vi oppretter et panel ved å bruke default-konstruktør:
JPanel panel = new JPanel();
så vil panelet ha FlowLayout
. Men vi kan lett sette en hvilken
som helst annen layout på panelet ved å bruke metoden setLayout
med
ønsket layout-objekt som aktuell parameter, eller ved å bruke ønsket layout
som konstruktørparameter, som i eksemplet
JPanel panel = new JPanel(new BorderLayout()); //å foretrekke!
Det siste alternativet er av effektivitetshensyn absolutt å foretrekke, for
da unngår vi at det blir opprettet et FlowLayout
-objekt som likevel ikke
skal brukes.
Dette opplegget fungerer imidlertid ikke dersom vi ønsker å sette BoxLayout
,
for bruk av den forutsetter at det allerede eksisterer en container,
se notat om bruk av BoxLayout
.
Paneler kan også brukes som egne tegneflater, slik at vi kan unngå at uttegning av bilder og annen grafikk kommer i konflikt med andre komponenter. Hvordan dette gjøres, har vi sett på tidligere, se Panel brukt som tegneflate.
Det er mulig å plassere komponenter i en container uten å gjøre bruk av layout-manager, men dette blir ikke anbefalt. Grunnen er at en layout-manager gjør det lettere å tilpasse størrelse og utseende på komponenter til forskjellige plattformer og omgivelser, til forskjellige fontstørrelser, til varierende størrelse og oppløsning på containere og vinduer, og til forskjellige lokaliteter. Dessuten kan layout-managere brukes om igjen av andre containere, og også av andre programmer.
Dersom du ønsker å bruke absolutt posisjonering så finnes det nærmere beskrivelse av framgangsmåten for dette i The Java Tutorials.
Copyright © Kjetil Grønning, revidert av Eva Hadler Vihovde 2014![]() |
![]() |
Start på kapittel om grafiske brukergrensesnitt |