Laboration 15 Grafiskt användargränssnitt Avsikten med denna laboration är att du ska träna på att skriva program som använder grafiskt användargränssnitt, dvs program som använder grafiska komponenter som t.ex. fönster och knappar. Uppgift 1 - FCKonverterare Du ska skriva ett program vilket låter användaren mata in ett antal grader låter användaren klicka på typ av konvertering, dvs. från Celsius till Fahrenheit eller från Fahrenheit till Celsius. Visar resultatet av konverteringen (rad två i figuren) Låter användaren avsluta programmet genom att klicka på Avsluta -knapp. Steg 1 Skapa projekt + klass Skapa en klass, FCKonverterare, i ett lämpligt projekt. Glöm inte extends JFrame. Se till att nödvändiga paket importeras (javax.swing, java.awt, java.awt.event) import javax.swing.*; import java.awt.*; import java.awt.event.*; public class FCKonverterare extends JFrame { public FCKonverterare() { Steg 2 Vilka komponenter behövs i programmet? Gör en lista över dem. Om vi tittar på figuren ovan så ser vi följande komponenter (uppifrån och ner): JLabel (Grader) + JTextField (Inmatningsfönstret) JLabel (Resultatutskrift) JButton (Fahrenheit ) + JButton (Celsius ) JButton (Avsluta) Skriv in dessa komponenter som attribut i klassen. private JButton ftoc = new JButton("Fahrenheit till Celsius"); private JButton ctof = new JButton("Celsius till Fahrenheit"); private JButton exit = new JButton("Avsluta"); private JLabel label = new JLabel("Grader "); private JLabel result = new JLabel("Resultat: "); private JTextField degrees = new JTextField(); DA129A, Programmering 1 1
Steg 3 Hur ska komponenterna placeras ut? Nedanstående skriver du i konstruktorn. Om vi tittar på figuren ovan så verkar den vara uppbyggd av 4 rader. Det innebär att en GridLayout med 4 rader och en kolumn verkar lämplig i huvudcontainern. Container c = getcontentpane(); c.setlayout(new GridLayout(4,1)); Den översta raden har två komponenter. Det innebär att vi behöver en panel att placera dem på. Den vänstra tar den storlek den behöver och den högra tar resten. Detta passar för en BorderLayout med JLabel i WEST och JTextField i CENTER. JPanel firstrow = new JPanel(new BorderLayout()); Rad 3 består av två knappar vilka ska ta lika stor plats båda två. Vi behöver en panel med GridLayout med två kolumner att placera knapparna på. JPanel thirdrow = new JPanel(new GridLayout(1,2)); Nu är det dags att placera komponenterna. firstrow.add(label,borderlayout.west); firstrow.add(degrees,borderlayout.center); thirdrow.add(ftoc); thirdrow.add(ctof); c.add(firstrow); c.add(result); c.add(thirdrow); c.add(exit); Steg 4 Fönstrets ska ha en storlek och fönstret ska vara synligt. setsize(350,130); setvisible(true); Steg 5 Testkör designen. Är du nöjd eller ska du göra förändringar? Nu kan du testa med andra inställningar. T.ex. annan fönsterstorlek, bakgrundsfärg/förgrundsfärg på olika komponenter osv. public static void main(string[] args) { FCKonverterare fck = new FCKonverterare(); Händelsehantering. Användaren ska ju kunna använda programmet, något ska hända vid klick på någon av knapparna, så nu är det dags att implementera händelsehantering. Först gör vi en lista på olika händelser som användaren ska kunna generera. Följande händelser ska användaren kunna generera: Klick på Celsius till Fahrenheit -knappen varvid graderna i inmatningsfönstret ska omvandlas till fahrenheitgrader. Klick på Fahrenheit till Celsius -knappen varvid graderna i inmatningsfönstret ska omvandlas till celsiusgrader. Klick på Avsluta-knappen varvid programmet ska avslutas. 2
Samtliga händelser fångas bäst med en ActionListener. Följande ska du göra: Låta klassen implementera ActionListener public class FCKonverterare extends JFrame implements ActionListener { Skriva metoden actionperformed i klassen public void actionperformed(actionevent e) { Koppla knapparna så att actionperformed anropas vid klick på dem. Skriv nedanstående rader i konstruktorn. ctof.addactionlistener(this); ftoc.addactionlistener(this); exit.addactionlistener(this); Nu är ramverket klart och vi kan skriva in den kod som ska exekveras då användaren klickar på en knapp. Celsius till Fahrenheit -knappen Innehållet i inmatningsfönstret ska hämtas genom anrop till metoden gettext(). Metoden returnerar en sträng (String). Denna sträng måste konverteras till ett decimaltal. Metoden Double.parseDouble(String) kan du använda för konverteringen. Metoden returnerar en double. Celsiusgraderna ska räknas om till fahrenheitgrader. Formeln F = 32 + 1.8C är användbar till detta. Slutligen ska innehållet i resultat-lablen ändras. Detta görs genom anrop till metoden settext(string). public void actionperformed(actionevent e) { double deg, res; if(e.getsource()==ctof) { // Klick på ctof? res = 32+1.8*deg; result.settext("resultat: "+deg+" C är "+res+" F"); Fahrenheit till Celsius -knappen Samma steg som ovan ska tas. Men formeln för beräkning av celsiusgrader är annorlunda: C = (F 32)/1.8. public void actionperformed(actionevent e) { double deg, res; if(e.getsource()==ctof) { res = 32+1.8*deg; result.settext("resultat: "+deg+" C är "+res+" F"); else if(e.getsource()==ftoc) { // klick på ftoc? res = (deg-32)/1.8; result.settext("resultat: "+deg+" F är "+res+" C"); 3
Avsluta -knappen Programmet ska avslutas när användaren klickar på denna knapp. public void actionperformed(actionevent e) { double deg, res; if(e.getsource()==ctof) { res = 32+1.8*deg; result.settext("resultat: "+deg+" C är "+res+" F"); else if(e.getsource()==ftoc) { res = (deg-32)/1.8; result.settext("resultat: "+deg+" F är "+res+" C"); else if(e.getsource()==exit) { // klick på exit-knapp System.exit(0); Nu är eventuellt programmet färdigt. Testkör det och experimentera gärna med olika ändringar. 4
Uppgift 2 FCKonverterare I denna uppgift ska du göra om applikationen i Uppgift 1 till en applet. Detta är ganska enkelt men man får tänka sig för. Följande ska du göra: Gå ut i Utforskaren och skapa en kopia av FCKonverterare.java. Döp kopian till FCApplet.java. Flytta kopian till src-katalogen (den är förmodligen i katalogen laboration15). Om du använder JBuilder ska du uppdatera projektet. Nu dyker FCApplet.java upp i projektfönstret. Du ska göra en del förändringar i innehållet i FCApplet.java. Ta bort package laboration15; Ändra så att klassen heter FCApplet och ärver JApplet. public class FCApplet extends JApplet implements ActionListener { Ta bort attributet exit. En applet kan inte avslutas användaren byter sida. private JButton exit = new JButton("Avsluta"); Ändra konstruktorhuvudet (public FCKonverterare()) till public void init() { Ta bort raderna exit.addactionlistener(this); // exit används inte mer. c.add(exit); // exit används inte mer. setsize(350,130); // appleten får sin storlek via html-sidan setvisible(true); // browsern ser till att appleten blir synlig Ändra huvudcontainerns GridLayout till 3 rader (ingen Avsluta -knapp) c.setlayout(new GridLayout(3,1)); Ta bort exit-koden ur actionperformed else if(e.getsource()==exit) { System.exit(0); Nu är det dags att testköra appleten. Börja med att skapa en fil med namnet FCApplet.html i src-katalogen. * netbeans: Högerklicka Source Packages och välj New HTML File. Skriv namnet FCApplet och klicka på Finish. * JBuilder: Välj File New File, skriv in ett lämpligt namn (t.ex. FCApplet), välj typen html och se till att src-katalogen i ditt projekt är vald (automatiskt). Klicka på OK. Skriv in nedanstående APPLET-tagg i FCApplet.html: <APPLET CODE="FCApplet.class" WIDTH="350" HEIGHT="100"> </APPLET> Testkör appleten genom att i * netbeans: Högerklicka FCApplet.java och välj Run File * JBuilder: Högerklicka FCApplet.html och välj Run using defaults Om allt ser ut som det ska och fungerar korrekt är det dags med Uppgift 3. 5
Uppgift 3 Appleten körs via browsern För att testköra appleten från en webserver så kan du utnyttja katalogen public_html här på MAH. Du har katalogen på enhet M. Nu ska du placera filerna FCApplet.class (finns i classes) och FCApplet.html (finns i src) i katalogen public_html som finns på enhet M. Enhet M om du jobbar hemifrån Om du jobbar med laborationen utanför Malmö högskola så når du ditt utrymme med hjälp av ftp, t.ex. via Internet Explorer (skriv i adressfältet): ftp://ftphome.mah.se Nu får du logga in med ditt användarnamn och lösenord. Kopiera filerna FCApplet.class och FCApplet.html från din dator till public_html. Testa appleten Testa din applet med att hämta hem dokumentet FCApplet.html via din browser. Du når dokumentet på adressen homeweb.mah.se/~xxxxxx/fcapplet.html där xxxxxx står för din nätverksidentitet. (användarnamn) Du får gärna placera filerna i en underkatalog, t.ex. lab7. I detta fall når du html-dokumentet med homeweb.mah.se/~xxxxxx/lab7/fcapplet.html Dags att testa om appleten startar! Men du måste använda en browser med Java2-plug. Detta är vanligt i dessa dagar. 6
Uppgift 4 Mushändelser Man kan fånga musklick på diverse komponenter, och även fånga händelsen att musmarkören förs in över en komponent eller förs ut från en komponent. Detta ska exemplifieras i denna uppgift. Slutresultatet kommer att likna figuren till höger. Om man klickar på en rektangel med en siffra så kommer denna siffra att läggas till i den översta raden. Om man klickar på <- så tas den sista siffran bort från raden (om det finns några siffror). Om man klickar på Del så tas alla siffror bort från raden. Om man för musmarkören över någon av komponenterna så kommer innehållet att visas på statusraden. Och när man tar bort musmarkören från komponenten så töms statusraden. Programmet består av 12 st JLabel med röd bakgrund. De har dessutom försetts med en border. Överst och underst är det ytterligare två JLabel, en med svart bakgrund, vit text och grå border och en med fönstrets bakgrundfärg och svart text (default-inställningar). Ett skal till programmet import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; // Behövs för knapp -border public class MouseEventsEx extends JPanel implements MouseListener { När programmet är färdigt kan du starta det med main-metoden: public static void main( String[] args ) { JFrame frame = new JFrame(); MouseEventsEx panel = new MouseEventsEx(); frame.setsize( 300, 170 ); frame.setdefaultcloseoperation( JFrame.EXIT_ON_CLOSE ); frame.getcontentpane().add( panel ); frame.setvisible( true ); Instansvariabler Alla komponenter som man ska referera till måste vara instansvariabler. Detta gäller inte komponenter som genererar händelser om de endast refereras i samband med händelserna. Endast den översta och den understa JLabel-komponenten behöver vara instansvariabler. Varför knapp -lablarna inte behöver vara instansvariabler ser du i händelsemetoderna. Lägg till instansvariablerna result och statusbar. private JLabel result = new JLabel( " " ); private JLabel statusbar = new JLabel( "No event" ); 7
Konstruktorn I konstruktorn designar vi panelen. En extra panel, med GridLayout (rader och 6 kolumner), används för att hålla knapp -lablarna.. Extra-panelen placeras i CENTER, result-labeln i NORTH och statusbar i SOUTH. public MouseEventsEx () { // Lokal variabler statusbar JLabel label; // Fält med knapparnas etiketter. Endast för att förenkla designen. String[] labels = {"0","1","2","3","4","<-","5","6","7","8","9","Del"; // Panel för de tolv "knapparna". 2 rader med 6 kolumner i vardera JPanel labelpanel = new JPanel( new GridLayout( 2, 6 ) ); Font font = new Font( "SansSerif", Font.BOLD, 16 ); // Design-del // De tolv JLabel-komponenterna skapas, designas, kopplas och placeras. // Detta sker en i taget. for (int i = 0; i < labels.length; i++) { label = new JLabel( labels[i], JLabel.CENTER ); // skapa JLabel label.addmouselistener( this ); // muslyssnare i denna klass label.setbackground( Color.red ); // röd bakgrundgärg label.setopaque( true ); // syns endast om JLabel göres tät label.setfont( font ); // större font än default-fonten // Varje knapp ska ha en border label.setborder(borderfactory.createbevelborder(bevelborder.raised)); labelpanel.add( label ); // Placera knappen på panelen result.setfont( font ); result.setforeground( Color.white ); // Textfärg result.setbackground( Color.black ); // Bakgrundsfärg result result.setopaque( true ); // Bakgrundsfärgen ska synas result.setborder(borderfactory.createlineborder(color.lightgray, 3)); result.setpreferredsize(new Dimension(200,30)); // Avmarkera och se! // Komponenter placeras i huvudpanelen (den ärvda panelen) setlayout( new BorderLayout() ); // Borderlayout är lämplig add( result, BorderLayout.NORTH ); add( labelpanel, BorderLayout.CENTER ); add( statusbar, BorderLayout.SOUTH ); Händelsehantering Nu återstår händelsehantering, dvs reagera på musklick och musrörelser in över och ut från komponenter. När en klass implementerar MouseListener så måste fem metoder finnas i klassen nämligen: public void mouseclicked(mouseevent e) { public void mousepressed(mouseevent e) { public void mousereleased(mouseevent e) { public void mouseentered(mouseevent e) { public void mouseexited(mouseevent e) { I programmet är det mousepressed, mousereleased, mouseentered och mouseexited som innehåller kod. 8
mousepressed mousepressed används när du ska registrera musklick på en komponent. Man lockas att använda mouseclicked men det ska man undvika. Funktionaliteten försämras nämligen avsevärt om du använder mouseclicked. public void mousepressed(mouseevent e) { String button = label.gettext(); // Text i klickad komponent String content = result.gettext(); // Innehåll i result-label statusbar.settext( "mousepressed on " + label.gettext() ); if(button.equals("del")) { // Del, Töm result-label på innehåll result.settext(""); // <-, ta bort ett tecken om det finns något tecken else if(button.equals("<-") && button.length()>0) { result.settext(content.substring(0,content.length()-1)); else { // siffra lägg till i result result.settext(content + label.gettext()); Kommentar till mousepressed-metoden: När en händelse genereras så anropas en händelse-hanterare. Du kan alltid erhålla en referens till komponenten som genererade händelsen (här: labeln som klickades) genom e.getsource(). Dock måste du typkonvertera komponenten till korrekt typ: Sedan kan referensen användas på vanligt sätt. mouseentered mouseentered anropas om musmarkören förs in över en komponent. public void mouseentered(mouseevent e) { statusbar.settext( "mouseentered on " + label.gettext() ); mouseexited mouseexited anropas om musmarkören förs bort från en komponent. public void mouseexited(mouseevent e) { statusbar.settext( "mouseexited from " + label.gettext() ); mousereleased mousereleased anropas när ett klick på en komponent avslutas (musknappen släpps upp) public void mousereleased( MouseEvent e ) { statusbar.settext( "mousereleased on " + label.gettext() ); mouseclicked mouseclicked anropas när användaren klickar på en component. Men användaren får inte flytta på musen medan klicket sker. Detta gör metoden svår att använda. public void mouseclicked(mouseevent e) { 9
Uppgift 1 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class FCKonverterare extends JFrame implements ActionListener { private JButton ftoc = new JButton("Fahrenheit till Celsius"); private JButton ctof = new JButton("Celsius till Fahrenheit"); private JButton exit = new JButton("Avsluta"); private JLabel label = new JLabel("Grader "); private JLabel result = new JLabel("Resultat: "); private JTextField degrees = new JTextField(); public FCKonverterare() { Container c = getcontentpane(); JPanel firstrow = new JPanel(new BorderLayout()); JPanel thirdrow = new JPanel(new GridLayout(1,2)); ctof.addactionlistener(this); ftoc.addactionlistener(this); exit.addactionlistener(this); c.setlayout(new GridLayout(4,1)); firstrow.add(label,borderlayout.west); firstrow.add(degrees,borderlayout.center); thirdrow.add(ftoc); thirdrow.add(ctof); c.add(firstrow); c.add(result); c.add(thirdrow); c.add(exit); setsize(350,130); setvisible(true); public void actionperformed(actionevent e) { double deg, res; if(e.getsource()==ctof) { res = 32+1.8*deg; result.settext("resultat: "+deg+" C är "+res+" F"); else if(e.getsource()==ftoc) { res = (deg-32)/1.8; result.settext("resultat: "+deg+" F är "+res+" C"); else if(e.getsource()==exit) { System.exit(0); public static void main(string[] args) { FCKonverterare fck = new FCKonverterare(); 10
Uppgift 2 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class FCApplet extends JApplet implements ActionListener { private JButton ftoc = new JButton("Fahrenheit till Celsius"); private JButton ctof = new JButton("Celsius till Fahrenheit"); private JLabel label = new JLabel("Grader "); private JLabel result = new JLabel("Resultat: "); private JTextField degrees = new JTextField(); public void init() { Container c = getcontentpane(); JPanel firstrow = new JPanel(new BorderLayout()); JPanel thirdrow = new JPanel(new GridLayout(1,2)); ctof.addactionlistener(this); ftoc.addactionlistener(this); c.setlayout(new GridLayout(3,1)); firstrow.add(label,borderlayout.west); firstrow.add(degrees,borderlayout.center); thirdrow.add(ftoc); thirdrow.add(ctof); c.add(firstrow); c.add(result); c.add(thirdrow); public void actionperformed(actionevent e) { double deg, res; if(e.getsource()==ctof) { res = 32+1.8*deg; result.settext("resultat: "+deg+" C är "+res+" F"); else if(e.getsource()==ftoc) { res = (deg-32)/1.8; result.settext("resultat: "+deg+" F är "+res+" C"); 11
Uppgift 4 import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; public class MouseEventsEx extends JPanel implements MouseListener { private JLabel result = new JLabel( " " ); private JLabel statusbar = new JLabel( "No event" ); public MouseEventsEx() { JLabel label; String[] labels = { "0","1","2","3","4", "<-","5","6","7","8","9","Del" ; JPanel labelpanel = new JPanel( new GridLayout(2, 6) ); Font font = new Font( "SansSerif", Font.BOLD, 16 ); for (int i = 0; i < labels.length; i++) { label = new JLabel( labels[i], JLabel.CENTER ); label.addmouselistener( this ); label.setbackground( Color.red ); label.setopaque( true ); label.setfont( font ); label.setborder( BorderFactory.createBevelBorder( BevelBorder.RAISED ) ); labelpanel.add( label ); result.setfont( font ); result.setforeground( Color.white ); result.setbackground( Color.black ); result.setopaque( true ); result.setborder( BorderFactory.createLineBorder( Color.lightGray, 3 ) ); result.setpreferredsize( new Dimension( 200,30 ) ); setlayout( new BorderLayout() ); add( result, BorderLayout.NORTH ); add( labelpanel, BorderLayout.CENTER ); add( statusbar, BorderLayout.SOUTH ); public void mousepressed( MouseEvent e ) { String button = label.gettext(); String content = result.gettext(); statusbar.settext( "mousepressed on " + label.gettext() ); if( button.equals( "Del" ) ) { result.settext( "" ); else if( button.equals( "<-" ) && button.length()>0 ) { result.settext( content.substring( 0, content.length()-1 ) ); else { result.settext( content + label.gettext() ); public void mouseentered( MouseEvent e ) { statusbar.settext( "mouseentered on " + label.gettext() ); 12
public void mouseexited( MouseEvent e ) { statusbar.settext( "mouseexited from " + label.gettext() ); public void mousereleased( MouseEvent e ) { statusbar.settext( "mousereleased on " + label.gettext() ); public void mouseclicked( MouseEvent e ) { public class StartUppgift5 { public static void main( String[] args ) { JFrame frame = new JFrame(); MouseEventsEx panel = new MouseEventsEx(); frame.setsize( 300, 170 ); frame.setdefaultcloseoperation( JFrame.EXIT_ON_CLOSE ); frame.getcontentpane().add( panel ); frame.setvisible( true ); 13