Java Programmering - 8. del

Tags:    java
Skrevet af Bruger #4487 @ 07.02.2011

Indledning


Vi har lært lidt om hvordan man laver et simpelt GUI, og lægger grafik ind i det, men hvordan laver man applikationer, som ligner almindelige programmer med tekst felter, knapper, menuer, tekst og så videre? Det er faktisk ikke så svært som man skulle tro, og vi er allerede en god del af vejen, fordi vi allerede har set på hvordan man laver et simpelt vindue (Frame).

Vores første rigtige GUI.


Der findes en pakke, som hedder swing (ligger på stien javax.swing), og swing pakken indeholder mange klasser som man benytter når man laver GUI's. Et af disse klasser er som bekendt klassen JFrame. Denne klasse lavede som bekendt vores vindue (frame), som vi kunne lægge komponenter ind i. Vi kender også lidt til JPanel, som var en slags container hvor vi kunne organiserer vores komponenter. I dette eksempel vil vi ikke benytte et Panel, men kun et Frame. Der findes også mange andre klasser i denne swing pakke, og nogle af disse klasser hedder f.eks. JTextField, JButton og JLabel. Disse klasser er alle klasser, som laver nogle komponenter for os, som dem vi kender fra almindelige programmer. Men lad os komme igang med det sjove, nemlig programmeringen.

Vi skal lave 2 klasser, den første er vores Main klasse, og den skal bare indeholde en Main metode. Main metoden skal lave et objekt af den anden klasse som vi skal lave (Vi vil i eksemplet her kalde klassen for MinApplikation) og kosntruktøren som kaldes kræver en parameter, som er af datatypen String. Parameteren skal bruges til at angive titlen i vores JFrame. Vores Main klasse ser nu således ud:
Fold kodeboks ind/udJava kode 

Vi kan nu begynde på vores klasse 'MinApplikation', som skal lave et Frame til at starte med. Vi kan lave dette frame i en metode, som så kaldes i konstruktøren. Vores klasse MinApplikation ser nu således ud:
Fold kodeboks ind/udJava kode 

Vi vil gerne lave en applikation, hvor brugeren kan indtaste sit navn i et tekst felt, og herefter få det vist i et andet tekst felt. alt sammen skal ske når brugeren indtaster navnet i et tekst felt, og så trykker på knappen. Vi vil gerne have en lille tekst før vores tekst felt som indeholder teksten "Indtast navn:", og vi skal derfor bruge komponenterne: 1x Label, 2x Text Fields og 1x Button. Men før vi overhovedet begynder at lave objekt variablerne til vores komponenter, og indsætte objekter i disse felter af de forskellige komponenter, skal vi først sætte vores vindues layout. Hvis ikke du sætter et layout manuelt, vil Java automatisk sætte layoutet til noget som hedder 'FlowLayout', men da vi gere vil have fuld kontrol over vores positionering af komponenterne skal vi ikke benytte noget forudprogrammeret layout. Vi skal først i vores lavVindue() metode kalde metoden setLayout(LayoutManager manager) fra vores vindue (frame) via dot notation. Herefter skriver vi nøgleordet null i parameterfeltet. Ordet null betyder intet objekt/ingenting, og det er lige præcis hvad vi ønsker i dette eksempel. Vi vil ikke bruge noget forud programmeret layout til dette eksempel, da vi ønsker at få fuld råderet over, hvor vores komponenter skal være. Når vi har valgt vores Layout til vinduet, skal vi lave nogle objekt variabler for hvert af vores 4 komponenter (label, 2x tekst felter og en knap.) og det skal vi for nemt at kunne få fat i komponenterne hvis vi får brug for det. Vores klasse ser nu således ud:
Fold kodeboks ind/udJava kode 

