Java Programmering - 7. del

Tags:    java
Skrevet af Bruger #4487 @ 27.12.2010

Indledning


Indtil videre har vi kun kigget på tekst baserede applikationer/programmer, men kan man overhovedet ikke lave noget grafisk i Java? Svaret er, selvfølgelig kan man det! Java er et højt udviklet sprog, og kan i dag bruges til stort set alle typer af programmer, lige fra vores tekst baserede eksempler, til kontor programmer, og end dog spil. I denne artikel, skal vi med andre ord til at kigge på hvordan man kan lave nogle grafiske applikationer. Til dette skal vi først kende lidt til en klasse som hedder Graphics.

Graphics Klassen


Klassen Graphics er en abstrakt klasse, hvilket simpelthen betyder at man ikke kan lave objekter af klassen, og den kun eksisterer til at assisterer andre klasser med nogle specielle ting, i dette tilfælde nemlig metoder til at tegne grafik af forskellig art. Denne klasse ligger i bibliotekstien - java.awt.Graphics; - og skal selvfølgelig importeres i starten af vores klasser, ellers kan vi ikke benytte os af dens metoder.

Opgave - I dit Java Bibliotek, skal du finde klassen Graphics (Husk den ligger i pakken java.awt). Find herefter ud af hvilke metoder denne klasse har til at tegne forskellige ting med. Kan du også regne ud hvad forskellen på draw og fill metoderne er? Når opgaven er løst så læs videre.

I dit Java Bibliotek, kan vi finde metode som drawOval(), drawRect(), drawString(), fillOval() og fillRect() (Parametrene er udeladt her for læsevenlighedens skyld). Forskellen på en draw og en fill metode er at draw kun tegner kanten af vores grafik (f.eks. kanten af rektanglet), mens fill metoderne tegner både kanten og indholdet i en farve. Vores drawString() metode tegner en streng (tekst) som grafik, oftest i et vindue af en slags.

Nu har vi hørt lidt om Graphics klassen, og det er på tide at gå lidt videre, fordi vi skal nemlig have lidt mere teoretisk, før vi kan begynde på vores kode eksempler. Fordi vi skal nemlig høre lidt om JFrame og JPanel klasserne.

JFrame og JPanel


JFrame er en klasse til et vindue. Denne klasse ligger i et nyere java bibliotek, som hedder javax.swing. Oftest når man skal bruge disse klasser fra Swing klassen, importerer man bare alle klasserne direkte, da man oftest bruger flere end Jframe og for den sags skyld også JPanel. Som sagt før, så er JFrame en klasse til et vindue, som vi blandt andet kender dem i Windows. Vinduet kan vi så lægge et JPanel ind i. Et JPanel er så en slags kasse, som ligger inde i vores vindue (JFrame), og man kan på en måde kalde den vores container, som vi kan smide indhold ind i (I dette tilfælde grafik). Som en sidste ting før vi går i krig med koden, er at JPanel er en underklasse til klassen JComponent, som har en metode, som vi skal bruge senere der hedder paintComponent(Graphics g). Med denne metode og lille viden omkring JFrame og JPanel, kan vi nu gå igang med vores eksempel.

Vores første grafiske program


Det første vi skal gøre er at lave en klasse, som kan tegne vores forskellige grafiske komponenter. Jeg har valgt at kalde klassen for Tegn, men du kan selvfølgelig kalde din hvad du ønsker. Vores nye klasse Tegn, skal udvide (extends) fra klassen JPanel. Vi skal derfor selvfølgelig skrive - extends JPanel - i vores klassedefinition (Husk endelig også at importere Swing klassen fra javax.swing). I vores nye klasse skal vi så lave en metode, som skal præcis denne signatur - public void paintComponent(Graphics g) - Da den så vil override metoden som eksisterede i klassen JComponent, da JPanel var en underklasse til denne. Da parameteren er med datatypen Graphics, skal vi også huske at importere klassen Graphhics fra pakken java.awt. Vores metodes krop, skal du lade stå tom indtil videre. Når alt dette er udført, skulle din kode gerne se således ud.
Fold kodeboks ind/udJava kode 

