Tabeller Opgaver |
1. Introduktion |
||
Inden vi mere systematisk gennemgår mulighederne for at lave tabeller i Swing, vil vi først se nogle simple eksempler, der viser hvad der er idéen med tabeller. | ||
1.1 Eksempel: Den lille tabel | ||
|
Først vil vi se et eksempel der er taget fra de første skoleår, hvor den lille tabel stod på programmet. |
|
Figur 1: |
| |
|
Kildeteksten til eksemplet er beskeden i omfang: | |
| ||
| ||
Betegnelsen AbstractTableModel leder tanken hen på MVC Pattern, og Swing bruger da også Document-View varianten til at implementere tabeller. Swings JTable er View-klassen, mens AbstractTableModel er Document-klassen (som vi dog vil kalde modellen i det følgende). | ||
Vi har i LilleTabelModel implementeret tre metoder som view (JTable) baserer sig på. | ||
Først er der: | ||
| ||
der giver tabellens dimensioner i antal rækker (eng.: rows) og kolonner (eng. columns). | ||
Dernæst er der: | ||
| ||
der giver tabellens indhold for den givne række og kolonne. | ||
Man skal implementere disse tre metoder, da de er abstrakte i AbstractTableModel. | ||
Ud fra de oplysninger JTable kan hente fra disse metoder opbygger den sit view. | ||
1.2 Eksempel: Porto | ||
Selvom Swing bruger Document-View varianten af MVC Pattern er der mulighed for at bruge én form for controllere, nemlig scrollbars - ja, det nærmest forventes at man gør det! | ||
Lad os se et eksempel på en tabel med porto for almindelige brevforsendelser. | ||
Figur 2: |
| |
Det første man bemærker er naturligvis scrollbaren, men ellers er det kolonnenavne A til G som vækker opmærksomhed. | ||
Lad os se kildeteksten til eksemplet: | ||
| ||
| ||
Man spejder forgæves efter hvad det er, der får kolonne-navnene til at dukke op. Om kolonne-navnene er der, eller ej, er en af særhederne ved Swing. Er der en scrollbar, så er der også kolonnenavne - ellers ikke! Dvs. det er en ydre omstændighed, der afgør om kolonne-navnene vises, ikke noget der afgøres i viewet (JTable). | ||
Personligt finder jeg det ikke helt tilfredsstillende, at man ikke kan få kolonnenavne med mindre man har en scrollbar, men man kan lige så godt vende sig til det først som sidst - vil man have "ordentlige" tabeller i Swing så skal de altid i en JScrollPane. | ||
1.2.1 Kolonnenavne | ||
Kolonnenavnene A til G er default-navne - navne der bruges med mindre vi selv indfører nogen. Default-navnene er inspireret af de kolonne-navne, der er default i regneark. | ||
Ønsker vi at indføre vores egne kolonnenavne skal vi override følgende metode: | ||
| ||
som vores model arver fra AbstractTableModel. | ||
Hvis vi f.eks. gerne vil have følgende kolonnenavne i vores eksempel med porto: | ||
Figur 3: |
| |
Gøres det med følgende ændringer i PortoTabelModel: | ||
| ||
| ||
Det er en TableCellRenderer, som bestemmer hvordan hver enkelt celle skal se ud. Vi skal senere se, at en TableCellRenderer er en fabrik der producerer det, der bliver vist i en celle, men i første omgang vil vi se en TableCellRenderer som et JComponent. | ||
Når man laver sin egen TableCellRenderer nedarver man normalt fra DefaultTableCellRenderer, der realiserer TableCellRenderer interfacet og er en subklasse til JLabel: | ||
Figur 4: |
| |
Vi har her kun vist de mest relevante metoder fra de implicerede klasser. JLabel indeholder som bekendt betydelig flere metoder, som kan bruges til at indstille udseendet, her er blot vist de tre mest anvendte. | ||
Når man laver en TableCellRenderer består opgaven først i at override setValue i vores subklasse til DefaultTableCellRenderer, og dernæst i at få fat i den relevante kolonne fra JTable og give TableCellRenderer'en til denne kolonne med setCellRenderer. | ||
Lad os se et eksempel: | ||
2.1 Eksempel: class KommatalsRenderer | ||
Vi vil lave en TableCellRenderer, som løser en ofte forekommende problem: At skulle vise beløb, eller andre kommatal, med et vist antal decimaler efter kommaet. | ||
Først er der modellen, der er upåvirket af dette problem: | ||
| ||
Vi har her valgt en model, som består af et to-dimensionalt array af doubles, der repræsenterer tilfældige reelle tal i intervallet ]-100:100[.
| ||
Dernæst kommer det mest interessante - selve vores TableCellRenderer: | ||
| ||
Vi bruger konstruktoren til at indstille antallet af decimaler vi ønsker efter kommaet, og sætter alignment til højre (Bemærk, at vi ikke behøver at skrive JLabel.RIGHT, da vores klasse har JLabel som en af sine super-klasser). | ||
Metoden setValue indeholder kernen i dette eksempel. Efter at have konstateret, at der er tale om en double, gør vi to ting. | ||
Først sætter vi forgrundsfarven, så den bliver rød ved negative tal, ellers blå. | ||
Dernæst bearbejder vi en tekststreng, så den indeholder en tekstuel repræsentation af værdien med det rigtige antal decimaler efter kommaet (vi vil ikke her gennemgå hvordan, da det falder udenfor emnet). Vi bruger JLabel's setText til at vise det resultat vi når frem til.
| ||
Det er i framen, vi sætter vores KommatalsRenderer til de enkelte kolonner i tabellen: | ||
| ||
Bemærk, at vi her anvender de kolonne-navnene som vi selv har indført med metoden getColumnName i KommaTabelModel, når vi kalder getColumn på vores JTable.
| ||
Resultatet bliver: | ||
Figur 5: |
| |
2.2 getTableCellRendererComponent | ||
Som vi gjorde ovenfor, plejer man at lave en TableCellRenderer ved at nedarve fra DefaultTableCellRenderer, men man kan naturligvis selv realisere TableCellRenderer interfacet - det har trods alt kun én metode. | ||
| ||
Hvis man gør det, vil man dog opdage hvor rodet DefaultTableCellRenderer's implementation er på dette punkt. getTableCellRendererComponent-metoden er en fabriks-metode! | ||
Realiseringen i DefaultTableCellRenderer er ikke nogen fabriks-metode, da den slutter med linien: | ||
| ||
før denne linie er der en lang række linier der indstiller JLabel'et og bla. et polymorft kald af setValue - metoden som vi har overrided ovenfor. | ||
Designet bliver dermed et rodsammen af en fabriks-metode og noget prototype-lignende, der alligevel ikke rigtig er det. | ||
Fordelen ved at lave sin egen implementation af TableCellRenderer er, at man kan anvende andre grafiske komponenter til at vise indholdet af tabellens celler, i stedet for at være begrænset til at bruge JLabel's | ||
3. TableCellEditor | ||
Når man vil lave en TableCellEditor, til at ændre værdier i en tabels celler, skal man ikke til at lave nye klasser (med mindre man vil lave meget specielle editorer). | ||
Modellen skal indrettes efter, at der er en TableCellEditor til visse af kolonnerne, og framen skal sætte en TableCellEditor til de kolonner de vedrører. | ||
En TableCellEditor får man ved at lave en instans af DefaultCellEditor. Konstruktoren tager som parameter enten et JTextField, en JCheckBox eller en JComboBox. | ||
Lad os se et eksempel: | ||
3.1 Eksempel: Reservation af Auditoriet | ||
Vi vil lave en tabel der har form af et skema. Det skal være et skema der dækker alle hverdage for et bestemt lokale: Auditoriet. | ||
Skemaet får følgende udseende: | ||
Figur 6: |
| |
Vi ønsker at kunne ændre status for de enkelte lektioner ved at vælge mellem forskellige muligheder fra en combobox, når man klikker på den pågældende lektion. | ||
Vi opnår dette ved at knytte en TableCellEditor til de fem af kolonnerne og giver instansen af DefaultCellEditor en JComboBox der har valgmulighederne.
| ||
Modellen bliver som følger: | ||
| ||
Her er to arrays gjort til public klasse-konstanter, da de bruges i forbindelse med opsætningen af editorer i framen | ||
Man skal specielt bemærke metoderne: isCellEditable og setValueAt. | ||
isCellEditable bruges til at angive hvilke af tabellens celler det er tilladt at rette. Vi vil kun tillade at der rettes i de fem hverdage - ikke i tidspunkter for lektioner. | ||
setValueAt kaldes af DefaultCellEditor'en når den ønsker at ændre en værdi i tabellen. Ikke alene kan denne metode ændre i tabellen, men man kan også foretage en evt. kontrol af de værdier, der indgår i ændringen. Vores ændringer kommer fra en combobox der kun kender tilladte værdier, så vi foretager ingen kontrol.
| ||
Dernæst følger framen, der tildeler de fem kolonner deres editorer: | ||
| ||
Vi henter kolonnernes navne fra arrayet med dagenes navne og giver comboboxen arrayet med de mulige tilstande.
| ||
Resultatet bliver: | ||
Figur 7: |
| |
4. "Det grå felt" | ||
I eksemplerne ovenfor har vi ofte anvendt at resize framen så den har passet til tabellens størrelse. I eksemplet med "Reservation af Auditoriet" er dette særlig tydeligt, idet der anvendes en højde, på framen, på 143 pixels. Havde vi i stedet brugt et kald af pack-metoden, ville vi have fået følgende: | ||
Figur 8: |
| |
Man får en gråt område, som man i de fleste tilfælde gerne ville være foruden. Det grå felt opstår fordi det område som tabellen gerne vil have når den indgår i sammenhænge, hvor den kan scroll'es er sat til en fast størrelse og ikke til tabellens egen størrelse - hvilket ofte ville være at foretrække. | ||
Problemet kan løses ved at indsætte følgende linie efter instantieringen af tableView (en JTable) i SkemaTabelFrame's konstruktor: | ||
| ||
Med metoden: setPreferredScrollableViewportSize, kan vi selv sætte den ønskede størrelse, og vi vælger har at tage denne fra tabellens egen foretrukne størrelse. Der er her tale om to forskellige foretrukne størrelser: I almindelighed, og i scroll-sammenhæng. Vi vælger at sætte disse til det samme! | ||
Laver man en subklasse til JTable kan man i stedet vælge at override den nedarvede getPreferredScrollableViewportSize-metode: | ||
| ||
Såfremt man nedarver fra JTable, er det smar og behag, hvad man vælger at gøre. |