Nu kan vi så begynde at indsætte vores komponenter i vores vindue/frame. Lad os starte med at lave en ny privat mutator metode, som vi kunne kalde for lavKomponenter(). Her skal vi først initialisere hvert af vores objekt variabler (label, tekst felterne og knappen), med et objekt der matcher. Bemærk at vi skal give JLabel's konstruktør og JButton's konstruktør en parameter, nemlig en string, for hvad der skal stå i vores label, og hvad der skal stå på vores knap. Vi kan herefter kalde add metoden for vores vindue/frame, og tilføje vores komponenter en efter en. Vi kan så kalde metoden i vores kosntruktør, før setVisible() metoden. Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Nu da vi har klargjort vores objekter, skal vi positionere dem allesammen. Dette gøres ved at kalde metoden setBounds(int x, int y, int width, int height) for hver af vores komponenter. Du kan enten selv prøve dig frem med nogle passende x og y værdier samt bredde og højde, men hvis du er doven, kan du også benytte disse - label.setBounds(10, 10, 90, 20); - txtFieldNavn.setBounds(120, 10, 80, 20); - txtFieldVis.setBounds(220, 10, 80, 20); - knap.setBounds(220, 40, 80, 20); - Vi kan herefter for vores txtFieldVis objekt kalde metoden setEditable(boolean b). Parameteren sætter du til false, fordi så bliver tekst feltet nemlig ikke redigerbart, altså man kan ikke skrive i dette felt. Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Du kan nu lave et objekt af din GUI klasse i din main klasse, og herefter vil du få et resultat, som følgende nedenunder.



Lav Handling på dine komponenter


Hvis du ikke skulle have bemærket det, så har vi kun lavet den grafiske del. Der sker nemlig ikke noget, når vi prøver at trykke på vores knap. Når der skal ske noget i Java, kalder man det en action. På dansk kunne vi kalde det en handling, for hovedteorien i ordet action, er nemlig handling. Vi kan altså tilføje en handling til vores knap, altså at der skal ske noget, når vi trykker på denne knap. I Java kan man benytte noget, som hedder en ActionListener. Dette er en klasse, eller rettere sagt et interface i Java, som vi kan benytte og bruge, for at tilføje handling til et komponent/objekt. Når man har med interfaces at gøre i Java, skal man fortælle i klassedefinitionen, at man vil benytte et bestemt interface i vores klasse. Dette gøres ved efter klassen navn at skrive implements og så interfacets navn (i vores eksempel ActionListener). Nu er alt dog ikke fryd og gammen, da interfaces altid medfører at man skal lave/implementerer nogle tvungne metoder. Det skal vi fordi et interface er en slags bunden regelsæt for en klasse, som betyder at hver eneste gang vi implementerer interfacet, skal vi lave dens metoder som nu engang er defineret i interfacet, uanset om vi skal bruge dem alle eller ej. Heldigvis for os, har interfacet ActionListener kun en metode som vi skal implementerer/lave. metoden hedder - public void actionPerformed(ActionEvent evt) - Denne metode er en metode, som Java automatisk leder efter, så snart vi har at gøre med en ActionListener. Det betyder at så snart vi tilføjer vores ActionListener til en af vores komponenter (Formentlig vores knap), så leder Java efter denne metode i klassen. Vi skal derfor for vores knap kalde metoden addActionListener(ActionListener l), og give ordet this some parameter, da vi har implementeret ActionListener i denne klasse. Vi skal også huske at importere java.awt.event pakken. Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Vi kan nu bare skrive hvilken handling som skal ske i vores metode actionPerformed(ActionEvent evt). Vi ville jo gerne have at indholdet fra vores ene tekst felt, skulle vises i vores andet tekst felt. Vi kan derfor benytte metoden getText() for at hente teksten, og metoden setText(String t) for at lave en ny tekst. Begge metoder kalder vi fra vores tekst felter objekter. Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Du kan nu køre din kode igen, og vi kan nu indtaste vores navn og klikke på knappen, og herefter se navnet i vores andet tekst felt. Vi har nu tilført en handling til vores knap. Men denne måde at lave handling på er for det første ikke særlig godt designet, og for det andet også lidt dum, da det i princippet kun giver os mulighed for at tilføje en enkelt handling til hele programmet. Der findes en anden og lidt smartere løsning på dette problem.

Indre Klasser