Nu skal vi så putte noget kode ind i vores metode, som hedder paintComponent(Graphics g). Det første vi skal gøre er at kalde super klassens metode som hedder paintComponent, ligesom vores egen og give den parameteren g. Dette vil tegne baggrunden til vores JPanel (container). Måden vi gjorde for at kalde en super klasses metode, var at bruge nøgleordet super, også bruge dot notation, til at kalde metoden vi skal bruge.
Herefter kan vi tage vores parameters navn (som jo var, g) og kalde metoder fra vores Graphics klasse. Vi kunne jo f.eks. prøve at tegne et rektangel, så vi kan kalde metoden fillRect() via dot notation. Hvis du allerede har fundet den i Java biblioteket, kan du se at metoden har fire parametre. De første to er x og y værdier, og angiver positionen på skærmen, og i dette tilfælde i vores container (JPanel). Positionen starter altid fra øverste venstre hjørne af skærmen. De to sidste angiver bredden, og til sidst højden på vores rektangel (i pixels). Vi kunne f.eks. tegne et rektangel på positionen x=10, y=10, bredde=300 og højde=100. Vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Nå er vores Tegn klasse færdig, i hvert fald foreløbigt. Den vil nu kunne tegne et rektangel. Vi skal nu til at lave en Main klasse, med en main metode. Main klassen skal importere alle klasserne i javax.swing pakken. Main metoden skal lave et nyt objekt af vores klasse Tegn, og gemme objektet i en lokal variabel. Vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Herefter skal vi lave vores Vindue, hvor vi skal lægge vores panel (container) ind i. Husk at containeren, som vi vil benytte, allerede er defineret i vores Tegn klasse, og det er derfor vi har lavet et objekt af denne klasse og gemt objektet i en lokal variabel. Du kan sikkert huske at JFrame var klassen man brugte til at lave et vindue med, så vi skal lave et objekt af klassen JFrame, og gemme denne i en lokal variabel. Vi kan nu via dot notation kalde metoden add(), hvor vi lægger objektet fra klassen Tegn, som vi gemte i en lokal variabel, ind som parameter til denne metode. Når dette er gjort ser vores kode således ud.
Fold kodeboks ind/udJava kode 

Nå mangler vi kun tre små men meget vigtige ting. Det første er at sætte en standard funktion for hvad der skal ske, når vi klikker på krydset i vores vindue. Dette gøres ved at kalde metoden setDefaultCloseOperation() i vores JFrame objekt. Parameteren i denne metode, skal være - (JFrame.DISPOSE_ON_CLOSE); - og DISPOSE_ON_CLOSE skal være med store bogstaver. Det næste vi skal er at sætte vinduets størrelse (bredde og højde). Dette gøres med metoden setSize(), hvor parametrene er bredden og højden. Den sidste er en metode, som hedder setVisible(), som gør at vinduet bliver synligt på din skærm. Parameteren i denne metode, skal være en boolean, og i dette tilfælde true, da det er sandt at vinduet skal være visible (synligt). Vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Nu kan du så i kommandoprompt kompilerer dine Java filer, og herefter køre din Main klasse. Resultatet skulle gerne være at der popper et vindue op, som ligner billedet nedenunder.



Nu kunne en af de ting du tænker være, hvordan får jeg ændret farven på mit grafiske rektangel? Det er den næste ting vi skal kigge på.

Læg farve på din grafik


For at farvelægge vores grafiske komponenter, skal vi benytte klassen Color. Denne klasse ligger i pakken java.awt, og vi skal derfor importere den i vores Tegn klasse, for at kunne benytte den. Hvis du finder Color klassen i dit java bibliotek, vil du se at den har mere end en konstruktør. Vi kan vælge konstruktør alt afhængig af hvor meget vi vil gå i detaljen over hvilken farve vi ville have. En anden ting der er værd at ligge mærke til ved Color klassens dokumentation, er at vi her kan se nogle af dens felter. Dette er normalt ikke noget man gør, men med denne klasse har man valgt at gøre det lovligt for folk at tage en farve direkte via at felt, uden at bruge en metode først. Måden man kalder et felt på er akkurat, som hvis vi skulle kalde en metode i color klassen, bortset fra at man ikke laver paranteser efter felter. Lad os f.eks. prøve at give vores rektangel farven rød, ved hjælp af dot notation direkte til et af felterne.

