|
| Når man taler med begyndere, der laver GUI-programmring i Java,
støder man ofte på den holdning, at det er grimt! De har
problemer med at få de grafiske komponenter placeret præcist
hvor de ønsker det, er jeg har set de mest utrolige konstruktioner,
der skulle tvinge Java til at gøre som de ville. De problemer de
støder på, har deres rod i manglende kendskab til layout-managere.
|
|
| Der findes seks layout-managere: |
| Java's seks layout-managere
|
FlowLayout
BorderLayout
GridLayout
BoxLayout
CardLayout
GridBagLayout
|
|
|
| Antallet gør, at mange vælger kun at lære om de første
2-3 stykker og dernæst begynde at lave applikationer. Det er
katastrofalt! |
|
| Problemet ligger i den sidste layout-manager: GridBagLayout.
Hvis man ikke lærer at bruge GridBagLayout
kan man lige så godt glemme det - man får aldrig lavet en
grafisk brugergrænseflade der er værd at se på. De fem
første layout-managere er simple, mens GridBagLayout
er mere kompliceret. Det bør ikke afholde én fra at lære
at bruge den, for uden den, kan man lige så godt lade være
med at beskræftige sig med GUI-programmering i Java! |
| GUI Buildere
| Nogle vælger et andet alternativ: GUI-Buildere. En GUI-Builder
er et visuelt værktøj, hvor man kan opbygge grænsefladerne
ved at placere de grafiske komponenter med musen og efterfølgende
generere en file som indeholder GUI-koden. Denne file kan man så
føje til sin applikation og anvende den. Dermed behøver
man ikke beskæftige sig med layout-managere, da denne del af koden
laves automatisk. |
|
| Det er udemærket med en GUI-Builder, men ...! |
|
| Hvis man skal kunne bruge det resultat en GUI-Builder genererer, skal
man vide hvad det er den laver, ellers kan man ikke kæde det sammen
med resten af programmet. Det er derfor bydende nødvendigt, at
man først lærer at lave "rigtig" GUI-Programmering.
Når man har lært det, kan man begynde at bruge GUI-Buildere,
som kan være tidsbesparende og nyttige i prototyping. |
|
| Jeg har set flere projekter, hvor de studerende er stoppet efter designet
af brugergrænsefladerne, med ordene: "Vi kunne desværre
ikke nå at implementere nogen af grænsefladerne". Situationen
er normalt den samme: De har brugt en GUI-Builder. Man sidder med tanken
i baghovedet: "De har ikke implementeret noget, fordi de ikke kan
forstå hvad det er GUI-Builderen har lavet og de aner ikke hvordan
de skal få det til at fungere sammen med deres program". |
|
| Den mest katastrofale indlæringsproces man kan opleve i forbindelse
Java og GUI-programmering er: |
|
|
|
- Lær kun de mest simple layout-managere
at kende
- Bliv irriteret over hvor "dumme" de er
- Slå alle layout-managere fra
- Placer alt manuelt med pixels nøjagtighed
- Bliv træt af den tid det tager (det tager en evighed at
justere)
- Brug en GUI-Builder
- Bliv frustreret over ikke at kunne få det til at fungere
sammen med sit program
- Begynde at programmere Visual Basic
- Lide hjernedøden :-)
|
|
| Lær det ordentligt!
| Jeg har set ovenstående forløb utallige gange, og vil derfor
kraftigt understrege vigtigheden af at lære GUI-programmering
ordentlig! |
|
| Hvis man ikke har tid til, i første omgang, at lære samtlige
layout-managere at kende, vil jeg anbefale at man starter med følgende
tre layout-managere: |
|
|
FlowLayout
BorderLayout
GridBagLayout
|
|
|
| I særdeleshed skal man give sig tid til at studere den sidste,
med mindre udseendet af éns brugergrænseflader er ligegyldigt
(det kan det være, hvis man blot vil studere grafiske komponenter
og event-håndtering) |
|
|
1. Hvordan man "sætter" en layout
manager
|
|
| Når man ønsker at anvende en layout-manager i forbindelse
med en container (typisk en JFrame
eller et JPanel),
gør man det samme uanset hvilken layout-manager man bruger. Derfor
vil vi berøre dette, før vi ser på den første
layout-manager. |
|
| Selve layout-manageren er en instans af en af de seks layout-klasser.
Hvis vi f.eks. har et JPanel
og ønsker at give det en FlowLayout-manager
gøres det med: |
|
|
JPanel panel = ...
...
panel.setLayout( new FlowLayout() );
|
|
|
| Her er anvendt metoden setLayout:
|
|
|
void setLayout( LayoutManager manager )
|
|
|
| der er erklæret i superklassen Container. |
|
| For en JFrame
er det lidt anderledes. Her skal man ikke kalde setLayout
på selve JFrame'n,
men i stedet gør det på ContentPane. Hvis vi f.eks. vil sætte
en JFrame til at
bruge et FlowLayout
gøres det typiske ved at placere følgende linie i konstruktoren: |
|
|
getContentPane().setLayout( new FlowLayout() );
|
|
|
| Man skal sætte layout-manageren før man begynder at tilføje
komponenter til containeren. |
|
| JPanel og JFrame
har hver deres default-layout. For JPanel
er det FlowLayout,
og for JFrame er
det BorderLayout.
Man behøver ikke sætte noget layout; hvis man er tilfreds
med disse i forbindelse med JPanel
henholdvis JFrame. |
|
|
2. java.awt.FlowLayout
|
|
| FlowLayout er
default-layout for JPanel. |
|
|
2.1 Design
|
|
| FlowLayout er
den mest enkle af de seks layout managere. Det "flow" der indgår
i navnet, er den sammenhæng man finder i almindelig tekst på
et stykke papir; hvor ordene kommer i en "lind strøm".
Man skriver fra venstre mod højre, og når der ikke er mere
plads skifter man til næste linie. |
|
| Det samme er tilfældet i FlowLayout.
Her placeres de grafiske komponenter efter hinanden, som på en "linie",
og man skifter til næste "linie" når det næste
komponent ikke kan være inden for "rammen". |
|
| Det eneste man kan indstille i FlowLayout,
er om komponenterne skal være venstre-, højre- eller midter-stillede.
Default er midter-stillede komponenter. |
|
| Betragt følgende fire vinduer, der illustrerer default, og de
tre indstillinger af det såkaldte alignment. |
Figur 1:
De tre alignments
|
|
|
| I vinduerne har vi placeret otte knapper med tiltagende størrelse. |
|
|
2.2 Anvendelse
|
|
| Man sætter alignment med følgende metode: |
|
|
void setAlignment( int align )
|
|
|
| på layout-manageren. |
|
| align skal være
en af konstanterne:
|
|
|
FlowLayout.LEFT
FlowLayout.CENTER
FlowLayout.RIGHT
|
|
|
| Man skal dog være opmærksom på at dette metode-kald
alene, ikke ændrer noget. Der skal efterfølgende laves
et kald af følgende metode (ligeledes på layout-manageren):
|
|
|
void layoutContainer( Container target )
|
|
|
| hvor target er den container
som layout-manageren er tilknyttet. |
|
| Følgende stykke kode, skitserer hvordan man kunne sætte
et FlowLayout på
en JFrame, med alignment
til højre: |
|
|
class FlowFrame extends JFrame ... {
...
private FlowLayout layout;
public FlowFrame( ... ) {
super( ... );
layout = new FlowLayout();
getContentPane().setLayout( layout );
...
}
...
layout.setAlignment( FlowLayout.RIGHT );
layout.layoutContainer( getContentPane() );
...
}
|
|
|
| I eksemplet indikeres det, at man sætter alignment (til højre)
på et senere tidspunkt end ved udførelse af konstruktoren.
Hvis man sætter alignment i forbindelse med konstruktoren, og før
man tilføjer komponenter, er det ikke nødvendigt at kalde
layoutContainer. |
|
|
3. java.awt.BorderLayout
|
|
| BorderLayout er
default-layout for JFrame.
Ønsker man derfor at anvende et BorderLayout
i en JFrame, behøver
man ikke sætte en layout manager. |
|
|
3.1 Design
|
|
| BorderLayout arbejder
ikke med linier, men med områder. Den inddeler i fem felter, der
er har forskellige egenskaber. De fire af felterne er opkaldt efter de
fire verdenshjørner og det femte hedder CENTER. Denne to-deling
af felterne finder man også i deres adfærd i forbindelse med
komponenter. CENTER-feltet opfører sig på én måde
og de fire andre felter på en anden måde. |
|
| Følgende vindue illustrerer opdelingen i de fem felter: |
Figur 2:
De fem felter
|
|
|
| Alle fem felter kan kun indeholde ét komponent. I eksemplet her,
er der placeret en JButton,
med det tilhørende navn, i hvert områder. |
|
| De fire verdenshjørner deles ikke om hjørnerne. Da felterne
naturligvis er firkantede, er det geometrisk umuligt for dem at mødes
i hjørnerne. Man har derfor vedtaget at hjørnerne tilhører
NORTH og SOUTH. |
|
| De fire verdenshjørner vil pakke sig ud til siderne. De
vil ikke brede sig ud på ledig plads. Al overskydende plads vil
blive tildelt CENTER, der breder sig. Hvis man resizer overstående
vindue så det f.eks. fylder hele skærmen vil WEST og EAST
stadig have samme bredde som på figuren, og NORTH og SOUTH vil stadig
have samme højde. CENTER vil derimod brede sig, så den fylder
det meste af skærmen. |
|
| Denne tilbageholdenhed, der udvises fra verdenshjørnernes side
er så dominerende, at de end ikke breder sig når CENTER ikke
har noget komponent. Hvis vi fjerner knappen fra CENTER-feltet bliver
billedet: |
Figur 3:
Uden CENTER-felt
|
|
|
| De fire verdenshjørner fylder nøjagtig det samme som før
og CENTER-feltet står tomt tilbage. |
|
| Verdenshjørnerne breder sig kun på ledig plads, hvis denne
ledige plads opstår ved at andre verdenshjørner mangler.
Hvis f.eks. NORTH ikke indeholder noget komponent vil WEST, CENTER og
EAST alle brede sig mod nord. |
|
| Hvis vi fjerner knappen fra NORTH-feltet, og tilføjer CENTER-knappen
igen, bliver billedet: |
Figur 4:
Uden NORTH-felt
|
|
|
| Man ser at WEST og EAST nu breder sig ud til hjørnerne. Man bemærker
også at det ville være geometrisk umuligt for CENTER-feltet
af bemægtige sig disse hjørne-områder, da det ville
kræve at WEST og EAST også blev fjernet. |
|
| Når CENTER-feltet kan tage det hele, bliver der ikke noget til
de andre felter. |
|
| Hvis vi f.eks. fjernede WEST, og havde komponenter i alle andre felter,
ville billedet være: |
Figur 5:
Uden WEST-felt
|
|
|
| Her har CENTER-feltet bredt sig, og taget den ledige plads. |
|
| Lad os se hvordan billedet bliver hvis vi fjerner flere af verdenshjørnerne.
Hvis vi f.eks. fjerner EAST og SOUTH bliver billedet: |
Figur 6:
Uden EAST- og SOUTH-felt
|
|
|
| Igen er det alene CENTER-feltet der breder sig mens de to andre er uændrede. |
|
| For til slut er understrege verdenshjørnernes tilbageholdenhed
vedrørende ledig plads, vil vi se billedet når kun WEST har
et komponent: |
Figur 7:
WEST-feltet alene
|
|
|
| WEST står urokkelig pakket ud til venstre og ignorerer den ledige
plads. |
|
|
3.2 Anvendelse
|
|
| Selve opbygningen og placeringen af de fem knapper i de fem felter laves
med følgende linier, placeret i framens konstruktor: |
|
|
getContentPane().setLayout( new BorderLayout() );
northButton = new JButton( "NORTH" );
eastButton = new JButton( "EAST" );
southButton = new JButton( "SOUTH" );
westButton = new JButton( "WEST" );
centerButton = new JButton( "CENTER" );
getContentPane().add( northButton, BorderLayout.NORTH );
getContentPane().add( eastButton, BorderLayout.EAST );
getContentPane().add( southButton, BorderLayout.SOUTH );
getContentPane().add( westButton, BorderLayout.WEST );
getContentPane().add( centerButton, BorderLayout.CENTER );
|
|
|
| Det eneste nye er add-metoden
med to parametre1,
og de fem konstanter, som optræder som anden parameter: |
|
|
void add( Component comp, Object constraints )
|
|
|
| Som det ses er signaturen meget abstrakt. Ikke alene kan man tilføje
et hvilket som helst komponent (også de gamle heavyweight komponenter),
men den anden parameter kan være hvad som helst. |
|
| I tilfældet med BorderLayout
er anden parameter en angivelse af, hvilket felt komponentet skal add'es
til. De fem konstanter, der er vist ovenfor, angiver hver en af de fem
felter, og der findes ikke andre. Interessant nok, er de fem konstanter
tekststrengs-konstanter. De har samme indhold som navnet på verdenshjørnet.
F.eks. er BorderLayout.WEST
lig med "West", og man kunne i stedet vælge at bruge denne
tekststreng, men bør lade være. |
|
| Default er BorderLayout.CENTER.
Vi kunne derfor ændre add-kaldet
ovenfor til: |
|
|
getContentPane().add( centerButton );
|
|
|
|
4. java.awt.GridLayout
|
|
| Idéen i GridLayout
er at inddele det hele i rektangulære områder af samme størrelse
og placere ét komponent i hvert. Som f.eks. følgende vindue
med tolv JButton's. |
Figur 8:
Tolv knapper i GridLayout
|
|
|
| Brugergrænsefladen efterligner de grundlæggende taster på
min telefon. |
|
| For at lave vinduet skal man angive hvor mange rækker og kolonner
der skal være. Efterfølgende "hælder" man
komponenterne i, i den rigtige rækkefølge, der følger
læseretningen som vi kender den fra FlowLayout. |
|
| Konstruktoren til ovenstående frame har følgende indhold. |
|
|
public GridFrame( String title ) {
super( title );
getContentPane().setLayout( new GridLayout( 4, 3 ) );
getContentPane().add( new JButton( "1" ) );
getContentPane().add( new JButton( "2" ) );
getContentPane().add( new JButton( "3" ) );
getContentPane().add( new JButton( "4" ) );
getContentPane().add( new JButton( "5" ) );
getContentPane().add( new JButton( "6" ) );
getContentPane().add( new JButton( "7" ) );
getContentPane().add( new JButton( "8" ) );
getContentPane().add( new JButton( "9" ) );
getContentPane().add( new JButton( "*" ) );
getContentPane().add( new JButton( "0" ) );
getContentPane().add( new JButton( "#" ) );
addWindowListener( new ApplicationTerminator() );
pack();
setVisible( true );
}
|
|
|
| Selve indholdet af denne konstruktor understreger det monotone og ensartede
i GridLayout. |
|
| GridLayout har følgende
konstruktorer: |
|
|
GridLayout()
GridLayout( int rows, int columns )
GridLayout( int rows, int columns, int hgap, int vgap )
|
|
|
| rows og columns
er naturligvis rækker og kolonner (rækker er "højden",
kolonner er "bredden"). Default-konstruktoren svarer til et
kald af den anden konstruktor med: |
|
|
|
|
| Højest én af de to parametre rows
og columns kan være
0. Værdien 0 betyder, at der er "uendelig" mange pladser
i den pågældende retning. F.eks. betyder this-kaldet
ovenfor, at der er én række med uendelig mange kolonner. |
|
| hgap og vgap
betyder: horizontal gap og vertical gap. De angiver i pixels, hvor meget
padding der skal være mellem komponenterne. |
|
| Hvis vi i eksemplet med telefonen havde anvendt hgap
5 og vgap 10 ville vinduet
have fået følgende udseende: |
Figur 9:
Tolv knapper med hgap
og vgap
|
|
|
| Bemærk, at der ikke kommer noget "gap" mellem de grafiske
komponenter og containerens ydre grænser. |
|
|
5. javax.swing.BoxLayout
|
|
|
5.1 Design
|
|
| BoxLayout er et
relativ simpelt layout; hvis grundidé er at placere en række
grafiske komponenter i enten en vandret række: |
Figur 10:
Vandret række af grafiske komponenter
|
|
|
| eller en lodret række: |
Figur 11:
Lodret række af grafiske komponenter
|
|
|
| Som man måske kan se af eksemplet med knapperne, der er placeret
lodret, vil BoxLayout
ikke styre hvor meget de enkelte komponenter fylder. BoxLayout
prøver at give hvert komponent, netop den plads det selv ønsker
- derfor den forskellige bredde af knapperne (knapperne første
og anden er en
anelse breddere end de tre andre knapper). Af samme grund bliver knapperne
heller ikke bredt ud over den ledige plads i højre side af framen. |
|
| Swing anvender selv BoxLayout
(i form af en subklasse til BoxLayout)
til at ordne menupunkter på popup og almindelige menuer. |
|
|
5.2 Anvendelse
|
|
| Om de grafiske komponenter skal ordnes vandret eller lodret, angives
som parameter til konstruktoren:
|
|
|
BoxLayout( Container target, int axis )
|
|
|
| idet man som axis
bruger en af følgende to konstanter:
|
|
|
BoxLayout.X_AXIS
BoxLayout.Y_AXIS
|
|
|
| F.eks. kan man lave eksemplet, fra figur 11, på følgende
måde:
|
|
|
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class BoxLayoutFrame extends JFrame {
public BoxLayoutFrame( String title ) {
super( title );
BoxLayout layout = new BoxLayout( getContentPane(), BoxLayout.Y_AXIS );
getContentPane().setLayout( layout );
String[] nummer = { "første", "anden", "tredie", "fjerde", "femte" };
for ( int i=0; i<nummer.length; i++ )
getContentPane().add( new JButton( nummer[i] ) );
addWindowListener( new ApplicationTerminator() );
pack();
setVisible( true );
}
class ApplicationTerminator extends WindowAdapter {
public void windowClosing( WindowEvent e ) {
System.exit( 0 );
}
}
}
|
|
|
|
5.3 javax.swing.Box
|
|
| Box-klassen er
en hjælpeklasse, der kan være nyttig når man arbejder
med BoxLayout. Box
er grundlæggende et JComponent,
der er født med et BoxLayout,
og man kan bruge den som sådan. Ud over, at instanser af Box
er grafiske komponenter, har klassen en række statiske fabriks-metoder,
der kan være nyttige i forbindelse med BoxLayout,
og dermed ikke kun i forbindelse med instanser af Box: |
|
| |
|
|
6. java.awt.CardLayout
|
|
|
6.1 Design
|
|
| Den grundlæggende idé i CardLayout
er, at man har en bunke kort, hvoraf man altid kun kan se det øverste. |
|
| Man vælger som oftest at lade disse kort være instanser
af JPanel. Hvert panel
er en brugergrænseflade og ved at samle dem i en container der anvender
et CardLayout, bliver
det muligt at skifte hurtigt mellem de forskellige grænseflader. |
|
| Når man arbejder med kortene kan man gøre det enten med
iterator-metoder eller med absolut reference til hvert kort. Man kan også
blande de to teknikker, da de ikke udelukker hinanden. |
|
| Vi vil i det følgende opdele gennemgange efter disse to tilgangsvinkler
til kortene. |
|
|
6.2 Absolut reference
|
|
| Betragt følgende eksempel: |
Figur x:
Vindue med CardLayout
|
|
|
| Layoutet i dette vindue er opbygget med fem knapper i et GridLayout,
placeret i WEST i et BorderLayout.
I CENTER af dette BorderLayout
har vi placeret et JPanel
med et CardLayout og fem
kort, der hver indeholder et JLabel.
Panelet i CENTER har en
EmptyBorder på 50
pixels, som padding. |
|
| Eksemplet er lavet med følgende frame: |
|
|
class CardFrame extends JFrame implements ActionListener {
private CardLayout layout;
private JPanel panel;
public CardFrame( String title ) {
super( title );
getContentPane().setLayout( new BorderLayout() );
JPanel knapper = new JPanel();
knapper.setLayout( new GridLayout( 0, 1 ) );
panel = new JPanel();
layout = new CardLayout();
panel.setLayout( layout );
panel.setBorder( new EmptyBorder( 50, 50, 50, 50 ) );
String[] numre = { "Første", "Anden", "Tredie", "Fjerde", "Femte" };
for ( int i=0; i<numre.length; i++ ) {
String n = "" + (i+1);
JLabel label = new JLabel( numre[i] );
JButton b = new JButton( n );
b.addActionListener( this );
knapper.add( b );
panel.add( label, n );
}
getContentPane().add( panel, BorderLayout.CENTER );
getContentPane().add( knapper, BorderLayout.WEST );
addWindowListener( new WindowHandler() );
pack();
setVisible( true );
}
public void actionPerformed( ActionEvent e ) {
Object source = e.getSource();
if ( source instanceof JButton ) {
JButton b = (JButton) source;
layout.show( panel, b.getText() );
}
}
class WindowHandler extends WindowAdapter {
public void windowClosing( WindowEvent e ) {
dispose();
System.exit(0);
}
}
}
|
|
|
| I kildeteksten er markeret en række linier. Det er dem der vedrører
selve CardLayout. |
|
| Først giver vi panel
et CardLayout. Dernæst
laver vi i for-sætningen fem labels, der alle add'es
til panel. Hver af dem
er et kort, som vi add'er
sammen med en anden parameter. Denne parameter er en teksstreng, som bruges
som senere reference til kortet. |
|
| I actionPerformed henter
vi knappens tekst (som vi bekvemt nok brugte som tekststreng til senere
reference) og beder layoutet om at vise det tilsvarende kort med metoden: |
|
|
void show( Container container, String name )
|
|
|
| Ved hjælpe af denne metode, kan vi på et hvilket som helst
tidspunkt få vist et givent kort, blot vi har reference-navnet.
Som første parameter angiver man den container som CardLayout
er sat til. |
|
| Følgende figur illustrerer hvordan man med knapperne kan vælge
hvilket kort der vises: |
Figur x:
Sammenhæng mellem knapper og kort
|
|
|
|
6.3 Iterator-metoder
|
|
| Betragt følgende eksempel: |
Figur x:
Vindue med CardLayout
|
|
|
| Layoutet i dette vindue er opbygget på samme måde som i
foregående eksempel. Knapperne i WEST
er ændret så de svarer til de fire iterator-metoder der findes
i CardLayout. |
|
| Eksemplet er lavet med følgende frame: |
|
|
class CardFrame extends JFrame implements ActionListener {
private CardLayout layout;
private JPanel panel;
private JButton firstB, nextB, prevB, lastB;
public CardFrame( String title ) {
super( title );
getContentPane().setLayout( new BorderLayout() );
JPanel knapper = new JPanel();
knapper.setLayout( new GridLayout( 0, 1 ) );
panel = new JPanel();
layout = new CardLayout();
panel.setLayout( layout );
panel.setBorder( new EmptyBorder( 50, 50, 50, 50 ) );
String[] numre = { "Første", "Anden", "Tredie", "Fjerde", "Femte" };
for ( int i=0; i<numre.length; i++ ) {
String n = "" + (i+1);
JLabel label = new JLabel( numre[i] );
panel.add( label, n );
}
firstB = new JButton( "Første" );
firstB.addActionListener( this );
knapper.add( firstB );
nextB = new JButton( "Næste" );
nextB.addActionListener( this );
knapper.add( nextB );
prevB = new JButton( "Forrige" );
prevB.addActionListener( this );
knapper.add( prevB );
lastB = new JButton( "Sidste" );
lastB.addActionListener( this );
knapper.add( lastB );
getContentPane().add( panel, BorderLayout.CENTER );
getContentPane().add( knapper, BorderLayout.WEST );
addWindowListener( new WindowHandler() );
pack();
setVisible( true );
}
public void actionPerformed( ActionEvent e ) {
Object source = e.getSource();
if ( source == firstB )
layout.first( panel );
else if ( source == nextB )
layout.next( panel );
else if ( source == prevB )
layout.previous( panel );
else if ( source == lastB )
layout.last( panel );
}
class WindowHandler extends WindowAdapter {
public void windowClosing( WindowEvent e ) {
dispose();
System.exit(0);
}
}
}
|
|
|
| Man ser i actionPerformed
hvorledes de fire knapper svarer til hver deres iterator-metode. |
|
| Følgende figur illustrerer hvordan man med knapperne kan iterere
kortene: |
Figur x:
Iterering af kort med iterator-metoder
|
|
|
|
7. java.awt.GridBagLayout
|
|
| GridBagLayout er, med
flere længder, den mest komplekse af de seks layout-managere. Vi
vil derfor studere den på en lidt anden måde end de foregående.
Først vil vi se et eksempel på hvordan den kan anvendes og
dernæst vil vi gennemgå de enkelte detaljer i GridBagLayout
hver for sig. |
|
| [Det skal bemærkes af vi ikke behandler konstanterne
RELATIVE og REMAINDER.
Disse vil blive behandlet i en senere udgave af dette kapitel.] |
|
|
7.1 Eksempel
|
|
| Følgende eksempel illustrerer hvordan man arbejder sig frem mod
at realisere det design man ønsker vha. GridBagLayout.
Eksemplet er rimelig enkelt og vi berører ikke alle mulighederne
i GridBagLayout. |
|
| Når man vil opbygge en brugergrænseflade, er det naturligvis
altid nyttigt at tegne den. I forbindelse med GridBagLayout
er det endvidere nyttigt at gøre det på et stykke papir.
Man kan enten bruge pen og papir, eller et GUI-designprogram og lave en
udskrift. Jeg har valgt at lave en tegning i hånden: |
Figur x:
Vores design på papir
|
|
|
| Jeg har valgt at lave en frame med fem grafiske komponenter: et JTextField,
et JTextArea og tre JButton's.
På tegningen har jeg valgt at bruge disse navne, da eksemplet er
meget generelt. Ellers ville man normalt beskrive indhold/funktion. |
|
| På figuren er der indtegnet en række linier med rødt.
Disse linier viser de rækker og kolonner, der beskriver komponenternes
placering relativt i forhold til hinanden. Det første man gør
er at fastlægge disse linier og dermed beskrive placering og udstrækning
af de enkelt komponenter. |
|
| Ud fra linierne kan vi se at JTextField
skal være to bred og JTextArea
skal være tre høj. Bortset fra disse relative størrelser
skal alle andre dimensioner være én række eller kolonne. |
|
| Placeringerne angives med koordinaten for det øverste venstre
hjørne af komponentet. |
|
| Der er mange andre attributter der skal indstilles, før det kommer
til at ligne, men vi vil i første omgang holde os til disse. |
|
| I GridBagLayout skal
der disse egenskaber knyttes til det enkelte komponent. Det gøres
ved at lave et objekt for hvert komponent, der beskriver dets visuelle
egenskaber i relation til layoutet. Objektet er en instans af GridBagConstraints,
og det indeholder attributterne. |
|
| Der er fire attributter i denne klasse, som omhandler placering og udtrækning
af det grafiske komponent. Placeringen angives i gridx
og gridy, udstrækningen
i gridwidth og gridheight. |
|
| Det GridBagConstraints-objekt
vi skal bruge i forbindelse med JTextField
kunne vi f.eks. lave og effektuere med følgende kode: |
|
|
GridBagLayout layout = new GridBagLayout();
getContentPane().setLayout( layout );
...
GridBagConstraints con = new GridBagConstraints();
JTextField tekstFelt = new JTextField( "JTextField" );
con.gridx = 0; // positionens x-koordinat
con.gridy = 0; // positionens y-koordinat
con.gridwidth = 2; // udstrækning i bredden
con.gridheight = 1; // udstrækning i højden
layout.setConstraints( tekstFelt, con );
getContentPane().add( tekstFelt );
|
|
|
| Først laver vi naturligvis selve GridBagLayout
og sætter det. Dernæst laver vi en instans af GridBagConstraints
og JTextField. Vi sætter
de fire af GridBagConstraints
attributter, der beskriver placering og udstrækning af komponentet.
Inden vi add'er komponentet,
foretager vi et kald af setConstraints
på layout-manageren. Dette kald knytter komponentet og GridBagConstraints-objektet
sammen i layout-manageren, så den anvender det når den skal
vise komponentet. |
|
| Som man kan se er det mange linier for et komponent, og når vi
senere arbejder med flere af attributterne bliver kun værre. Man
kan evt. lave en eller flere service-metoder, der forenkler det lidt (Vi
skal senere se, hvordan det kan gøres endnu bedre, ved at bruge
delegering). Vi vil indføre to sådanne service-metoder. |
|
| Den første knytter sig til instantieringen af GridBagConstraints-objektet.
Det er forsekelligt hvilke af attributterne man ønsker at anvende,
men netop de fire vi bruger her, ønsker man altid at sætter.
Derfor vil vi lave en fabriksmetode,
der returnerer en instans af GridBagConstraints,
hvor de fire attributter sætter til værdien af fire parametre
til metoden. Vi kalder metoden createGBC: |
|
|
private GridBagConstraints createGBC( int x, int y, int width, int height ) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = width;
gbc.gridheight = height;
return gbc;
}
|
|
|
| Når vi får objektet tilbage fra metoden, kan vi vælge
at sætte andre af attributterne, alt efter den konkrete situation. |
|
| I forbindelse med den afsluttende indstilling vil vi også anvende
en service-metode add: |
|
|
private void add( JComponent component, GridBagConstraints gbc ) {
layout.setConstraints( component, gbc );
getContentPane().add( component );
}
|
|
|
| Denne metode kræver at layout
er en instansvariabel. Man kunne alternativt lade den være en parameter
til metoden, men det synes unødvendigt da man normalt alligevel
lader layout være
en instansvariabel med henblik på eventuelle senere justeringer. |
|
| Ved anvendelse af disse to servicemetoder bliver eksemplet med JTextField
i stedet: |
|
|
GridBagLayout layout = new GridBagLayout();
getContentPane().setLayout( layout );
...
GridBagConstraints con;
con = createGBC( 0, 0, 2, 1 );
add( new JTextField( "JTextField" ), con );
|
|
|
| Det har givet en kraftig reduktion i antallet af linier. Da vi i dette
eksempel kun vil fokusere på layoutet og ikke ser på event-håndtering,
har vi valgt at flytte instantieringen af JTextField
ind i kaldet af add. |
|
| Når vi senere føjer flere attributter til, vil det igen
begynde at vokse i omfang, men vi har taget toppen af. |
|
| Den samlede konstruktor med samtlige komponenter bliver: |
|
|
public GridBagFrame( String title ) {
super( title );
layout = new GridBagLayout();
getContentPane().setLayout( layout );
GridBagConstraints con;
con = createGBC( 0, 0, 2, 1 );
add( new JTextField( "JTextField" ), con );
con = createGBC( 0, 1, 1, 3 );
add( new JTextArea( "JTextArea" ), con );
con = createGBC( 1, 1, 1, 1 );
add( new JButton( "JButton" ), con );
con = createGBC( 1, 2, 1, 1 );
add( new JButton( "JButton" ), con );
con = createGBC( 1, 3, 1, 1 );
add( new JButton( "JButton" ), con );
addWindowListener( new WindowHandler() );
pack();
setVisible( true );
}
|
|
|
| Vinduet får følgende udseende: |
Figur x:
Med placering og udstrækning
|
|
|
| Måske lidt skuffende. Det ligger stadig langt fra det ønskede
design, og vi skal have sat en del andre attributer før vi når
målet. |
|
| JTextField og JTextArea
antager minimale størrelser. Det første man kunne ønske
sig, var at få dem til at brede sig ud på den plads de har
til rådighed. |
|
| Komponenters tilbøjeligheden til at resize så de fylder
deres plads ud, sættes med attributten fill. |
|
| Man kan sætte denne attribut til en af følgende fire konstanter: |
|
|
GridBagConstraints.NONE (default)
GridBagConstraints.HORIZONTAL
GridBagConstraints.VERTICAL
GridBagConstraints.BOTH
|
|
|
| Default værdien NONE,
betyder at komponentet ikke vil brede sig. Det er resultatet af
denne default-værdi vi ser ovenfor. HORIZONTAL
betyder at den vil brede sig i bredden, mens VERTICAL
betyder at den breder sig i højden. BOTH
betyder at den vil brede sig i begge retninger. |
|
| Vi kan nu tage stilling til hvordan vi ønsker komponenterne skal
brede sig i deres områder. |
|
| JTextField skal brede
sig i bredden, og selvom den nok aldrig vil blive højere (den vil
altid kun være en tekstlinie), vælger vi alligevel at være
præcise, og bruger derfor HORIZONTAL. |
|
| Alle andre komponenter ønsker vi skal brede sig i alle retninger,
og vi vælger derfor BOTH
til dem. |
|
| Konstruktoren får nu følgende indhold: |
|
|
public GridBagFrame( String title ) {
super( title );
layout = new GridBagLayout();
getContentPane().setLayout( layout );
GridBagConstraints con;
con = createGBC( 0, 0, 2, 1 );
con.fill = GridBagConstraints.HORIZONTAL;
add( new JTextField( "JTextField" ), con );
con = createGBC( 0, 1, 1, 3 );
con.fill = GridBagConstraints.BOTH;
add( new JTextArea( "JTextArea" ), con );
con = createGBC( 1, 1, 1, 1 );
con.fill = GridBagConstraints.BOTH;
add( new JButton( "JButton" ), con );
con = createGBC( 1, 2, 1, 1 );
con.fill = GridBagConstraints.BOTH;
add( new JButton( "JButton" ), con );
con = createGBC( 1, 3, 1, 1 );
con.fill = GridBagConstraints.BOTH;
add( new JButton( "JButton" ), con );
addWindowListener( new WindowHandler() );
pack();
setVisible( true );
}
|
|
|
| Og vinduet får følgende udseende: |
Figur x:
Med fill
|
|
|
| Det hjælper lidt, men det hele er for småt. Specielt er
JTextArea helt forkert. |
|
| Problemet med JTextArea
kan vi i første række løse ved at give det en størrelse
med: |
|
|
add( new JTextArea( "JTextArea", 8, 16 ), con );
|
|
|
| Dermed få vinduet følgende udseende: |
Figur x:
Med størrelse på JTextArea
|
|
|
| Det begynder at ligne, men nu er der opstået et problem med den
nederste af de tre knapper. |
|
| Pga. BOTH breder den
sig på den ledige plads, og af en eller anden grund (se senere)
er det netop den nederste knap, der får al den ledige plads. |
|
| Ifølge vores design skal de tre kanpper deles ligeligt om pladsen.
Vi skal have indstillet hvor meget komponenterne fylder forholdsmæssigt
i forhold til hinanden. Til dette formål skal vi bruge attributterne
weightx og weighty. |
|
| Defaultværdien for disse attributter er 0. 0 betyder
at komponentet i den givne retning x:
vandret, y: lodret, får
den størrelse, der er dens minimale udstrækning. Hvis alle
komponenter i en retning lodret/vandret har attributten sat til 0,
vil det sidste komponent få den resterende plads. Det er derfor
den nederste knap få al den ledige plads. |
|
| Hvad vis værdien ikke er 0? Layout-manageren vil tage alle
attributter i en retning og summere dem. Dernæst vil den bruge deres
andel af denne sum som forholdtal, når den lader komponenterne brede
sig. Vi vil derfor sætte weighty
for de tre knapper til den samme værdi, og lade weighty
for JTextField forblive
0, da den ikke skal brede sig lodret (hvilket den alligevel ikke
kan!). |
|
| Hvad skal vi konkret sætte weighty
til? de to attributter weightx
og weighty er double's,
og deres konkrete værdier er uden betydning, det er forholdet mellem
dem der er afgørende. Vi vil vælge at sætte de tre
knappers weighty til 1. |
|
| Konstruktoren får nu følgende indhold: |
|
|
public GridBagFrame( String title ) {
super( title );
layout = new GridBagLayout();
getContentPane().setLayout( layout );
GridBagConstraints con;
con = createGBC( 0, 0, 2, 1 );
con.fill = GridBagConstraints.HORIZONTAL;
add( new JTextField( "JTextField" ), con );
con = createGBC( 0, 1, 1, 3 );
con.fill = GridBagConstraints.BOTH;
add( new JTextArea( "JTextArea", 8, 16 ), con );
con = createGBC( 1, 1, 1, 1 );
con.fill = GridBagConstraints.BOTH;
con.weighty = 1;
add( new JButton( "JButton" ), con );
con = createGBC( 1, 2, 1, 1 );
con.fill = GridBagConstraints.BOTH;
con.weighty = 1;
add( new JButton( "JButton" ), con );
con = createGBC( 1, 3, 1, 1 );
con.fill = GridBagConstraints.BOTH;
con.weighty = 1;
add( new JButton( "JButton" ), con );
addWindowListener( new WindowHandler() );
pack();
setVisible( true );
}
|
|
|
| Og vinduet får følgende udseende: |
Figur x:
Med weighty
sat på de tre knapper
|
|
|
| Hvis man trækker lidt i vinduet, for at gøre det større,
vil man opdage følgende: |
Figur x:
Vinduet efter det er gjort større af brugeren
|
|
|
| Lodret ser det rimelig fornuftigt ud. De tre knapper har bredt sig på
den ledige plads, og fordelt den ligeligt mellem dem. Derimod er der problemer
vandret. Vi skal have sat weightx,
så komponenterne kan fordele den ledige plads mellem dem. Ifølge
designet skal størrelsesforholdet mellem knapperne og JTextArea
være 7:2. Vi vælger derfor disse værdier til
weightx. JTextAreas
sættes til 7 og de tre knappers sættes til 2. |
|
| Det giver følgende udseende, hvis man resizer vinduet til samme
størrelse som ovenfor: |
Figur x:
Med anvendelse af weightx
|
|
|
| Vores vindue er nu mere robust, idet det kan tåle, at man resizer. |
| Luft
| En sidste ting vi vil berøre er "luft". I forhold til
vores ønskede design står komponenterne skulder ved skulder
- der er ikke nogen luft imellem dem. |
|
| Luft laves ved at man sætter attributten insets
til en instans af klassen: Insets.
|
|
| Insets har selv fire
attributter der angiver hvor meget luft (i pixels) der skal være
i hver af de fire retninger. Insets
konstruktor har følgende signatur: |
|
|
Insets( int top, int left, int botton, int right )
|
|
|
| For at få en ensartet fordeling af luften imellem komponenterne
anvender vi den samme Insets
til alle fem komponenter, med 5 pixels i hver retning. |
|
|
public GridBagFrame( String title ) {
super( title );
layout = new GridBagLayout();
getContentPane().setLayout( layout );
GridBagConstraints con;
Insets ins = new Insets( 5, 5, 5, 5 );
con = createGBC( 0, 0, 2, 1 );
con.fill = GridBagConstraints.HORIZONTAL;
con.insets = ins;
add( new JTextField( "JTextField" ), con );
con = createGBC( 0, 1, 1, 3 );
con.fill = GridBagConstraints.BOTH;
con.insets = ins;
con.weightx = 7;
add( new JTextArea( "JTextArea", 8, 16 ), con );
con = createGBC( 1, 1, 1, 1 );
con.fill = GridBagConstraints.BOTH;
con.insets = ins;
con.weightx = 2;
con.weighty = 1;
add( new JButton( "JButton" ), con );
con = createGBC( 1, 2, 1, 1 );
con.fill = GridBagConstraints.BOTH;
con.insets = ins;
con.weightx = 2;
con.weighty = 1;
add( new JButton( "JButton" ), con );
con = createGBC( 1, 3, 1, 1 );
con.fill = GridBagConstraints.BOTH;
con.insets = ins;
con.weightx = 2;
con.weighty = 1;
add( new JButton( "JButton" ), con );
addWindowListener( new WindowHandler() );
pack();
setVisible( true );
}
|
|
|
| Det endelige vindue bliver: |
Figur x:
Med anvendelse af insets
|
|
|
|
7.2 GridBagConstraints
attributter
|
|
| Vi vil her gennemgå de enkelte attributter og deres betydning. |
|
|
7.2.1 gridx
og gridy
|
|
| Angiver placeringen af komponentet. Angivelsen er af øverste
venstre hjørne af komponentet, og disse værdier starter fra
( 0, 0 ). Værdierne er ikke ækvidistante, men er relative
placeringer. |
|
| Man kan evt. tænke på dem som index-lignende positioner
som de kendes fra arrays. |
|
|
7.2.2. gridwidth
og gridheight
|
|
| Angiver hvor mange kolonner henholdsvis rækker komponentet har
til sin rådighed. Komponenten vil ikke nødvendigvis brede
sig over det angivne område, da det er op til andre attributter
at styre dette. |
|
| Området er beskyttet mod at andre komponenter trænger i
ind på. Hvis komponentet ikke breder sig over hele området,
henstår det resterende tomt. |
|
| Angivelsen af bredde/højde er ikke ækvidistant, da forskellige
kolonner/rækker kan have forskellig bredde/højde. |
|
|
7.2.3 weightx
og weighty
|
|
| Disse værdier er doubles,
der fungerer som forholdstal. De beskriver den vægt hvormed de skal
tildeles overskydende plads i konkurrence med andre komponenter i den
pågældende retning. |
|
| weightx er vandt, og
weighty er lodret. |
|
| Default er 0, og betyder at komponentet ikke breder sig, men antager
sin minimale størrelse i den pågældende retning. |
|
| Når layout-manageren skal tildele overskydende plads beregner
den summen af vægtene for de indgående komponenter, i den
pågældende retning, og fordeler pladsen efter den del som
deres vægt udgør af denne sum. |
|
|
7.2.4 ipadx
og ipady
|
|
| Dette er de interne paddings (i modsætning til de externe, der
beskrives nedenfor). De interne paddings angiver hvor meget komponentet
vil lægge til sin minimale størrelse i den pågældende
retning. Komponentet vil altid antage sin minimale størrelse plus
disse paddings. |
|
| Effekten er derfor, at man udvider komponentets minimale størrelse
i samspillet med de andre komponenter, så det altid er sikret denne
ekstra plads inde i det område, hvor det befinder sig. |
|
| Default er ingen intern padding. |
|
|
7.2.5 fill
|
|
| Angiver om et komponent vil brede sig for at udfylde ledig plads i sit
område. Attributten tildeles en af følgende fire konstanter. |
|
|
GridBagConstraints.NONE (default)
GridBagConstraints.HORIZONTAL
GridBagConstraints.VERTICAL
GridBagConstraints.BOTH
|
|
|
| Default værdien NONE,
betyder at komponentet ikke vil brede sig. Det er resultatet af denne
default-værdi vi ser ovenfor. HORIZONTAL
betyder at den vil brede sig i bredden, mens VERTICAL
betyder at den breder sig i højden. BOTH
betyder at den vil brede sig i begge retninger. |
|
|
7.2.6 insets
|
|
| insets angiver extern
padding. Denne attribut sættes til en instans af Insets,
som har følgende konstruktor: |
|
|
Insets( int top, int left, int botton, int right )
|
|
|
| De fire værdier angiver i pixels hvor meget extra plads (luft)
der skal være mellem dette komponent (uanset dets udstrækning)
og de tilstødende komponenter. To komponenter der støder
op til hinanden, deles ikke om extern padding, og deres externe padding
vil derfor blive placeret side om side. |
|
| Default er ingen extern padding. |
|
|
7.2.7 anchor
|
|
| Angiver den relative placering af et komponent i dets område,
når den ikke udfylder det. Attributten sættes til en af følgende
konstanter: |
|
|
GridBagConstraints.CENTER
GridBagConstraints.NORTH
GridBagConstraints.NORTHEAST
GridBagConstraints.EAST
GridBagConstraints.SOUTHEAST
GridBagConstraints.SOUTH
GridBagConstraints.SOUTHWEST
GridBagConstraints.WEST
GridBagConstraints.NORTHWEST
|
|
|
| Default er CENTER som
placerer komponentet midt i dets område. De andre angivelser placerer
komponentet ud til kanten af området i den pågældende
retning. |
|
|
7.3 GridBagGUI
|
|
| Vi så ovenfor hvordan man kunne lette anvendelse af GridBagLayout
med to service-metoder. Når man skal anvende GridBagLayout
i større sammenhæng er det dog nyttigt at vælge en
mere skalerbar løsning, der til gengæld kræver en større
forståelse af objektorienteret design. Vi vil i dette afsnit studere
et sådant design som jeg selv plejer at bruge i forbindelse med
GridBagLayout. |
|
| Det grundlæggende skaleringsproblem ligger i, at der skal være
flere/mange af de to service-metoder. Hvis man opbygger en mere sammensat
GUI, med f.eks. flere paneler til at styrre de grafiske komponenter, er
man nød til at lave en subklasse for hvert panel og indføre
de to service-metoder i hver af dem. Det skyldes at metoderne anvender
instansvariable i objektet og derfor er uløselig bundet til klassens
layout. |
|
| Den designmæssige løsning ligger netop i at løsne
metoderne fra selve klassen og placere dem i et andet objekt som får
ansvaret for at håndtere layoutet. Lad os for eksemplet skyld antage
at vi ønsker at anvende et GridBagLayout
i forbindelse med en JFrame. |
|
|
|
|
| I første række ser vi derfor designet som en delegering
af layout-opgaven til et andet objekt - en instans af en klasse vi selv
laver: GridBagGUI.
|
|
| Set fra JFrame
ønsker vi at kunne sende grafiske komponenter til GridBagGUI
og få dén til at placere dem i et GridBagLayout
med de indstillinger af GridBagConstraints
som vi også meddeler GridBagGUI.
Det betyder at GridBagGUI
skal have en række metoder til indstilling af layoutet samt en add-metode
til de grafiske komponenter. |
|
| Hvad skal GridBagGUI
have adgang til for kunne håndtere layout i JFrame'n? |
|
| Den skal blot have adgang til den container som de grafiske elementer
føjes til. For en JFrame's
vedkommende er det, det objekt, man får en reference til ved at
kalde getContentPane,
mens det for de fleste andre komponenter er komponentet selv - f.eks.
er JPanel sin egen
container. |
|
| De komponenter, der kan indeholde andre grafiske komponenter - dvs.
grafiske containere - har alle en fælles superklasse, som passende
hedder Container.
Det er derfor GridBagGUI's
konstruktor tager en Container
som parameter: |
|
|
public GridBagGUI( Container container ) {
this.container = container;
layout = new GridBagLayout();
container.setLayout( layout );
reset();
}
|
|
|
| Konstruktoren gemmer naturligvis denne reference til senere add-kald,
men mere interessant er det, at den selv laver en instans af GridBagLayout
og sætter den som layout på containeren. GridBagGUI
har nu de to objekter den skal bruge: Containeren og layoutet. (reset-metoden
vender vi tilbage til senere). |
|
| Hvis der er tale om en JFrame
vil vi instantiere den tilhørende GridBagGUI
med kaldet: |
| GirdBagGUI
i JFrame
|
... = new GridBagGUI( getContentPane() );
|
|
|
| Mens man i et JPanel
vil bruge: |
| GirdBagGUI
i JPanel
|
... = new GridBagGUI( this );
|
|
|
| |
|
| Den vigtigste metode i GridBagGUI
er add-metoden: |
|
|
public void add( Component component ) {
layout.setConstraints( component, gbc );
container.add( component );
}
|
|
|
| Her anvendes begge de associerede objekter: layout
og container, og
man genkender kaldene fra service-metoden af samme navn, som vi har anvendt
tidligere. |
|
| |
|
| Ser man på kildeteksten til GridBagGUI
(hvilket vi ikke vil gøre her) er den præget af de mange
metoder til indstilling af diverse
GridBagConstraints. |
|
| Da jeg som nævnt selv anvender klassen i praksis, er der visse
af metoderne, som ikke er helt pædagogisk korrekte, men jeg har
alligevel taget dem med i det følgende: |
|
| |
|
| Til indstilling af position og relativ størrelse er der følgende
seks metoder: |
|
|
void setPosition( int x, int y )
void setPositionX( int x )
void setPositionY( int y )
void setSize( int width, int height )
void setWidth( int width )
void setHeight( int height )
|
|
|
| |
|
| Til indstilling af fill
er der hele tre metoder: |
|
|
void setFill( int con ) void setFill( char con ) void setFill( String con )
|
|
|
| Den første tager som ventet en af de sædvanlige GridBagConstraints-konstanter,
mens de to andre er bekvemmelige. Man kan i stedet anføre en tekststreng:
"HORIZONTAL", "VERTICAL", "BOTH" eller "NONE",
eller forkorte det til et tegn: 'H', 'V', 'B' eller 'N'. |
|
| Som nævnt er det bekvemt. Man skal til gengæld ikke forvente
at ens kode bliver nemmere at læse, specielt ikke for andre, og
i særdeleshed ikke hvis man anvender enkelt bogstaver. |
|
| |
|
| Dernæst er der tre metoder til indstilling af weightx
og weighty: |
|
|
void setWeight( int weightx, int weighty )
void setWeightX( int weight )
void setWeightY( int weight )
|
|
|
| og to metoder til insets: |
|
|
void setInsets( Insets insets )
void setInsets( int top, int left, int botton, int right )
|
|
|
| og tre metoder til intern padding: |
|
|
void setIPad( int padx, int pady )
void setIPadX( int pad )
void setIPadY( int pad )
|
|
|
| |
|
| Til slut er der to metoder til anchor; hvoraf den sidste får forkortelserne
i setFill-metoderne
til at blegne: |
|
|
void setAnchor( int dir ) void setAnchor( String dir )
|
|
|
| Her kan man nemlig anvende en af følgende forkortelser for de
mulige GridBagConstraints:
"C", "N", "NE", "E", "SE",
"S", "SW", "W" eller "NW". Bemærk
at "C" står for CENTER. |
|
| |
|
| Endelig er der en reset-metode, der sætter alle GridBagConstraints
til deres default-værdier: |
|
|
|
|
| |
|
| Vi vil ikke gennemgå et samlet eksempel på en anvendelse
af GridBagGUI, men
blot se et enkelt eksempel, nemlig følgende panel: |
|
|
|
|
| Der er lavet med følgende kode: |
|
|
GridBagGUI gui = new GridBagGUI( this );
gui.setPosition( 0, 0 );
gui.setSize( 1, 1 );
gui.setFill( 'H' );
gui.setWeightX( 1 );
gui.add( input );
gui.setPosition( 1, 0 );
gui.setWeightX( 0 );
gui.add( searchButton );
gui.setPosition( 2, 0 );
gui.add( stopButton );
|
|
|
| Som man ser drager jeg nytte af at genbruge de tidligere indstillinger
af GridBagConstraints
efterhånden som de enkelte grafiske komponenter føjes til,
ved ikke at kalde reset,
men i stedet ændre dem der skal være anderledes. Det gør
koden kortere, men nedsætter til gengæld læsbarheden. |
|
| |
|
|
Kildetekst:
GridBagGUI.zip
|
|
|
8. JPanel
|
|
| JPanel er naturligvis
ikke nogen layout-manager, men den spiller en væsentlig rolle i
forbindelse med opbygningen af grafiske brugergrænseflader med layout-managere.
|
|
| Et JPanel er en ren
container. Den kan indeholde grafiske komponenter og den har sig egen
layout-manager, men den har ikke nogen visuel repræsentation
i sig selv. |
|
| JPanels kan indeholde
JPanels og på den
måde optræder den i et Composite
Pattern, på samme måde som f.eks. JMenu. |
|
| <Det skal der skrives en hel del mere om> |
|
| |
|
| <Se på javax.swing.OverlayLayout
(<=1.3) og javax.swing.SpringLayout
(1.4)> |
|
|
|
| fodnoter:
|
|
| 1
| Der findes i alt fem forskellige add-metoder
i klassen Container.
|
|
| |