12 Layout, panel- och canvasobjekt Några Component-metoder Paneler (Panel) FlowLayout - BorderLayout – GridLayout Canvas (Canvas) Kap 12: Sid 2 Detta kapitel handlar om appletens utseende. Hur förändrar man storlek och placering på en komponent? Hur kan man förändra layouten hos en applet? (Det finns flera att välja på….) I detta sammanhang kommer du också att stifta bekantskap med klassen Panel. Om du inte är intresserad av appletens utseende utan endast av dess funktion kan du hoppa över det här kapitlet så länge. Först i kapitel 15 förutsätts att du läst avsnitten om layouter och Panel I slutet av detta kapitel finns några övningar med klassen Canvas. Detta är en komponent på vilken man kan rita och som är muskänslig. Från och med Java 1.1 miste Canvas något av sin betydelse eftersom rit- och muskänslighetsegenskaperna numera även finns hos andra komponentert.ex. en Panel. Om du inte är speciellt nyfiken på Canvas så kan du hoppa över avsnittet helt. 875102815 © Ove Lundgren 2 Kap 12: Sid 3 Några Component-metoder Klassen Component har många metoder, vilka förstås ärvs av subklasserna (Button, Textfield o s v) Det finns bl a metoder för att ge komponenter en viss storlek (size) och placering (location): knapp.setSize(50,30) ger knappen knapp bredden 50 och höjden 30 pixels ledtext.setLocation(10,20) placerar labeln ledtext med sitt övre vänstra hörn i position 10 , 20 ( 10 pixel från appletens vänsterkant och 20 pixel från överkanten) Övning: Storlek och placering Kör först denna applet: import java.awt.*; import java.applet.*; import java.awt.event.*; public class KompTest extends Applet { Label ledtext; TextField inruta; Button knapp; public void init() { ledtext = new Label("En ledtext"); ledtext.setBackground(Color.yellow); inruta = new TextField(10); knapp = new Button("Tryck här!"); add(ledtext);add(inruta); add(knapp); } } setSize() och setLocation() ska anropas efter att appleten initierats och startats. Gör anropen i t ex paint()-metoden. Lägg alltså till följande och provkör: public void paint(Graphics g) { ledtext.setSize(70,30); inruta.setSize(100,30); knapp.setSize(100,50); ledtext.setLocation(10,10); inruta.setLocation(90,10); knapp.setLocation(90,70); } Metoder som börjar på ”set” sätter värden för en komponent. Ibland behöver man i stället ta reda på aktuella värden. Metoder som börjar på ” get” returnerar aktuella värden. Exempel: knapp.getLocation() ger aktuell position för knapp som ett Point-objekt. Point-objekt har i sin tur två heltalsvariabler, x och y så knapp.getLocation().x ger aktuellt x-värde knapp.getLocation().y ger aktuellt y-värde Anmärkning: Om det returnerade värdet är boolskt börjar metodnamnet med ”is” och inte med ”get” 875102815 © Ove Lundgren 3 Kap 12: Sid 4 Övning: Visa och göm komponenter Metoden setVisible(b) gör att en komponent är synlig eller osynlig. Om den boolska variabeln b är true visas komponenten, annars inte. Metoden isVisible() returnerar värdet true om komponenten är synlig, annars false I ovanstående applet: Implementera gränssnittet ActionListener och sätt en lyssnare på knappen knapp. Lägg till metoden actionPerformed() : public void actionPerformed(ActionEvent e) { inruta.setVisible( ! inruta.isVisible() ); } Om inruta är synlig så har metoden inruta.isVisible() värdet true Då har uttrycket ! inruta.isVisible() värdet false – det vill säga inruta görs osynlig. Om inruta är osynlig så har metoden inruta.isVisible() värdet false Då har uttrycket ! inruta.isVisible() värdet true – det vill säga inruta görs synlig. Testa! Övning: Aktivera och avaktivera knappar Lägg till ytterligare en knapp, knapp2, i ovanstående applet. Den ska visas som inaktiv, ”utgråad” knapp: Efter att den instansierats skriv in satsen knapp2.setEnabled(false); I actionPerformed-metoden: Skriv in satsen knapp2.setEnabled(true); Knappen knapp2 blir aktiv efter att du tryckt på knappen knapp Övning: API-dokumentationen Öppna API-dokumentationen i webbläsaren och slå upp klassen Component Leta upp och läs om metoderna setLocation(), setSize()och setVisible() Leta upp och läs om metoderna getLocation(), getSize()och isVisible() Studera även övriga metoder i klassen…. Översikt över klasserna som ärver från Component: Object Event Label Button TextArea Component TextComponent TextField CheckboxGroup Container Checkbox List Choice Scrollbar Canvas Panel Applet 875102815 © Ove Lundgren 4 Kap 12: Sid 5 Paneler (Panel) Se översikten över klasser som ärver från Component ! Vi har gått igenom alla klasser - utom två: Container och Canvas. Vi studerar först Container, och framför allt dess subklass Panel Ett objekt av klassen Container kan få andra objekt "klistrade" på sig , eller "adderade" till sig, med add()-metoden. Klassen Panel ärver från Container, så ett Panel-objekt (en panel) har dessa "Container-egenskaper" Klassen Applet ärver från Panel, så ett Applet-objekt (en applet) har dessa "Container-egenskaper". Vi är ju vid det här laget väl bekanta med satser som t ex this.add(textruta); Övning: Paneler Låt oss experimentera med paneler. Skriv koden. Spara som Paneltest.java import java.awt.*; import java.applet.Applet; public class Paneltest extends Applet { Panel p1, p2; Label l1, l2, l3; public void init() { p1 = new Panel(); // panelobjektet p1 instansieras p2 = new Panel(); // panelobjektet p2 instansieras l1 = new Label("Detta är l1"); l2 = new Label("Detta är l2"); l3 = new Label("Detta är l3"); p1.setBackground(Color.red); // färg på p1 p2.setBackground(Color.blue); // färg på p2 this.setBackground(Color.yellow); p1.add(l1); p2.add(l2); // färg på appleten // här adderas label l1 till panel p1 // här adderas label l2 till panel p2 this.add(p1); // här adderas panel p1 till appleten this.add(p2); // här adderas panel p2 till appleten this.add(l3); // här adderas label l3 "direkt" till appleten } } Koden är lätt att förstå, förhoppningsvis… Så här blir det när vi exekverar: (fortsättning…) 875102815 © Ove Lundgren 5 Kap 12: Sid 6 Ändra nu raden p2.add(l2); // här adderas label l2 till panel p2 så att den lyder: p1.add(l2); // här adderas label l2 till samma panel, panel p2 Kompilera - provkör. Nu blir resultatet: Nu ligger båda lablarna i panel p1 medan panel p2 är tom. Panelerna ändrar storlek efter antal komponenter som adderats och hur stora dessa är… Övning: Krysstest - återigen I kapitel 9 gjorde du övningen Krysstest. Ta fram Krysstest.java Vi lade in en label, ett textfält och en knapp i en applet Vidare två kryssrutor (som bestämde textens stil) Därefter tre radioknappar (som bestämde typsnitt) Eventuellt har du med ytterligare tre radioknappar (som bestämde textens storlek) Sedan lade vi till en alternativlista som bestämde textens färg och, slutligen, (eventuellt) ytterligare en alternativlista som bestämde appletens bakgrundsfärg I Krysstest.java: Deklarera fyra (eller kanske fem) paneler och instansiera dem. Sätt olika färger på panelerna. Addera komponenter som passar ihop till samma panel Addera panelerna till appleten Så här kanske det blir: Paneler används (bland annat) just på detta sätt, att visuellt ”hålla samman” sammanhörande komponenter. I början på det här kapitlet använde vi metoden setLocation(x,y) för att placera en komponent på en speciell position. I allmänhet vill man undvika detta sätt att ”löda fast” komponenter på appleten. Man eftersträvar i stället en flexibel layout som är anpassningsbar för alla miljöer (alla plattformar). Nästa avsnitt behandlar hur man kan arbeta med olika flexibla layouter i Java. I detta sammanhang har paneler en viktig roll. 875102815 © Ove Lundgren 6 Kap 12: Sid 7 FlowLayout - BorderLayout - GridLayout Vi ska lära oss hur man kan påverka komponenters placering i en applet. I appleten Layouttest (nästa sida) finns fem paneler. På varje panel ”klistras” ett textfält och en knapp fast på appleten. Studera koden. Vi ska utgå från denna kod i några övningar. Genom att förändra koden på olika sätt ska vi studera Javas layouter. Övning: FlowLayout a) Kopiera koden på nästa sida. Spara den som Layouttest.java Skapa en html-fil, Layouttest.html, som anropar Layouttest.class I utgångsläget är ingen layout vald (alla setLayout är ”bortkommenterade” med // ) Kompilera och provkör med Appletviewer. Dra i appletens kanter. Allt ser ut ”som vanligt”. Komponeneterna rättar sig efter appletens bredd och höjd. De är centrerade. b) Ta bort // framför satsen this.setLayout(new FlowLayout( 1,5,5) ); Vi väljer på detta sätt en layout av typen FlowLayout för vår applet... Kompilera – provkör. Ingen förändring jämfört med förra exekveringen! FlowLayout( 1,5,5) är nämligen default- (standard-) layouten i applets. Konstruktorn har tre parametrar som synes: Den första bestämmer justering: 0 = vänster, 1 = center, 2 = höger Den andra: Avstånd horisontellt i pixels mellan komponenterna Den tredje: Avstånd vertikalt i pixels mellan komponenterna c) Ändra till this.setLayout(new FlowLayout( 0,15,15) ); Kompilera – provkör. (Vänsterjustering – större avstånd) d) Ändra till this.setLayout(new FlowLayout( 2,0,0) ); Kompilera – provkör. (Högerjustering – inga avstånd) 875102815 © Ove Lundgren 7 Kap 12: Sid 8 //Övning Layouttest import java.awt.*; import java.applet.*; public class Layouttest extends Applet { Panel p1, p2, p3, p4, p5; TextField tf1, tf2, tf3, tf4, tf5; Button butt1, butt2, butt3, butt4, butt5; public void init() { // -- Här är layouterna som vi ska experimentera med: --------------//this.setLayout(new FlowLayout( 1,5,5) ); //this.setLayout(new BorderLayout(10,10) ); //this.setLayout(new GridLayout( 2,3,10,10) ); // -tf1 = tf2 = tf3 = tf4 = tf5 = Textfälten instansieras. Vi ger dem vit bakgrundsfärg: ------new TextField("Ett"); tf1.setBackground(Color.white); new TextField("Två"); tf2.setBackground(Color.white); new TextField("Tre"); tf3.setBackground(Color.white); new TextField("Fyra"); tf4.setBackground(Color.white); new TextField("Fem"); tf5.setBackground(Color.white); // -butt1 butt3 butt5 Knapparna instansieras: -------------------------------------= new Button("1"); butt2 = new Button("2"); = new Button("3"); butt4 = new Button("4"); = new Button("5"); // p1 p3 p5 -- Panelerna instansieras: -------------------------------------= new Panel(); p2 = new Panel(); = new Panel(); p4 = new Panel(); = new Panel(); // -- Panelerna ges olika bakgrundsfärger: ------------------------p1.setBackground(Color.red); p2.setBackground(Color.orange); p3.setBackground(Color.yellow); p4.setBackground(Color.green); p5.setBackground(Color.blue); // -- Vi adderar ett textfält och en knapp till varje panel: ------p1.add(tf1); p1.add(butt1); p2.add(tf2); p2.add(butt2); p3.add(tf3); p3.add(butt3); p4.add(tf4); p4.add(butt4); p5.add(tf5); p5.add(butt5); } // -- Här adderas panelerna till appleten: ------------------------this.add("Center",p1); // +------------------------------------+ this.add("West",p2); // ! Argumenten ”Center”, ”West” osv ! this.add("North",p3); // ! har betydelse endast om man valt ! this.add("East",p4); // ! BorderLayout. Övriga layouter ! this.add("South",p5); // ! ignorerar dessa argument. ! // +------------------------------------+ // -- En utskrift med Paint-metoden: ---------------------------------public void paint(Graphics g) { g.setColor(Color.red); g.drawString("Java", 100,100); } } 875102815 © Ove Lundgren 8 Kap 12: Sid 9 Anmärkning: När man använder FlowLayout måste klassen FlowLayout vara importerad. Motsvarande gäller för BorderLayout och GridLayout (vilka vi strax ska studera.) Alla finns i paketet java.awt Övning: Borderlayout a) Ändra nu till: //this.setLayout( new FlowLayout( 1,5,5) ); this.setLayout( new BorderLayout(10,10) ); det vill säga vi väljer BorderLayout. Borderlayout skapar fem områden, så här: North West Center East South Observera att add()-metoden har två argument: this.add(”Center”,p1); this.add(”West”,p2); ... Argumenten ”Center”, ”West”, o s v fungerar endast i samband med Borderlayout (Om andra layouter används kan argumenten stå kvar där – de ignoreras helt ...) I vårt fall kommer panel p1 att hamna i område ”Center”, p2 i område ”West” o s v En komponent (en panel eller t ex en knapp) fyller alltid upp ett helt område. I vårt fall kommer panelen p1 att fylla upp hela ”Center”-området. ( Om det hade stått exempelvis this.add(”Center”,butt1); så hade knappen butt1 fyllt upp hela ”Center”-området. ) Med de två argumenten i BorderLayout(10,10) kan man specificera avstånd mellan områdena: North West Center East 10 pixels South South 10 pixels 875102815 © Ove Lundgren 9 Kap 12: Sid 10 Spara – kompilera – provkör. Så här blir det: b) Pröva att ändra avstånden mellan områdena. Testa t ex this.setLayout( new BorderLayout(20,20) ); och this.setLayout( new BorderLayout(0,0) ); Vad händer om man inte ”ockuperar” ett område med en panel? Testa: c) Ändra till: // this.add("Center",p1); this.add("West",p2); this.add("North",p3); // this.add("East",p4); this.add("South",p5); Så här blir det: Här syns texten Java som hittills oftast skymts av komponenter. 875102815 © Ove Lundgren 10 Kap 12: Sid 11 Övning: Gridlayout a) Ändra nu koden till //this.setLayout(new FlowLayout( 1,5,5) ); //this.setLayout(new BorderLayout(10,10) ); this.setLayout(new GridLayout( 2,3,10,10) ); Se till att alla add()-satser är ”aktiva”: this.add("Center",p1); this.add("West",p2); this.add("North",p3); this.add("East",p4); this.add("South",p5); Anmärkning: Orden ”Center”, ”West” ... o s v ska du egentligen ta bort nu ... MEN: Låt dom stå kvar så länge – de stör inte! Dessa argument ignoreras nämligen helt när vi inte använder BorderLayout. (Vi ska experimentera mer med Borderlayout strax, och då slipper du skriva in argumenten på nytt!) Spara – kompilera – kör! Så här blir det: GridLayout( 2,3,10,10) ger ett ”rutnät” med områden, en matris med 2 rader och 3 kolumner. Avståndet mellan områdena är 10 pixels såväl horisontellt som vertikalt. Här är den ordning i vilken add()-satserna skrivs i koden viktig! I vårt exempel står add(p1); först, sedan add(p2); och så vidare Områdena ”fylls på” i den ordning som figuren visar... Det går inte att ”hoppa över” ett område (som man ju kan i BorderLayout) b) Testa någon annan GridLayOut, t ex GridLayout(3,2,0,0) (som ger 3 rader, 2 kolumner och avståndet 0 pixels mellan områden) 875102815 © Ove Lundgren 11 Kap 12: Sid 12 Övning: Layout i paneler Vi har nu sett exempel på hur en applet tilldelas en viss layout med metoden setLayout() Metoden setLayout() ”härstammar” från klassen Container. Applet ärver från Panel som ärver från Container . Således: Även en panel ”äger” metoden setLayout() Vi kan alltså välja layouter för paneler! Vi testar: Ändra först i ovanstående kod så här: //this.setLayout(new FlowLayout( 1,5,5) ); this.setLayout(new BorderLayout(10,10) ); //this.setLayout(new GridLayout(2,3,10,10) ); Appleten ska alltså ha BorderLayout... I init()-metoden, förslagsvis direkt efter att panelen p1 instansierats – skriv satsen: p1.setLayout(new GridLayout(3,2,0,0) ); Panelen p1 får GridLayout med 3 rader och 2 kolumner Ändra så att alla knappar hamnar i panelen p1 (tillsammans med textfältet tf1 ): p1.add(butt1); p1.add(butt2); p1.add(butt3); p1.add(butt4); p1.add(butt5); p1.add(tf1); Låt övriga textfält klistras in i var sin panel: p2.add(tf2); p3.add(tf3); p4.add(tf4); p5.add(tf5); Addera panelerna till appleten (samma som förut): this.add("Center",p1); this.add("West",p2); this.add("North",p3); this.add("East",p4); this.add("South",p5); Spara – kompilera – kör. Så här blir det: 875102815 © Ove Lundgren 12 Kap 12: Sid 13 Ett tips: I satserna p1.add(butt1); p1.add(butt2); p1.add(butt3); ... och så vidare... klistras komponenter direkt in i GridLayout-panelen p1. Som tidigare nämnts så kommer komponenterna att ”stretchas ut” så att var och en av dem fyller ut hela ”sitt” område. (I figuren ovan fyller varje knapp ut sitt område...) (Komponenter fyller ut sitt område på detta sätt i GridLayout och i BorderLayout) Om man vill behålla komponentens storlek (som den ser ut ”normalt” – vid FlowLayout alltså) så kan man först addera komponenten till en FlowLayout-panel och sedan klistra in denna panel i GridLayout-panelen. (FlowLayout är ju default, så om ingen annan layout är vald så blir det FlowLayout) Om du vill testa: - Lägg till ytterligare en panel, p6 (deklarera och instansiera den) - Addera knappen butt1 till p6 och addera p6 till GridLayout-paneln p1: p6.add(butt1); p1.add(p6); Nu blir det så här: 875102815 © Ove Lundgren 13 Kap 12: Sid 14 Övning: Scrolltest2 - igen Ta fram filen ScrollTest2.java Gör så att appleten får detta utseende: (kap 9) Lite hjälp: Appleten har BorderLayout I “North” läggs en panel med GridLayout(3 rader, 1 kolumn) I “South” läggs en panel med FlowLayout (standard) Övning: Krysstest – igen! Ta fram Krysstest.java Gör så att appleten ser ut så här: 875102815 © Ove Lundgren 14 Kap 12: Sid 15 Canvas (Canvas) En canvas är en komponent på vilken man kan rita ( paint-metoden) och som är muskänslig. I gamla Java 1.0 kunde inte en panel göras muskänslig. Man använde canvas-objekt i stället. I och med Java 1.1 har canvas mist något av sin betydelse. Vi tar ändå med några övningar: Övning: Testa canvas Vi vill ha en canvas (en yta) på vilken det ritas en oval och en fylld rektangel. Vi skapar en ny canvas-klass alltså en klass som ärver från klassen Canvas Skriv in koden. Spara som MinCanvas.java. Kompilera (MinCanvas.class skapas...) (Detta är alltså inget program, ingen applet, men en klass ur vilken vi kan skapa canvas-objekt!) import java.awt.*; class MinCanvas extends Canvas { MinCanvas() // konstruktorn { this.setBackground(Color.yellow); } public void paint(Graphics g) { g.setColor(Color.red); g.fillRect(70,50,40,40); g.drawOval(10,20,50,20); } } I konstruktorn ger vi canvasen gul bakgrundsfärg. Till en canvas hör ett Graphics-objekt, som ju har en rad metoder för att rita och skriva. Det grafiska objektet utgör parameter i paint()-metoden som körs automatiskt när canvasen visas. Det hela fungerar alltså som i en applet. Kontrollera i Utforskaren att du har filerna MinCanvas.java och MinCanvas.class Nu skriver vi en applet: Vi deklarerar och instansierar fyra stycken canvas-objekt, c1, c2,c3 och c4 ur vår egen klass MinCanvas. Vi väljer också en layout, en GridLayout med 2 rader och 2 kolumner. Skriv koden på nästa sida. Spara som CanvasTest.java (i samma mapp som föregående fil) Kompilera. Kör! 875102815 © Ove Lundgren 15 Kap 12: Sid 16 import java.awt.*; import java.applet.*; public class CanvasTest extends Applet { MinCanvas c1,c2,c3,c4; public void init() { this.setLayout(new GridLayout(2,2,10,10) ); c1 c2 c3 c4 = = = = new new new new MinCanvas(); MinCanvas(); MinCanvas(); MinCanvas(); this.add(c1); this.add(c2); this.add(c3); this.add(c4); } } Mushändelser En canvas tar förstås också emot mushändelser (på samma sätt som en applet kan ta emot mushändelser) Övning: Muskänslig canvas Vi vill att canvasen ovan ska vara känslig för mushändelserna mouseEntered och mouseExited: Canvasen ska bli blå när muspekaren går in över den, och gul när muspekaren lämnar ytan. I MinCanvas.java lägg till följande: import java.awt.event.*; class MinCanvas extends Canvas implements MouseListener addMouseListener(this); // skriv satsen i konstruktorn samt metoderna: public public public public public void void void void void mouseEntered(MouseEvent e){this.setBackground(Color.blue);} mouseExited(MouseEvent e){this.setBackground(Color.yellow);} mouseClicked( MouseEvent e){} mousePressed( MouseEvent e){} mouseReleased( MouseEvent e){} Spara och kompliera MinCanvas.java. Kör sedan CanvasTest igen 875102815 © Ove Lundgren 16 Kap 12: Sid 17 Övning: Ännu en konstruktor Gör ändringar och tillägg i MinCanvas: Deklarera två färger infarg och utfarg och låt dessa få värdena Color.blue respektive Color.yellow i standardkonstruktorn. Definiera ännu en konstruktor med Color-parametrarna in och ut. Då kan vi välja välja vilka färger som ska användas när canvasobjektet instansieras. I mouseEntered()- och mouseExited()-händelserna: Byt till bakgrundsfärgerna infarg resp. utfarg Så här alltså: import java.awt.*; import java.awt.event.*; class MinCanvas extends Canvas implements MouseListener { MinCanvas() { infarg = Color.blue; utfarg = Color.yellow; this.setBackground(utfarg); addMouseListener(this); } MinCanvas(Color in, Color ut) { infarg = in; utfarg = ut; this.setBackground(utfarg); addMouseListener(this); } Color infarg; Color utfarg; public void paint(Graphics g) { g.setColor(Color.red); g.fillRect(70,50,40,40); g.drawOval(10,20,50,20); } public public public public public void void void void void mouseEntered(MouseEvent e){this.setBackground(infarg);} mouseExited(MouseEvent e){this.setBackground(utfarg);} mouseClicked( MouseEvent e){} mousePressed( MouseEvent e){} mouseReleased( MouseEvent e){} } Spara och kompilera. I CanvasTest kan du nu instansiera canvasobjekten t ex så här: c1 = new MinCanvas(Color.green, Color.pink); Spara, kompilera och provkör! 875102815 © Ove Lundgren 17 Kap 12: Sid 18 Övning: En canvas som vi ritar på Skriv in (eller kopiera) klassen nedan. Du ser att den är påminner mycket om appleten Rita som ingick i en tidigare övning. import java.awt.*; import java.awt.event.*; public class RitCanvas extends Canvas implements MouseMotionListener { RitCanvas() // konstruktor! { this.addMouseMotionListener(this); this.setBackground(Color.yellow); } int x, y; boolean ritar = false; public void update(Graphics g) { paint(g) ; } public void paint(Graphics g) { if (ritar)g.fillOval(x, y,5,5); } public void mouseDragged(MouseEvent e) { x = e.getX(); y = e.getY(); ritar = true; repaint(); } public void mouseMoved(MouseEvent e){} } Spara filen som RitCanvas.java och kompilera den. Gör en applet (liknande CanvasTest.java) som deklarerar och instansierar fyra objekt av typ RitCanvas Spara och provkör! Appleten visar fyra gula ritytor. 875102815 © Ove Lundgren 18