Mini Opgave - I Tegn klassen, skal du før den linje hvor du tegner dit rektangel kalde metoden setColor() fra Graphics klassen. Parameteren i denne metode, skal være dot notation til feltet 'RED' i vores Color klasse, så du skriver som parameter - Color.RED - Husk herefter også at importere Color klassen fra stien java.awt. Din kode skulle nu gerne se således ud.
Fold kodeboks ind/udJava kode 

Når du kompilere Tegn klassen, og herefter kører din Main klasse i kommandoprompt, vil du se at din rektangel, pludselig er blevet rød i stedet for sort. Den vil altså se ud ligesom billedet nedenfor.



Lad os prøve at lave noget mere grafik, som har en anden farve. I din tegn klasse skal du nederst i metoden paintComponent, skrive først en ny setColor (Brug en anden farve en rød), og derefter laver du en drawString(), med følgende parametre - ("Her er noget tekst!", 100, 60) - Den første er den tekst, som skal vises som grafik. De to næste er henholdsvis x og y positionen i vores container (JPanel). Din kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Og dit output ville så se således ud (farven til teksten her jeg brugt grøn).



Som en sidste ting skal vi prøve at lave vores helt egen farve, i stedet for at bruge en af de standardiserede farver, som er angivet i Color klassens felter. Vi kunne prøve at lave en fyldt oval ( metoden fillOval() ), og give den en selv lavet kongeblå farve (Red: 65 Green: 105 Blue: 225). Måden vi giver den vores egen farve på, er ved i stedet at kalde et felt (Som faktisk i øvrigt var static, hvilket gør at vi faktisk ikke behøvede at lave et objekt af Color klassen), men ved at lave et objekt af Color klassen, ved at bruge en konstruktør, som kan indeholde et int tal til rød, grøn og blå. Vi kan ved at granske vores Color klasses dokumentation se at den har en metode, som skal bruge tre parametre, nemlig en int til henholdsvis r (red), g (green) og b (blue). Så når vi laver et objekt af klassen, giver vi bare vores konstruktør disse tre parametre, og den laver automatisk vores selv valgte farve. Husk at RGB'en for konge blå er r=65, g=105, b=225. Vi kalder så bare navnet på vores lokale variabel, hvor vi gemmer vores selvlavede Color objekt i, når vi skal sætte farven med setColor metoden. Herefter laver vi en fyldt Oval, med parametrene - (x=240, y=35, b=40, h=40) - Og vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Når du kompilere Tegn klassen, og kører din Main fil, skulle du gerne få følgende output.



Lidt mere grafik


For at vise lidt mere grafisk skal vi nu lave en grafisk 'linje', som tegnes op og ned, mens den skifter farve efterhånden som den tegnes. Til dette skal vi benytte to klasser, en til at tegne 'linjen' og lave vinduet som den tegnes i (Før tegnede vi vinduet i main klassen), og en Main klasse, med en main metode, som laver et objekt af vores 'linje' klasse, vi kunne kalde denne for Kurve, eller KurveTegning.