Løsningen er at benytte indre klasser. En indre klasse er en klasse inde i en anden klasse. En indre klasse kan bruge alt som er i den klasse den er defineret i, og derfor er den speciel god til at benytte som handlings klasser for vores program. Når du skal lave en indre klasse, behøver du ikke skrive ordet public til at starte med, men bare starte med ordet class efterfulgt af klassen navn. Så har du faktisk en indre klasse i vores klasse. Vi kan så implementere interfacet ActionListener til vores indre klasse istedet for vores hovedklasse, og herefter lave metoden actionPerformed i vores indre klasse. Dette er smart fordi vi så bare kan lave en ny indre klasse, som implementerer ActionListener og sætte denne som komponentens ActionListener. Så fjern nu implementeringen af interfacet og metoden actionPerformed fra vores klasse MinApplikation, og lav den i en indre klasse istedet. Vi kunne kalde vores indre klasse for KnapHandling, og i metoden for vores knap (addActionListener()) skriver du istedet for this i parameteren så bare new KnapHandling(). Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Hvis du kører dit GUI nu, vil du ligge mærke til at resultatet er det samme, men hvis vi nu f.eks. havde flere knapper, eller måske en menu, så kunne vi bare lave en ny indre klasse, som implementerede interfacet ActionListener. Også bare lave et nyt objekt af den indre klasse i vores addActionListener for det enkelte komponent. Det blev altså straks nemmere at tilføje nye handlinger til vores komponenter, og vores design blev pludselig også langt bedre. Men der findes en tredje og sidste løsning, på hvordan man kan tilføje handling til sine komponenter.

Anonyme Indre Klasser


I stedet for at skulle lave en indre klasse for hver eneste handling, kan vi lave noget som hedder en anonym indre klasse. Forskellen på en indre klasse og en anonym indre klasse, ligger i at det er en syntaktisk genvej for programmøren med en anonym indre klasse. Det betyder at der er langt hurtigere at lave en anonym indre klasse, for programmøren, men princippet er stort set det samme som med en indre klasse. Det først vi skal gøre er at slette vores indre klasse og dens actionPerformed metode. Herefter laver vi en helt almindelig mutator metode i klassen MinApplikation og vi kan bare gøre den private. Metoden kunne vi kalde noget så opfindsomt som knapHandling(). I metodens krop skriver du præcis det samme som der stod i actionPerformed metodens krop. Vi skal nu i parameteren til metoden addActionListener() skrive en helt masse. Først skal vi definere at vi arbejder med interfacet ActionListener og herefter give det en krop. Vi skriver altså - new ActionListener() { } - Herefter skriver du inde i dens krop signaturen til metoden actionperformed(ActionEvent evt), og giver denne en krop. Indholdet til parameteren ser nu således ud - new ActionListener() { public void actionPerformed(ActionEvent evt) { } } - Til sidst kunne vi vælge at skrive handlingskoden, som vi har gjort før i actionPerformed, men da vi allerede har lavet en metode med denne kode i, som hedder knapHandling(), kan vi bare lave et kald til denne metode. Husk at afslutte kaldet med et semikolon, vores indhold til parameteren ser nu således ud - new ActionListener() { public void actionPerformed(ActionEvent evt) { knapHandling(); } } - Og hele linjen ser således ud - knap.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { knapHandling(); } }); - Vores kode ser således ud:
Fold kodeboks ind/udJava kode 


Forskellige Layout modeller i Java


Som sagt så brugte vi ikke noget forud programmeret layout til vores GUI før, men der findes nogle layouts, som man nemt kan bruge og her vil jeg kort fortælle og vise dem.

FlowLayout
Hvis ikke vi satte vores layout før til null, ville Java automatisk hav brugt det som kaldes et FlowLayout. Et FlowLayout er et layout hvor alle komponenterne bliver sat ved siden af hindanden, også snart vi ikke have mere plads i vores vindue, ville de gå ned til næste linje. Her er koden til et FlowLayoout og nedenunder denne kan du se hvordan det ser ud i praksis.
Fold kodeboks ind/udJava kode 

