![]() |
![]() |
Start på kapittel om grafiske brukergrensesnitt |
interface MouseListener
:interface MouseMotionListener
:paint
-
eller paintComponent
-metode?Det som her blir kalt musehendelser, er i java-sammenheng objekter av
type MouseEvent
. Slike blir generert hver gang musa brukes
på en eller annen måte mens musepekeren peker på en
skjermkomponent. Det finnes flere forskjellige typer lytteobjekt som
kan brukes til å fange opp musehendelser. Nedenfor er det satt opp
en oversikt over de forskjellige typer interface
som kan
implementeres for å definere lytteobjekt for mus og hvilke metoder
som tilhører hvert interface
.
Som du ser, indikerer navnene på metodene hvilken type museaktivitet
vedkommende metode kan bli aktivert av. Når det i beskrivelsen nevnes
'komponenten', så menes en skjermkomponent som det er registrert
lytteobjekt for av den type som vedkommende interface
spesifiserer.
interface MouseListener
:public void mousePressed( MouseEvent e ) { ... }
public void mouseReleased( MouseEvent e ) { ... }
public void mouseClicked( MouseEvent e ) { ... }
public void mouseEntered( MouseEvent e ) { ... }
public void mouseExited( MouseEvent e ) { ... }
interface MouseMotionListener
:public void mouseMoved( MouseEvent e ) { ... }
public void mouseDragged( MouseEvent e ) { ... }
Begge interface MouseListener
og
MouseMotionListener
befinner seg i pakken
java.awt.event
. Fra og med java-versjon 1.4 er det i pakken
javax.swing.event
definert et
interface
MouseInputListener
som omfatter alle metodene i de to nevnte
interface
. Som alternativ til å implementere de to nevnte
interface
hver for seg (i tilfelle man har bruk for metoder
fra begge), kan man derfor implementere dette siste. Men dette vil da
ikke bli kompatibelt bakover til eldre kjøresystemer for java-programmer.
De nevnte typer muselyttere kan registreres for alle typer
swing
-komponenter.
For å knytte muselyttere til en komponent, gjør man kall på komponentens
metoder addMouseListener
og addMouseMotionListener
for å knytte til lytteobjekt av type henholdsvis MouseListener
og
MouseMotionListener
. Det finnes imidlertid ingen tilsvarende
metode addMouseInputListener
. Isteden må man gjøre kall på begge
de nevnte metodene for å knytte til et lytteobjekt av type
MouseInputListener
. (Interface MouseInputListener
arver fra både MouseListener
og MouseInputListener
.
Et objekt av type MouseInputListener
kan derfor oppfattes både
som en MouseListener
og som en MouseMotionListener
.)
I java-versjon 1.4 er det også definert et
interface
MouseWheelListener
med metoden
public void mouseWheelMoved( MouseWheelEvent e ) { ... }
Denne vil bli aktivert hver gang musehjulet blir rotert (forutsatt at det er registrert lytteobjekt av denne type for komponenten). Vi trenger imidlertid vanligvis ikke implementere noen musehjulslytter. Musehjulet brukes hovedsakelig for skrolling, og komponenter med skrollefelter registrerer automatisk musehjulslyttere som vil reagere riktig på bruk av musehjulet.
I beskrivelsen ovenfor er det brukt uttrykket 'en museknapp'.
Vanligvis er det venstre museknapp det da er snakk om. Dersom vi trenger
å sjekke om det dreier seg om høyre museknapp, eller den midterste
museknappen for en mus med tre knapper på, kan vi bruke vedkommende
metodes MouseEvent
-parameter e
til å gjøre
kall på metoden isMetaDown
, som returnerer true
i tilfelle høyre museknapp er trykket ned, eller metoden
isAltDown
, som returnerer true
i tilfelle
den midterste museknappen er trykket ned.
Som vi har sett, inneholder de interface
som spesifiserer
lytteobjekter for mus mange metoder. Ofte er det bare én eller noen få
av metodene vi har behov for å implementere. Men for å implementere
vedkommende interface
er vi da nødt til å implementere
de øvrige metodene med tomt innhold. For at vi skal slippe å gjøre
dette, er det i javas klassebibliotek definert klasser som implementerer
hvert interface
med metoder som har tomt innhold.
Disse klassene blir med et felles navn kalt for adapterklasser.
Når vi skal definere et lytteobjekt der vi ikke trenger å implementere
alle metodene i vedkommende interface
, kan vi derfor
velge å definere en subklasse til vedkommende adapterklasse der vi
redefinerer den eller de metodene som vi har behov for å implementere.
Dermed slipper vi å implementere metoder med tomt innhold. (Vi kjenner
for øvrig til dette allerede i forbindelse med programmering av
lukkeknappen til et vindu. Vi har da definert en subklasse til adapterklassen
WindowAdapter
der vi har redefinert metoden
windowClosing
.)
Adapterklassene til MouseListener, MouseMotionListener
og
MouseInputListener
heter henholdsvis
MouseAdapter, MouseMotionAdapter
(begge i pakke
java.awt.event
) og MouseInputAdapter
(i pakke
javax.swing.event
).
Når vi skal programmere en muselytter, er det som regel behov for å
finne ut hvor på den aktuelle komponenten musehendelsen fant sted. Opplysninger om
dette kan vi hente ut av MouseEvent
-objektet som er
parameter til vedkommende metode. Heter denne parameteren e
,
så vil e.getX()
og e.getY()
gi oss
koordinatene i antall piksler relativt til øverste venstre hjørne i
vedkommende komponent.
paint
-
eller paintComponent
-metode?Noen ganger kan vi ønske oss å kunne tegne grafikk (inkludert tekst)
på skjermen uten å måtte gjøre bruk av paint
-
eller paintComponent
-metoden.
Som vi husker, har disse metodene en parameter av type
Graphics
som vi bruker for å få tegnet ut grafikk (ved
kall på metodene drawString, drawLine
, etc.).
Hver grafisk komponent har det som blir kalt en grafikk-kontekst.
Det vil si at det til komponenten er knyttet et Graphics
-objekt.
Når dette objektet blir brukt til å gjøre kall på metodene
drawString, drawLine
, etc., så vil grafikken bli tegnet ut på nettopp
denne komponenten. Det vi trenger er derfor å få tak i det
Graphics
-objektet som representerer grafikk-konteksten til
vedkommende komponent. Dette får vi tak i ved å gjøre kall på
komponentens metode getGraphics
. Retur-objektet fra denne metoden
må vi selvsagt fange opp i en variabel av type Graphics
.
Det er imidlertid viktig å være klar over at grafikk-konteksten ikke
vil eksistere før komponenten er såkalt realisert. I praksis vil dette si at
setVisible(true)
eller pack()
er utført på komponenten,
eller dersom komponenten er add
'et til en container som er blitt realisert.
Før denne tid vil metoden getGraphics
returnere null
.
I følgende programeksempel blir denne teknikken brukt for å få tegnet ut
grafikk på et panel uten å gjøre bruk av panelets
paintComponent
-metode.
Koden for tegnepanelet
Musepanel
er gjengitt nedenfor.
For panelet er det definert og registrert et lytteobjekt av
type MouseListener
. Lytteobjektet er definert av den indre klassen
Muselytter
i panelklassen. Når en museknapp blir trykket ned (samme
hvilken av dem), blir det gjort kall på metoden mousePressed
.
Denne er programmert til å registrere posisjonen for dette og skrive den ut på
panelet. Starten for utskriften er i vedkommende posisjon.
Utskriften gjøres uten bruk av
paintComponent
-metoden, slik det er forklart ovenfor.
Vi ønsker dessuten at det nederst i vinduet som panelet skal ligge i
samtidig blir skrevet ut en melding om at museknapp ble
trykket ned. For å få til dette, trenger vi en kommunikasjonsvei tilbake til
vindusklassen som har opprettet panelet og plassert det på sin vindusflate.
Denne kommunikasjonen får vi til ved at det i panelklassen er et datafelt av
den typen som vindusklassen definerer. Datafeltet blir i muselytteren brukt til å
gjøre kall på metoden visHendelse
i vindusklassen. Denne metoden
setter tekst for en label som vises nederst i vinduet. Vindusklassen
Testvindu
er gjengitt
etter tegnepanelet Musepanel
nedenfor.
Muselytteren virker videre på den måten at dersom museknappen slippes opp
igjen på samme sted som den er trykket ned, så blir metoden
mouseClicked
kalt opp. Denne skriver
på det stedet dette skjedde melding om at musa ble klikket, samt posisjonen.
Dersom musa derimot flyttes mens knappen holdes nede, så vil det
på det stedet der knappen slippes opp igjen bli gjort kall på metoden
mouseReleased
. Denne skriver ut melding om at
museknappen ble sluppet, samt posisjonen for dette. I tillegg blir
det tegnet en rett linje mellom det sted museknappen ble trykket ned
(posisjonen for dette ble i sin tid registrert)
og der den ble sluppet opp igjen.
Når musepekeren blir flyttet inn på panelet utenfra, blir det gjort kall på
metoden mouseEntered
i det øyeblikk musepekeren kommer inn på
panelet. Metoden registrerer i hvilken posisjon dette skjedde.
Dette blir kommunisert til vinduet ved kall på dets metode
visHendelse
. Dessuten settes panelets bakgrunnsfarge til hvit.
Når musepekeren går ut av panelet, er det metoden
mouseExited
som blir kalt opp. Denne er gitt tilsvarende virkning
som mouseEntered
, men nå settes bakgrunnsfargen til rød.
Noe annet
som er programmert inn i dette eksemplet, er at hver gang en museknapp
trykkes ned, så blir panelet blanket ut før ny tekst skrives
ut. Dette oppnås ved kall på metoden update
der
panelets tilknyttede Graphics
-objekt blir brukt som aktuell
parameter. Driverklasse for programmet finnes i fila
Muselytterdemo.java
.
På følgende bilde er en museknapp blitt trykket ned på ett sted i panelet, så
er den holdt nede mens musa er flyttet til et annet sted, der knappen er sluppet
opp igjen. Nederst i vinduet er det skrevet ut i hvilken posisjon musepekeren
kom inn på panelet.
1 import javax.swing.*; 2 import java.awt.*; 3 import java.awt.event.*; 4 5 public class Musepanel extends JPanel 6 { 7 private int xPos, yPos; //posisjon for klikk på museknapp 8 private Testvindu vindu; 9 10 public Musepanel() 11 { 12 setBackground( Color.red ); 13 addMouseListener( new Muselytter() ); 14 } 15 16 public void setVindu(Testvindu v) 17 { 18 vindu = v; 19 } 20 21 public Dimension getPreferredSize() 22 { 23 return new Dimension( 400, 400 ); 24 } 25 26 private class Muselytter implements MouseListener 27 { 28 public void mouseClicked( MouseEvent e ) 29 { 30 Graphics panelgrafikk = getGraphics(); 31 update( panelgrafikk ); //blanker skjermen 32 panelgrafikk.drawString( "Mus klikket i posisjon [" + 33 e.getX() + ", " + e.getY() + "]", e.getX(), e.getY() ); 34 } 35 36 public void mouseEntered( MouseEvent e ) 37 { 38 setBackground( Color.white ); 39 vindu.visHendelse( "Musepeker entret i posisjon [" + 40 e.getX() + ", " + e.getY() + "]" ); 41 } 42 43 public void mouseExited( MouseEvent e ) 44 { 45 setBackground( Color.red ); 46 int x = Math.max( 0, e.getX() ); 47 int y = Math.max( 0, e.getY() ); 48 vindu.visHendelse( "Musepeker gått ut av panel i posisjon [" + 49 x + ", " + y + "]" ); 50 } 51 52 public void mousePressed( MouseEvent e ) 53 { 54 Graphics panelgrafikk = getGraphics(); 55 update( panelgrafikk ); 56 xPos = e.getX(); 57 yPos = e.getY(); 58 panelgrafikk.drawString( "Museknapp trykket i posisjon [" + 59 e.getX() + ", " + e.getY() + "]", xPos, yPos ); 60 } 61 62 public void mouseReleased( MouseEvent e ) 63 { 64 Graphics panelgrafikk = getGraphics(); 65 panelgrafikk.drawString( "Museknapp sluppet i posisjon [" + 66 e.getX() + ", " + e.getY() + "]", e.getX(), e.getY() ); 67 panelgrafikk.drawLine( xPos, yPos, e.getX(), e.getY() ); 68 } 69 } //class Muselytter 70 } //class Musepanel 1 import javax.swing.*; 2 import java.awt.*; 3 4 public class Testvindu extends JFrame 5 { 6 private Musepanel tegnepanel; 7 private JLabel rapport; 8 9 public Testvindu() 10 { 11 super( "Musehendelser" ); 12 tegnepanel = new Musepanel(); 13 rapport = new JLabel(); 14 Container c = getContentPane(); 15 c.setLayout( new FlowLayout() ); 16 c.add( tegnepanel ); 17 c.add( rapport ); 18 setVisible( true ); 19 tegnepanel.setVindu(this); 20 } 21 22 public void visHendelse( String beskrivelse ) 23 { 24 rapport.setText( beskrivelse ); 25 } 26 }
Merknad Tegnepanelet kalt Musepanel
skal kommunisere med vinduet
som det ligger i for å skrive ut museposisjoner i et tekstfelt i vinduet.
For å opprette forbindelse til vinduet er det i panelet definert en metode
setVindu
. Denne blir kalt opp på slutten av vindusklassens konstruktør:
19 tegnepanel.setVindu(this);
Nøkkelordet this
refererer til
det objektet som utfører vedkommende kode, i dette tilfelle vindusobjektet, siden
instruksjonen står inne i konstruktøren til vindusobjektet. Generelt blir det advart
mot å bruke this
inne i konstruktøren
til et objekt, siden objektet ikke er ferdig konstruert ennå. Derfor er det
nevnte metodekallet plassert helt på slutten av konstruktøren, etter at vinduet er gjort
synlig på skjermen som følge av instruksjonen
18 setVisible( true );
Når den instruksjonen er utført, er vinduet blitt såkalt realisert og dermed ferdig konstruert.
Bruk av nøkkelordet this
skulle derfor
være trygt å gjøre på dette tidspunkt.
Programkoden for tegnepanelet
Musedrag
er gjengitt
nedenfor. Det blir for
panelet definert og registrert lytteobjekter for både museklikk og
musebevegelse. Når det gjelder museklikk, er det bare noen av metodene
det er behov for å implementere. Lytteklassen Muselytter
blir derfor definert som
en subklasse til adapterklassen MouseAdapter
, slik at vi
skal slippe å definere tomme metoder. Når det gjelder musebevegelser,
er det imidlertid behov for å implementere begge metodene spesifisert
av interface MouseMotionListener
. Lytteklassen
Musebevegelseslytter
blir derfor spesifisert til å
implementere dette.
Det blir registrert hvor på panelet museknappen blir trykket ned. Når musa dras med knappen nede, blir det kontinuerlig vekselvis trukket en rett (svært kort!) linje fra forrige til nåværende posisjon, og foretatt oppdatering av forrige posisjon ved at nåværende posisjon blir registrert som ny forrige-posisjon. Effekten av dette blir at musa virker som en penn: En kan skrive og tegne med musepekeren når museknappen holdes nede. Museposisjonen blir hele tida skrevet ut nederst i vinduet som panelet ligger i. For øvrig virker programmet på tilsvarende måte som det forrige på den måten at panelets bakgrunnsfarge skifter mellom hvit og rød avhengig av om musepekeren er inni eller utenfor panelet. Hver gang den kommer utenfor, blir dessuten panelet blanket ut.
Panelet er plassert i vinduet
Tegnevindu
.
Dette har helt tilsvarende innhold og virkemåte som vinduet
Testvindu
i foregående eksempel. Driverklasse for programmet
finnes i fila
Musetegning.java
.
I læreboka Deitel & Deitel, 9. utgave Fig. 14.34 og Fig. 14.5 er det et liknende program som er laget på en litt annen måte. Dette programmet gir imidlertid ikke sammenhengende strek dersom en fører musepekeren litt for fort.
Følgende bilde viser et eksempel på hvordan tegnevinduet til vårt program kan bli seende ut.
1 import javax.swing.*; 2 import java.awt.*; 3 import java.awt.event.*; 4 5 public class Musedrag extends JPanel 6 { 7 private int xPos, yPos; 8 private Tegnevindu vindu; 9 10 public Musedrag() 11 { 12 setBackground( Color.red ); 13 addMouseListener( new Muselytter() ); 14 addMouseMotionListener( new Musebevegelseslytter() ); 15 } 16 17 public void setTegnevindu(Tegnevindu v) 18 { 19 vindu = v; 20 } 21 22 public Dimension getPreferredSize() 23 { 24 return new Dimension( 400, 400 ); 25 } 26 27 private class Muselytter extends MouseAdapter 28 { 29 public void mouseEntered( MouseEvent e ) 30 { 31 setBackground( Color.white ); 32 repaint(); 33 } 34 35 public void mouseExited( MouseEvent e ) 36 { 37 setBackground( Color.red ); 38 repaint(); 39 } 40 41 public void mousePressed( MouseEvent e ) 42 { 43 xPos = e.getX(); 44 yPos = e.getY(); 45 } 46 } //class Muselytter 47 48 private class Musebevegelseslytter implements MouseMotionListener 49 { 50 public void mouseDragged( MouseEvent e ) 51 { 52 Graphics panelgrafikk = getGraphics(); 53 panelgrafikk.drawLine( xPos, yPos, e.getX(), e.getY() ); 54 xPos = e.getX(); 55 yPos = e.getY(); 56 vindu.visHendelse("Museposisjon: [ " + xPos + ", " + yPos + "]"); 57 } 58 59 public void mouseMoved( MouseEvent e ) 60 { 61 vindu.visHendelse("Museposisjon: [ " + e.getX()+ ", " + 62 e.getY() + "]"); 63 } 64 } //class Musebevegelseslytter 65 } //class Musedrag
Vi skal omarbeide tegneprogrammet i forrige programeksempel slik at brukeren har muligheter til underveis å velge bredde på tegnestreken samt hvilken av noen standardfarger det skal tegnes med. For fargevalget ønsker vi at fargealternativene skal vises i tegnevinduet som en listeboks med små fargepaneler, slik at når brukeren klikker på et fargepanel i denne listeboksen, så blir panelets farge satt som ny tegnefarge. Før vi kan skrive de nødvendige endringene i programkoden, må vi da vite hvordan vi kan få satt strekbredde, og hvordan vi kan få vist fargepaneler i en listeboks.
Hvordan sette strekbredde?
Som tidligere nevnt, er det til hver grafisk komponent på skjermen knyttet et
Graphics
-objekt som vi kan få tak i ved å gjøre kall på komponentens
metode getGraphics
. Objektet kalles komponentens grafikk-kontekst.
Når vi ved hjelp av dette objektet gjør kall på tegnemetoder som
drawLine, drawOval
, etc., så vil bredden på tegnestreken være lik
én piksel. I Graphics
-klassen finnes det ingen metoder som
vi kan bruke til å få endret på dette. Men det objektet som blir returnert fra
getGraphics
-metoden er egentlig av subklassetypen Graphics2D
, og
i denne klassen finnes det metoder for å endre strekbredden. For å gjøre bruk av dette,
kan vi imidlertid ikke skrive
Graphics2D g = getGraphics();
Det vil resultere i kompileringsfeil. Grunnen er at metoden getGraphics
er deklarert til å returnere en verdi av type Graphics
, og det er jo oppfylt
av Graphics2D
, siden det er en subklassetype. Men vi kan ikke tilordne returverdien
til et objekt av typen Graphics2D
, for kompilatoren tror at den er av type
Graphics
, siden det er det som står i metodesignaturen for getGraphics
.
Vi må isteden foreta eksplisitt typekonvertering:
Graphics g = getGraphics(); Graphics2D g2 = (Graphics2D) g;
For å sette ønsket strekbredde kan vi nå skrive
g2.setStroke( new BasicStroke( strekbredde ) );
Men merk deg: Parameteren strekbredde
må være av datatypen
float
.
Husk også at når vi angir en konkret slik verdi, så må den avsluttes med stor eller liten f, som
i eksemplet
float strekbredde = 2.5F;
Begge klassene Graphics2D
og BasicStroke
ligger i pakken
java.awt
.
Hvordan sette tegnefarge?
For objekter av type Graphics
vet vi at vi kan sette tegnefarge ved bruk av metoden
setColor
. Denne blir arvet til subklassen Graphics2D
og kan også brukes
på objekter av denne typen. Men i Graphics2D
finnes det også en metode
setPaint
med noe større potensiale. Den har parameter av type Paint
,
som er navnet på et interface
som blir implementert blant annet av
Color
-klassen. For å sette tegnefarge for et Graphics2D
-objekt
g2
kan vi derfor også skrive
g2.setPaint( farge );
der farge
er ønsket Color
-objekt.
Definere visning av valgalternativene i en listeboks
Når valgalternativene i en listeboks blir vist på skjermen, brukes det en såkalt cellerendrer til
å vise alternativene. Dersom vi ikke bestemmer noe annet, er det en forhåndsdefinert rendrer
som brukes. Denne vet hvordan den skal vise tekst og bilder. Dersom listeboksalternativene er av
annen type enn tekst eller bilde, er det resultatet av objektenes toString
-metode
som blir vist som alternativer i listeboksen. Dette er som regel ikke tilfredsstillende. For å få
noe annet, er det nødvendig å definere og installere vår egen cellerendrer. Det skal vi nå gjøre.
Vi ønsker at listeboksalternativene skal bestå av Color
-objekter og at
listeboksen skal vise alternativene i form av små paneler som har de aktuelle fargene. Vi må da gjøre
følgende:
interface ListCellRenderer<Color>
setCellRenderer
Fra og med javaversjon 7 er
interface ListCellRenderer<E>
en parametrisert type. For nærmere beskrivelse av parametriserte typer, se
Bruk av generiske datatyper.
En cellerendrer som viser listeboksalternativene på den ønskede måten kan vi skrive slik:
private class Fargelisterendrer implements ListCellRenderer<Color> { //Følgende panel blir returnert for alle celler i listeboksen med //bakgrunnsfarge satt lik den farge som skal velges. Vil vise //hvert alternativ som et panel med den farge som alternativet //representerer. private JPanel panel = new JPanel(); public Component getListCellRendererComponent( JList<? extends Color> liste, Color verdi, // listealternativ som skal vises int indeks, // celleindeks boolean erValgt, // er cella valgt? boolean harFokus) // lista og cella har fokus { panel.setBackground( verdi ); return panel; } }
Vi skulle dermed ha nok bakgrunnskunnskaper til å ta for oss selve programkoden.
Tegnepanelet er definert av klassen
Tegnepanel
som er gjengitt nedenfor.
Grafikk-konteksten er konvertert til type Graphics2D
for å kunne sette
bredde på tegnestreken. Dette er det definert set
-metode for. Legg merke
til at grafikk-konteksten lagres i et datafelt, slik at vi skal slippe å hente den fram
og typekonvertere den hver gang det skal tegnes på panelet. Legg også merke til at
det er definert en set
-metode for grafikk-konteksten. Grunnen til dette
er at den ikke vil eksistere før panelet er realisert, det vil si vist på skjermen.
Derfor blir metoden setPanelgrafikk
kalt opp fra vinduet som panelet ligger i,
etter at vinduet er opprettet og gjort synlig.
Det er dessuten definert funksjonalitet for å foreta utvisking av det som blir tegnet.
Utvisking av hele tegningen, slik at vi kan starte på nytt, oppnås ved bruk av metoden
blankUtTegning
. Denne gjør kort og godt kall på repaint
som
blanker ut tegneflata. Metoden viskUt
er definert
for å kunne viske ut deler av det som er tegnet. Metoden setter strekbredden
til 5.0 og tegnefargen lik panelets bakgrunnsfarge, slik at vi kan tegne med
bakgrunnsfargen. Dessuten lagrer metoden verdiene
som strekbredde og tegnefarge hadde før utvisking skal foretas, slik at vi kan bruke
metoden avsluttVisking
for å få tilbake de gamle verdiene og fortsette å tegne
med de gamle verdiene når vi har visket ut det vi vil.
1 import java.awt.*; 2 import java.awt.event.*; 3 import javax.swing.*; 4 5 public class Tegnepanel extends JPanel 6 { 7 private int xPos, yPos; 8 private Graphics2D panelgrafikk; 9 private Color tegnefarge = Color.BLACK; 10 private float strekbredde = 1.0F; 11 12 public Tegnepanel() 13 { 14 setBackground( Color.white ); 15 addMouseListener( new Muselytter() ); 16 addMouseMotionListener( new Musebevegelseslytter() ); 17 } 18 19 public void setPanelgrafikk() 20 { 21 Graphics g = getGraphics(); 22 panelgrafikk = (Graphics2D) g; 23 } 24 25 public void setTegnefarge( Color farge ) 26 { 27 if ( panelgrafikk != null ) 28 panelgrafikk.setPaint(farge); 29 } 30 31 public void setStrekbredde( float bredde ) 32 { 33 if ( panelgrafikk != null ) 34 panelgrafikk.setStroke(new BasicStroke(bredde)); 35 } 36 37 public void blankUtTegning() 38 { 39 repaint(); 40 } 41 42 public void viskUt() 43 { 44 tegnefarge = panelgrafikk.getColor(); 45 BasicStroke strek = (BasicStroke) panelgrafikk.getStroke(); 46 strekbredde = strek.getLineWidth(); 47 setTegnefarge(getBackground()); 48 setStrekbredde(5.0F); 49 } 50 51 public void avsluttVisking() 52 { 53 setStrekbredde( strekbredde ); 54 setTegnefarge(tegnefarge); 55 } 56 57 public Dimension getPreferredSize() 58 { 59 return new Dimension( 900, 500 ); 60 } 61 62 private class Muselytter extends MouseAdapter 63 { 64 public void mousePressed( MouseEvent e ) 65 { 66 xPos = e.getX(); 67 yPos = e.getY(); 68 } 69 } //class Muselytter 70 71 private class Musebevegelseslytter extends MouseMotionAdapter 72 { 73 public void mouseDragged( MouseEvent e ) 74 { 75 panelgrafikk.drawLine( xPos, yPos, e.getX(), e.getY() ); 76 xPos = e.getX(); 77 yPos = e.getY(); 78 } 79 } //class Musebevegelseslytter 80 }
Klassen
Skriblevindu
definerer tegnevinduet.
Den er gjengitt nedenfor.
I den blir det opprettet en listeboks med Color
-objekter som alternativer.
For listeboksen er det dessuten nødvendig å gjøre noen tilpasninger ved å skrive følgende
instruksjoner:
fargevelger.setVisibleRowCount( farger.length ); fargevelger.setFixedCellWidth( 80 ); fargevelger.setFixedCellHeight( 30 ); fargevelger.setCellRenderer( new Fargelisterendrer() ); fargevelger.addListSelectionListener( new Fargelytter() );
Den første instruksjonen bestemmer antall alternativer som skal være synlige samtidig. Vi ønsker at alle skal være det. (Forhåndssatt verdi er 8.) De to neste instruksjonene bestemmer hvor store de skal være panelene som viser fargene. De to siste instruksjonene installerer cellerendrer og lytteobjekt for listeboksen.
For å velge bredde på tegnestreken blir det definert en komboboks med tekstalternativer. Det er lagt opp til å kunne velge bredde fra 1 til 5 piksler, i sprang på en halv piksel. Legg for øvrig merke til hvordan lytteobjektet for listeboksen er definert.
1 import java.awt.*; 2 import javax.swing.*; 3 import java.awt.event.*; 4 import javax.swing.event.*; 5 6 public class Skriblevindu extends JFrame 7 { 8 private Tegnepanel tegnepanel; 9 private JButton vindusblanker, viskUt, sluttVisking; 10 private JComboBox<String> strekvelger; 11 private JList<Color> fargevelger; 12 private Color[] farger = {Color.black, Color.blue, Color.cyan, 13 Color.darkGray, Color.gray, Color.green, Color.magenta, 14 Color.orange, Color.pink, Color.red, Color.yellow}; 15 16 public Skriblevindu() 17 { 18 super( "Musetegning" ); 19 tegnepanel = new Tegnepanel(); 20 fargevelger = new JList<>( farger ); 21 fargevelger.setVisibleRowCount( farger.length ); 22 fargevelger.setFixedCellWidth( 80 ); 23 fargevelger.setFixedCellHeight( 30 ); 24 fargevelger.setCellRenderer( new Fargelisterendrer() ); 25 fargevelger.addListSelectionListener( new Fargelytter() ); 26 String[] strekvalg = new String[9]; 27 double bredde = 1.0; 28 for ( int i = 0; i < strekvalg.length; i++ ) 29 { 30 strekvalg[i] = bredde + ""; 31 bredde += 0.5; 32 } 33 strekvelger = new JComboBox<>(strekvalg); 34 vindusblanker = new JButton("Blank ut tegning"); 35 viskUt = new JButton("Visk ut"); 36 sluttVisking = new JButton("Slutt å viske"); 37 Knappelytter lytter = new Knappelytter(); 38 strekvelger.addActionListener(lytter); 39 vindusblanker.addActionListener(lytter); 40 viskUt.addActionListener(lytter); 41 sluttVisking.addActionListener(lytter); 42 JPanel knappepanel = new JPanel(); 43 knappepanel.add(new JLabel("Strekbredde")); 44 knappepanel.add(strekvelger); 45 knappepanel.add(vindusblanker); 46 knappepanel.add(viskUt); 47 knappepanel.add(sluttVisking); 48 Container c = getContentPane(); 49 c.add(fargevelger, BorderLayout.LINE_START); 50 c.add(knappepanel, BorderLayout.PAGE_END); 51 c.add( tegnepanel, BorderLayout.CENTER ); 52 pack(); 53 setVisible(true); 54 tegnepanel.setPanelgrafikk(); 55 } 56 57 private class Knappelytter implements ActionListener 58 { 59 public void actionPerformed( ActionEvent e ) 60 { 61 if ( e.getSource() == strekvelger ) 62 { 63 String valg = (String) strekvelger.getSelectedItem(); 64 float bredde = Float.parseFloat(valg + "F"); 65 tegnepanel.setStrekbredde(bredde); 66 } 67 else if ( e.getSource() == vindusblanker ) 68 tegnepanel.blankUtTegning(); 69 else if ( e.getSource() == viskUt ) 70 tegnepanel.viskUt(); 71 else if ( e.getSource() == sluttVisking ) 72 tegnepanel.avsluttVisking(); 73 } 74 } 75 76 private class Fargelytter implements ListSelectionListener 77 { 78 public void valueChanged( ListSelectionEvent e ) 79 { 80 if ( e.getSource() == fargevelger ) 81 tegnepanel.setTegnefarge( 82 farger[ fargevelger.getSelectedIndex() ] ); 83 } 84 } 85 86 //Definerer hvordan listealternativene i en liste skal vises. 87 private class Fargelisterendrer implements ListCellRenderer<Color> 88 { 89 //Følgende panel blir returnert for alle celler i lista med 90 //bakgrunnsfarge satt lik den farge som skal velges. Vil vise 91 //hvert alternativ som et panel med den farge som alternativet 92 //representerer. 93 private JPanel panel = new JPanel(); 94 95 public Component getListCellRendererComponent( 96 JList<? extends Color> liste, 97 Color verdi, // listealternativ som skal vises 98 int indeks, // celleindeks 99 boolean erValgt, // er cella valgt? 100 boolean harFokus) // lista og cella har fokus 101 { 102 panel.setBackground( verdi ); 103 return panel; 104 } 105 } 106 }
Kjøreklasse for programmet finnes på følgende link:
Tegneprogram.java
.
Følgende bilde viser tegnevinduet etter at det er tegnet en tegning med forskjellige farger og strekbredder.
Når vi bruker tastene på tastaturet, blir det generert hendelser
av type KeyEvent
. Disse kan en få fanget opp ved å definere
et lytteobjekt av type KeyListener
. Dette
interface
spesifiserer følgende tre metoder som da må
implementeres:
public void keyPressed( KeyEvent e ) { ... } public void keyReleased( KeyEvent e ) { ... } public void keyTyped( KeyEvent e ) { ... }
KeyListener
-objekter kan registreres (ved kall på
metoden addKeyListener
) for alle typer skjermkomponenter.
Det er imidlertid ikke så ofte vi har behov for å gjøre det.
Det finnes et eksempel i Deitel & Deitel, 9. utgave Fig. 14.36.
![]() |
![]() |
Start på kapittel om grafiske brukergrensesnitt |