Lad os starte med vores 'KurveTegning' klasse, som skal være en underklasse til JPanel. Importer hele pakken java.awt, og hele javax.swing. Importer også et ArrayList fra java.util pakken. Lav så en konstruktør, og herefter to metoder. Den første skal have signaturen - public void paintComponent(Graphics g) - Og den anden skal være en private mutator metode, som du kan navngive lavVindue(). Indtil nu skal metoderne være tomme. Lav så to felter, et felt til at indeholde et ArrayList med objekter af typen Color, og et felt som er med typen int og hedder forskydning. Da vi ikke vil have at indholdet i feltet skal kunne ændres, benytter vi et nøgleord, som hedder final, og sætter feltet til en konstant værdi af 50. Ordet Final gør at værdien i feltet er konstant, og derfor ikke kan ændres. Husk at initialisere ArrayListen til at holde et objekt af ArrayList i konstruktøren. Vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Lad os først lave vores vindue, da vi allerede har set hvordan det skal gøres. Sæt vinduets størrelse til 300 (bredde) og 400 (højde), og husk at lave et internt kald til metoden i konstruktøren. Hvor vi før skrev - vindue.add(tegnePanel) - Skal du istedet for tegnePanel skrive nøgleordet this, da dette nøgleord refererer til et objekt af den samme klasse, som den er skrevet i (I dette tilfølde refererer this til et objekt af klassen KurveTegning). Vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Vi kan nu gå igang med at tegne vores grafik. Det første vi skal gøre er at kalde super klassens paintComponent(g), så vi for tegnet baggrundet til panelet. Herefter skal vi bruge en for løkke, som skal have signaturen - for (int i = 0; i < 400; i++) - da vi gerne vil have den til at køre løkken 400 gange. I løkkens krop skal vi lave en lokal variabel med datatypen Color, og lave en ny farve hver eneste gang ved at kalde konstruktøren, som skal bruge tre parametre (RGB). Men før vi skriver lidt om dette, skal vi vide lidt om en operator, som hedder modulo.

Modulo Operatoren


En modulo ser således ud i java (%). Operatoren/tegnet for dette er nemlig et procent tegn, og hvis vi skal forstå en modulo er vi nødt til at tage et par matematiske eksempler, med division. Det skal vi fordi modulo angiver 'resterne' af brøkken, og derfor virker en modulo også kun med hele tal. Du ved sikkert fra din matematik undervisning, at hvis vi har regnestykket 20/5, så er resultatet i hele tal 4, og vi har ingen rest til overs. Men hvad nu hvis stykket istedet for hed 19/5, så ville du sikkert sige at det blev et komma tal, og du derfor måske ikke kunne regne det i hovedet. Dette er også sandt, undtagen, når vi snakker i hele tal, fordi divisionsstykket 19/5 i hele tal giver resultatet 3 og 4 i rest. Hvis vi brugte divisionstegnet (/), ville vi få resultatet af divisionen i hele tal (nemlig 3), men hvis vi derimod skrev stykket således - 19%5 (nitten modulo fem) - Ville Java give resultatet 4, altså ikke resultatet af divisionen, men resultatet af divisionens rest. Hvis vi skulle sammenkæde lidt, kan vi sige at en modulo bruges når vi gerne vil have resten som resultat af en division. Jeg har herunder lavet fem eksempler på resultatet af en division og en modulo.


  • Division: 11/5 (resultat: 2) - Modulo: 11%5 (resultat: 1)

  • Division: 49/7 (resultat: 7) - Modulo: 49%7 (resultat: 0)

  • Division: 43/3 (resultat: 14) - Modulo: 43%3 (resultat: 1)

  • Division: 4/20 (resultat: 0) - Modulo: 4%20 (resultat: 20)

  • Division: 33/34 (resultat: 0) - Modulo: 33%34 (resultat: 34)