Bemærk at vi bruger metoden pack() i vores vindue til allersidst. Denne metode "Pakker" alle komponenterne, sådan så vinduets størrelse passer til vores komponenter. Resultatet hvis vi kørte koden i vores Main klasse ville være som billedet nedenunder.



BoxLayout
Boks layout er et layout, hvor vi vælger hvordan vores komponenter skal vises Boks mæssigt. Vi kan f.eks. få en figur lignende FlowLayout'et hvis vi vil, men vi kan også få alle objekter til at stå oven på hinanden og nedad. (Ned ad på Y aksen). Koden til et box layout ser således ud.
Fold kodeboks ind/udJava kode 

Vi kan nu se at komponenterne ligger oven på hinanden ned af y aksen. Vores resultat er altså ligesom billedet nedenunder.



BorderLayout
Et borderLayout er et layout, hvor man angiver i hvilken retning komponenterne skal ligges. De kan f.eks. ligge i Nord, Syd, Øst, Vest og center. Derfor hver gang vi tilføjer et komponent til vores vindue (eller i dette tilfælde contentPane), så skal vi altså også definere i hvilken retning komponentet skal være. Vores kode ser sådan her ud.
Fold kodeboks ind/udJava kode 

Vores resultat kunne så se ud som følgende.



GridLayout
Til sidst findes der et layout, som hedder Grid layout. Det smarte ved dette layout, er at vi kan opstille komponenterne som ved en tabel. Vi kan f.eks. definere at vi ønsker et layout med 2 kolonner og 3 rækker. Dette gør vi selvfølgelig i kosntruktøren når vi sætter vores layout. Vores kode ser således ud:
Fold kodeboks ind/udJava kode 

Resultatet af vores GridLayout ses i billedet nedenunder. Vi kan f.eks. også se at alle knapperne bliver lige lange, så de får alle længden fra vores længste knap. Akkurat ligesom det sker i en tabel.



Vores første GUI med Borderlayout


Lad os prøve at lave endnu et GUI, men denne gang skal det benytte et BorderLayout til organisering af vores komponenter. Vi skal også her se på hvordan vi laver en menu bar, med nogle menuer og valgmulighder. Lad os først lave vidnuet, og da du kender rutinen til dette, vil jeg bare vise koden.
Fold kodeboks ind/udJava kode 

Vi kan nu lave en ny metode, og kalde den for lavMenBar(). I denne skal vi lave vores menu bar. Det første vi gør er at lave vores deklarere og initialisere et objekt af JMenuBar. Herefter skal vi tilføje/adde det til vores vindue, med metoden setJMenuBar(). Herefter kan vi lave menuerne i vores menu bar. Vi kan f.eks. lave en der hedder filer, og en som hedder funktioner. Vi skal lave menuerne med komponent klassen JMenu, og herefter tilføje dem til vores menu bar, med add metoden. Når vi har lavet menuerne, så skal vi lave valgmulighederne til menuerne, eller menuernes items som de også kaldes. Dette gøres med JMenuItem, og vi skal selvfølgelig huske at tilføjde disse til de enkelte menuer, med metoden add(). Vi kunne f.eks. i filer have en valgmulighed, der hed Afslut, og i funktioner en der hed Vis Navn. Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Nu har vi vores menuBar, og vi kan nu begynde at laves vores 'content'. Derfor skal vi lave en ny metode, som vi kunne kalde for lavKomponenter(), og her skal vi først have fat i vores vindues contentPane. Dette gøres ved at kalde metoden getContentPane(), også gemme det i en lokal variabel med datatypen Container (Husk at importere java.awt.*; ), fordi vores vindues cotentPane (indholds boks), jo er en container. Vi kan nu sætte vores layout for vores conatiner til at være et BorderLayout. Vi skal i vores contentPane have 3 komponenter. 2 JLabels, og et JPanel. labels var små tekst stykker, men vores JPanel var en anden slags container, som vi kan lægge andre komponenter ind i. Det er smart, fordi vi så kan give vores JPanel et andet layout, for at kunne få et flot design. Vi laver først objekt variabler til vores 3 komponenter. Herefter kan vi lave dem i metoden lavKomponenter(), og herefter tilføje dem til vores contentPane. Det første label skal være North, og kunne indeholde tekst som skriv dit navn i feltet, og vælg herefter funktioner - Vis Navn i menuen. Herefter skal vi lave vores Panel som skal være i positionen Center. Til sidst laver vi et label, som skal udskrive vores programs version (F.eks. version 1.0), og den skal være i bunden (Syd). Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Vi kan nu lave 2 tekst felter og tilføje dem til vores nye container (JPanel), og derfor skal vi nu sætte layoutet til denne container. Lad os benytte et FlowLayout til vores Panel, og herefter lave to tekst felter (Husk at lave dem som objekt variabler), og tilføj så disse tekst felter til vores panel. Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Vi har nu to tekst felter, som er tilføjet til vores panel. Hvis du kører dit GUI nu, vil du ikke rigtig kunne se tekstefelterne, det er fordi at vi skal sætte deres længde, når vi benytter et forud programmeret layout. Vi kan nemt gøre dette, for vi skriver bare længden i vores konstruktør til JTextField objekterne, og den vil automatisk få den ønskede længde. Længden er ikke i pixels, men i kolonner, og en kolonne er vel omkring 10-15 pixels, så vi kan bare skrive 8 i konstruktøren, for at få en længde på omkring 80 - 100 pixels. Vi vil også lave vores txtFieldVis til ikke redigerbart, og dette gøres jo med metoden setEditable(boolean b), også sætte metoden til false. Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Vores GUI ser nu således ud:



Lad os nu lave noget handling til vores menu. Vi har to menuer, med en valgmulighed i hver (et item i hver). Lad os lave nogle anonyme indre klasser, som bruger ActionListener. I actionPerformed laver vi så et kald til vores de enkelte metoder, som indeholder vores handling. Vi kunne lave en kaldet afslut() og en kaldet visNavn(). Vores metode afslut skal kun indeholde en linje, nemlig denne - System.exit(0); - Vores visNavn() skal indeholde noget kode som tager indholdet fra txtFieldNavn og sætter det som teksten der skal vises i txtFieldVis. Den skal herefter sætte teksten i txtFieldNavn til ikke at have nogen tekst (Så vi bruger stadig metoden setText(), men giver den en tom string, "", som parameter). Husk også at importere pakken java.awt.event.*; Vores kode ser nu således ud:
Fold kodeboks ind/udJava kode 

Vi har nu et fuldt fungerende GUI, som bruger en Menu Bar hvor vi har nogle valgmuligheder. Vi har set på hvordan man kan sætte forskellige layouts til sine 'containere/panels', efterhånden som man får brug for sådan noget. Det er vigtigt at øve sig i at sætte forskellige layouts så man ved præcis hvilket layout, som vil være den perfekte for ens program. Desuden er det også en god ide at tegne en skitse af sit GUI, på et stykke papir, som viser hvor man havde tænkt sig at de enkelte komponenter skulle være, og om man skulle benytte nogle forskellige containere til diverse komponenter. Det tager også tid at lære at programmere store programmer med et stort GUI, men hvis du kan lave dine egne GUI's i kode, er du straks blevet en programmør fra begynder niveau til en på et øvet niveau. Man kan bruge store editorer som NetBeans til at lave et GUI, helt uden kode, men det er en dårlig ide at begynde at lære at programmere GUI på denne måde, da du har svært ved at forstå hvordan den laver det for dig. Dog er det straks nemmere at lave sit program, da du kan lave GUI's via drag n drop, og du nemt kan tilføje alle mulige slags handlinger til dine komponenter. Så jeg anbefaler først at benytte et sådant program, når du har erfaring med at lave små GUI's i direkte kode. Ellers så glæd dig til næste artikel, hvor vi skal lære meget mere om Java Programmering.

Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (0)

Du skal være logget ind for at skrive en kommentar.
t