Når vi nu ved dette med modulo operatoren, kan vi gå tilbage til vores KurveTegning. Vi skulle jo i vores Color konstruktør angive tre parametre (RGB), og vi skal altså skrive - Color farve = new Color(i%256, (i*2)%256, (i*4)%256); - Herefter kalder vi vores add metode fra ArrayListen, og tilføjer Color objektet som er gemt i vores lokale variabel. Vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Løkken har nu lavet 400 farver, og gemt dem i vores ArrayList. Vi kan nu begynde at lave koden, som tegner vores kurve. Vi skal til dette benytte endnu en for løkken, med følgende signatur - for (int x = 0; x < 400; x++) - løkkens krop skal vi have tre lokale variabler. En for y positionen, og en for vores indeks, hvor vi skal hente vores farve i ArrayListen, og en til at gemme den farve vi henter fra vores ArrayList. Vi vil gerne have vores kurve til at ligne en slags sinusrytme, og for at gøre dette skal vi bruge en metode, som hedder sin(), som er at finde i klassen Math. Heldigvis behøver du ikke importerer klassen Math da denne klasse allerede er importeret fra lang pakken (Det er også i denne pakke at String er inkluderet). Da jeg ikke vil gå i detaljer med Sinusrelationerne her, så vil jeg bare skrive indholdet her, som skal puttes ind i den lokale variabel, for y positionen (husk at datatypen for den lokale variabel skal være en int). Vi skriver altså for y positionen - int y = 140 - (int) (130*Math.sin(0.05*x)); - Grunden til at vi skriver int i en parentes midt i det hele, er at Math.sin() metoden returnerer ikke en int, men en double, og vi vil gerne have at det skulle være en int. Derfor laver vi noget som hedder en casting, ved at skrive int i parentesen. Man kan ikke bare caste som man ønsker, men man kan godt caste fra en double, til en int. Man kan dog ikke caste fra en int til en double.
Nu er vores lokale variabel med y positionen klar, og vi kan nu gå videre med vores variabel for indekset og farven som vi henter. I variablen til indekset, skal vi lægge dette indhold ind i - (x+FORSKYDNING)%400; - og for variablen til at hente farven, skal vi kalde ArrayListens get() metode, og give den vores lokale variabel indeks, som parameter. Vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Nu har vi vores farve, og vores y position (x positionen er angivet via løkkens betingelse), og vi kan nu i løkken sætte grafikkens farve, med setColor() metoden, og herefter tegne en fyldt rektangel (fillRect()), med parametrene (x, y, 5, 5). Vores kode ser nu således ud.
Fold kodeboks ind/udJava kode 

Nu er KurveTegning klassen færdig, og vi kan nu lave vores Main klasse. Vi skal som det eneste i Main klassens Main metode, lave et objekt af klassen KurveTegning. Vores Main klasse ser således ud.
Fold kodeboks ind/udJava kode 

Kompiler nu og kør din Main fil, og resultatet skulle gerne se således ud.



Nu har vi kigget lidt på grafik, og hvordan vi laver vinduer. Vi har også lært lidt om Modulo operatoren, og hvordan vi farvelægger vores grafik. Vi ved nu også at nøgleordet final, kan gøre noget konstant. Følg med i 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 (3)

User
Bruger #17116 @ 11.03.12 15:35
Hej er rimelig ny indenfor Java og har fuldt alle de tidligere dele. Jeg har Arbejdet meget med Photoshop de sidste par år, og tænkte så om man ikke kan lave et design i photoshop og importere det til sit java program?
- Jeg har set i en af de andre tut at man skal lave en lommeregner, kan man så ikke lave knapperne i Photoshop så det ikke er de "kedelige" standart knapper? :)
User
Bruger #4487 @ 11.03.12 19:51
Du kan sagtens lave 'billed' knapper i Java, og på den måde 'style' dit GUI. Du også lægge baggrund på dit JFrame, eller JPanel, så du kan i princippet sagtens overføre dit photoshop layout til et GUI i java. En måde du f.eks. kan udskifte 'layout' på dine JButtons, er ved at benytte ImageIcon klassen som er med i pakken javax.swing. Her er et eksempel.
Fold kodeboks ind/udJava kode 

Følgende vil oprette en knap, med dit billede på. Herefter vil den gøre standard Look and Feel for knappen usynlig (undtagen billedet og kanten), og vi er derfor nødt til at give knappen en ny kant, som er en usynlig kant. Vupti, dit billede er ny en JButton :)

Håber at dette besvarede dit spørgsmål.
User
Bruger #4487 @ 11.03.12 20:06
Hader ikke at kunne redigere i mine kommentarer, nå men lige meget :P

Faktisk ville det være mere smart at lave en ny klasse kaldet f.eks. ImageJButton som arver fra JButton. På den måde får du alt funktionaliteten fra en JButton, og du kan i ImageJButton klassen konstruktør lave de ændringer der gør den normale knap usynlig og påfører dit billede. Et eksempel kunne være

ImageJButton.java
Fold kodeboks ind/udJava kode 

Du kan nu benytte klassen i stedet for Jbutton, når du skal lave dine billedfiler, dette gør du bare ved at oprette objekter af denne klasse i stedet for af JButton klassen.
Fold kodeboks ind/udJava kode 
Du skal være logget ind for at skrive en kommentar.
t