Laboration 13 Avsikten med denna laboration är att du ska använda ett par nya grafiska komponenter. Dessutom ska du göra designen utan hjälp av en layout-manager (enklare). Börja med att skapa paketet laboration13. I filen laboration13.zip hittar du fullständiga lösningar. Grundläggande uppgifter Uppgift 13a Uppgiften går ut på att använda ett antal komponenter i ett program. De som används är: 2 st JLabel 1 st JList 1 st JComboBox 2 st JPanel 3 st JRadioButton 5 st JCheckBox 1 st JButton 1 st JTextArea. Dessutom ska du inte använda någon Layout Manager när du designar fönstret. 1. Skapa en grafisk klass Högerklicka paketet laboration13 och välj New JFrame Form Välj namnet Personuppgifter som namn på klassen. Högerklicka i fönstret och välj Set Layout Null layout. Klicka på Code i fönstret nere till vänster och ändra Form Size Policy till Generate Resize Code. 2. JList boende Boende-delen består av en JLabel (texten Boende) och en JList. Placera en JLabel och en JList i fönstret. Inställningar i JList-komponenten är följande (se till att JList-komponenten är vald. Det kan krävas två klick på komponenten): Variable Name lstboende font Arial model Klicka på knappen till höger och ersätt Item 1 med Lägenhet osv (Klicka på Item 1, ersätt Item 1 med Lägenhet, Klicka på Edit). selectionmode SINGLE_INTERVAL selectedindex 0 JLabel-komponenten kan du ändra på egen hand. DA129A, Programmering 1 1
3. JComboBox Yrke Yrke-delen består av en JLabel och en JComboBox. Placera en JLabel och en JComboBox i fönstret. Inställningar i JComboBox är följande: Variable Name font model cbyrke Arial 4. JPanel och JRadioButton Ålder Se model under JList. Valbara alternativ kan vara: Studerande, Offentligt anställd, Privat anställd, Pensionär. Om ett antal komponenter ska samlas med någon typ av ram runt dem kan man först placera in en JPanel och sedan placera komponenterna på panelen. Panelen ges en lämplig Border. Placera en JPanel i fönstret. Klicka på knappen till höger om Border i Properties-fönstret. Klicka på Titled Border och gör sedan följande inställningar: Title Ålder Color Välj en färg för kanten och texten Ändra layouten på samma sätt som för fönstret, dvs. högerklicka på panelen och välj Set Layout Null layout i popup-menyn. Nu ska du välja Button Group i paletten och sedan klicka på fönstret. Detta är en osynlig komponent som vi behöver. Placera 3 st JRadioButton-komponenter i panelen. Ändra följande egenskaper: Variable Name rbalder0_17 rbalder18_64 rbalder65 font Arial Arial Arial text 0 17 18 64 65 - buttongroup buttongroup1 buttongroup1 buttongroup1 selected - - JLabel-komponenten kan du ändra på egen hand. 5. JPanel och JRadioButton Hobby Placera en JPanel i fönstret. Gör samma inställningar som under punkt 4. Placera 5 st JCheckBox-komponenter i panelen. Andra följande egenskaper: Variable Name cbidrott cbfolkdans cbfagel cbbridge cbkorsang font Arial Arial Arial Arial Arial text Idrott Folkdans Fågelsång Bridge Körsång 6. JButton och JTextArea Sammanfattning Placera en JButton i fönstret. Du kan själv göra nödvändiga inställningar Placera en JTextArea i fönstret. Ändra komponentens Variable Name till tasammanfattning och ändra font till Arial. I JTextArea-komponenten ska informationen från de andra komponenterna sammanställas. Skapa en händelsehanterare till knappen (Events actionperformed). Skriv in följande kod i händelsehanteraren: String txt = "Boende: " + lstboende.getselectedvalue() + "\n" + "Yrke: " + cbyrke.getselecteditem() + "\n" + "Ålder: " + getålder() + "\n" + "Hobby: " + hobbylista(); tasammanfattning.settext( txt ); 2
Som du ser anropas två metoder i ovanstående kod, nämligen getålder() och hobbylista(). Metoden getålder ska returnera någon av strängarna 0 17, 18 64 och 65. Metoden hobbylista() ska returnera samtliga hobbies med ett blanktecken mellan dem. Nedan ser du min version av metoderna: private String hobbylista() { String res = ""; if( cbidrott.isselected() ) { res += " " + cbidrott.gettext(); if( cbfolkdans.isselected() ) { res += " " + cbfolkdans.gettext(); if( cbfagelskadning.isselected() ) { res += " " + cbfagelskadning.gettext(); if( cbbridge.isselected() ) { res += " " + cbbridge.gettext(); if( cbkorsang.isselected() ) { res += " " + cbkorsang.gettext(); return res; private String getålder() { if( rbalder0_17.isselected() ) { return "0-17"; else if( rbalder18_64.isselected() ) { return "18-65"; else { return "65 -"; Nu är det dags att testköra programmet (om du inte redan har gjort det). 3
Uppgift 13b Uppgiften handlar om att visa bilder. I ett fönster (det högra) väljer användaren bild som ska visas i en JList-komponent. I det vänstra fönstret visas den valda bilden. Uppgiften har stora likheter med föreläsningens exempel. Controller Ovanstående bild kan även beskrivas med hjälp av klassdiagram: ImageViewer +ImageViewer() +showimage( String ) +noimage() Controller - viewer : ImageViewer - chooser : ImageChooser +Controller( ImageViewer, ImageChooser) +newimage( String ) +eraseimage() ImageChooser - controller : Controller +ImageChooser() +setcontroller( Controller ) ImageViewer Börja med att skapa en fönsterklass som du döper till ImageViewer. Sätt layouten till Null layout Placera en JLabel-komponent som täcker nästan hela fönstret. Det är i denna komponent som bilder ska visas. Glöm inte att ge JLabel-komponenten ett bra namn, t.ex. lblimage. ImageViewer-klassen ska inte ha några instansvariabler. Klassen används endast för att visa bilder (genom anrop till någon av metoderna showimage( String ) eller noimage(). public void noimage() ska ta bort bilden som visas och i stället visa texten INGEN BILD VALD i JLabel-komponenten. lblimage.seticon( null ); // ta bort bild lblimage.settext( "INGEN BILD VALD" ); public void showimage( String ) tar emot sökvägen till en bildfil vilken ska visas i JLabel-komponenten. lblimage.settext( ); // ta bort text som visas lblimage.seticon( new ImageIcon( filename ) ); 4
Du kan testa ImageViewer-klassen genom att ändra main-metoden i klassen till: public static void main(string args[]) { java.awt.eventqueue.invokelater(new Runnable() { public void run() { String filename = "C:/bilder/filmlogga.jpg"; ImageViewer viewer = new ImageViewer(); viewer.setvisible( true ); viewer.showimage( filename ); // eller: viewer.noimage(); ); ImageChooser, del 1 Börja med att skapa en fönsterklass som du döper till ImageChooser. Sätt layouten till Null layout Placera en JListkomponent och en JButton-komponent i fönstret. Ge JListkomponenten ett bra namn och gör övriga nödvändiga ändringar i designen (se Uppgift 13a). ImageChooser-klassen har en instansvariabel, nämligen referens till ett Controller-objekt: private Controller controller; Eftersom du inte skrivit klassen Controller kommer det bli en röd markering till vänster om raden. Den försvinner snart! Klassen ska innehålla en set-metod till instansvariabeln controller: public void setcontroller( Controller incontroller ) { this.controller = incontroller; Även metoden drabbas tillfälligt av ett par rödmarkeringar. ImageChooser-klassen ska också ha lite händelse-hantering. Något ska ju hända när användaren klickar på knappen eller på en rad i listan. Vi väntar med händelse-hanteringen till ImageChooser, del 2. Controller Börja med att skapa en vanlig java-klass som du döper till Controller. Du ska lägga till två instansvariabler i klassen, en konstruktor och två metoder. Dessa beskrivs nedan. Instansvariabler Controller-klassen har två referenser. En referens till ett ImageViewer-objekt (instansvariabeln viewer) och en referens till ett ImageChooser-objekt (instansvariabeln chooser). private ImageViewer viewer; private ImageChooser chooser; Konstruktor Controller-objektet får referenser till instansvariabler när det skapas: ImageChooser chooser = new ImageChooser(); ImageViewer viewer = new ImageViewer(); Controller cont = new Controller( viewer, chooser ); 5 Controller - viewer : ImageViewer - chooser : ImageChooser +Controller( ImageViewer, ImageChooser) +newimage( String ) +eraseimage()
Dessa referenser ska lagras i instansvariablerna. Dessutom måste ImageChooser-objektet ha en referens till Controller-objektet. Slutligen ska ImageViewer-objektet och ImageChooserobjktet göras synliga: public Controller( ImageViewer inviewer, ImageChooser inchooser ) { this.viewer = inviewer; this.chooser = inchooser; chooser.setcontroller( this ); // this = referens till Controller-objektet viewer.setvisible( true ); chooser.setvisible( true ); Metoder Controllerklassen har två metoder: newimage( String ) vilken tar emot sökvägen till en bildfil (t.ex. C:/temp/sommar.jpg ). Metoden ska kontrollera att bildfilen är av korrekt typ (jpg, gif eller png). Om bildfilen är av korrekt typ så ska metoden anropa metoden showimage( String ) i ImageViewer-objektet. Om bildfilen är av otillåten typ så ska ett felmeddelande visas (figur till höger). public void newimage( String filename ) { int lastdot = filename.lastindexof( '.' ); String suffix = filename.substring( lastdot+1 ); suffix = suffix.tolowercase(); // alla tecken små bokstäver if( suffix.equals("jpg") suffix.equals("gif") suffix.equals("png") ) { viewer.showimage( filename ); else { JOptionPane.showMessageDialog( null, "Felaktig filtyp: " + suffix ); eraseimage() vilken ska anropa metoden noimage() i ImageViewer-objektet. public void eraseimage() { viewer.noimage(); Du kan testa Controller-klassen med följande main-metod. Endast en av de tre sista raderna ska vara aktiv vid exekvering (test av olika fall). public static void main(string[] args) { ImageChooser chooser = new ImageChooser(); ImageViewer viewer = new ImageViewer(); Controller cont = new Controller( viewer, chooser ); cont.newimage( "C:/bilder/filmlogga.jpg" ); // cont.newimage( "C:/bilder/karta.bmp" ); // ej tillåten filtyp // cont.eraseimage(); ImageChooser, del 2 Nu är det dags att införa händelsehantering i ImageChooser-klassen. Vid klick på knappen ska metoden eraseimage() anropas i Controller-klassen. controller.eraseimage(); Vid klick på rad i listan ska metoden newimage anropas i Controllerklassen. För att lyssna på klick i listan ska du använda händelsen valuechanged. Antalet case i switch-satsen beror på antalet rader i listan. Exemplet nedan förutsätter fem 6
rader i listan. Låt gärna en av bildfilerna vara av felaktig typ, t.ex. en bmp-fil. String filename = null; int index = lstimages.getselectedindex(); switch( index ) { case 0 : filename = "C:/bilder/london06.jpg"; break; case 1 : filename = "C:/bilder/filmlogga.jpg"; break; case 2 : filename = "C:/bilder/LugiP93DMPB2.jpg"; break; case 3 : filename = "C:/bilder/tandem1.jpg"; break; case 4 : filename = "C:/bilder/karta1.bmp"; break; controller.newimage( filename ); Nu är det dags att testköra programmet. När det fungerar ska du förvissa dig om att du förstår hur anropen går till: 1. Från klick på list-element till visning av bild / felmeddelande. 2. Från klick på knappen till visningen av texten INGEN BILD VALD. 3. Vid konstruktion hur klasserna får nödvändiga refrenser till varandra. 7
Fördjupande uppgifter Uppgift 13c Placera en JLabel och en JPanel i ett fönster. JLabel-komponenten ska visa musmarkörens position när den förs över JPanel-komponenten. I denna uppgift ska du lyssna på musrörelser över en komponent. Du ska lyssna på Jpanel-komponenten och du ska använda händelsen mousemoved. För att få reda på x-värde respektive y-värde anropar du metoden getx resp gety i MouseEvent-objektet (i händelsehanteraren), t.ex.: int x = evt.getx(); Sedan ser du till att x- respektive y-värdet visas i JLabel-komponenten. Förbättring 1: När musmarkören lämnar panelen ska texten tömmas. Lyssna på händelsen mouseexited. När den inträffar så ska du tömma texten. Förbättring 2: När musmarkören är över panelen ska den vara ett kors men annars den normala markören. Lyssna på händelserna mouseentered respektive mouseexited. Vid mouseentered så ändra markör med: setcursor( new java.awt.cursor(java.awt.cursor.crosshair_cursor) ); Vid mouseexited så ändra markör med: setcursor( new java.awt.cursor(java.awt.cursor.default_cursor) ); Uppgift 13d Placera en JButton i ett fönster. Uppmana användaren att klicka på knappen. Men du ska se till att kanppen flyttar sig så fort som användaren rör musmarkören över kanppen. Ledning: Knappen flyttas genom anrop till setlocation-metoden. Knappens nya position ska slumpas. Men se till att knappen inte hamnar utanför fönsterytan. Lyssna på knappen med mousemoved. 8
Extra uppgift Uppgift 12e Ej färdig 9
Lösningsförslag Uppgift 13a vissa delar av klassen package laboration13; public class Personuppgifter extends javax.swing.jframe { private String hobbylista() { String res = ""; if( cbidrott.isselected() ) { res += " " + cbidrott.gettext(); if( cbfolkdans.isselected() ) { res += " " + cbfolkdans.gettext(); if( cbfagelskadning.isselected() ) { res += " " + cbfagelskadning.gettext(); if( cbbridge.isselected() ) { res += " " + cbbridge.gettext(); if( cbkorsang.isselected() ) { res += " " + cbkorsang.gettext(); return res; private String getålder() { if( rbalder0_17.isselected() ) { return "0-17"; else if( rbalder18_64.isselected() ) { return "18-65"; else { return "65 -"; private void btnsammanfattaactionperformed(java.awt.event.actionevent evt) { String txt = "Boende: " + lstboende.getselectedvalue() + "\n" + "Yrke: " + cbyrke.getselecteditem() + "\n" + "Ålder: " + getålder() + "\n" + "Hobby: " + hobbylista(); tasammanfattning.settext( txt ); // Variables declaration - do not modify private javax.swing.jbutton btnsammanfatta; private javax.swing.buttongroup buttongroup1; private javax.swing.jcheckbox cbbridge; private javax.swing.jcheckbox cbfagelskadning; private javax.swing.jcheckbox cbfolkdans; private javax.swing.jcheckbox cbidrott; private javax.swing.jcheckbox cbkorsang; private javax.swing.jcombobox cbyrke; private javax.swing.jlabel jlabel1; private javax.swing.jlabel jlabel2; private javax.swing.jpanel jpanel1; private javax.swing.jpanel jpanel2; private javax.swing.jscrollpane jscrollpane1; private javax.swing.jscrollpane jscrollpane2; private javax.swing.jlist lstboende; private javax.swing.jradiobutton rbalder0_17; private javax.swing.jradiobutton rbalder18_64; private javax.swing.jradiobutton rbalder65; private javax.swing.jtextarea tasammanfattning; // End of variables declaration 10
Uppgift 13b delar av klasserna package laboration13; import javax.swing.*; public class ImageViewer extends javax.swing.jframe { public void showimage( String filename ) { this.settitle( filename ); lblimage.settext( "" ); lblimage.seticon( new ImageIcon( filename ) ); public void noimage() { lblimage.settext( "INGEN BILD VALD" ); lblimage.seticon( null ); public static void main(string args[]) { java.awt.eventqueue.invokelater(new Runnable() { public void run() { String filename = "C:/bilder/filmlogga.jpg"; ImageViewer viewer = new ImageViewer(); viewer.setvisible( true ); viewer.showimage( filename ); // viewer.noimage(); ); // Variables declaration - do not modify private javax.swing.jlabel lblimage; // End of variables declaration package laboration13; public class ImageChooser extends javax.swing.jframe { private Controller controller; public void setcontroller( Controller incontroller ) { this.controller = incontroller; private void lstimagesvaluechanged(javax.swing.event.listselectionevent evt) { String filename = null; int index = lstimages.getselectedindex(); switch( index ) { case 0 : filename = "C:/bilder/london06.jpg"; break; case 1 : filename = "C:/bilder/filmlogga.jpg"; break; case 2 : filename = "C:/bilder/LugiP93DMPB2.jpg"; break; case 3 : filename = "C:/bilder/tandem1.jpg"; break; case 4 : filename = "C:/bilder/karta1.bmp"; break; controller.newimage( filename ); private void btneraseactionperformed(java.awt.event.actionevent evt) { controller.eraseimage(); 11
// Variables declaration - do not modify private javax.swing.jbutton btnerase; private javax.swing.jscrollpane jscrollpane1; private javax.swing.jlist lstimages; // End of variables declaration package laboration13; import javax.swing.*; public class Controller { private ImageViewer viewer; private ImageChooser chooser; public Controller( ImageViewer inviewer, ImageChooser inchooser ) { this.viewer = inviewer; this.chooser = inchooser; chooser.setcontroller( this ); viewer.setvisible( true ); chooser.setvisible( true ); public void newimage( String filename ) { int lastdot = filename.lastindexof( '.' ); String suffix = filename.substring( lastdot+1 ); suffix = suffix.tolowercase(); // alla tecken små bokstäver if( suffix.equals( "jpg") suffix.equals( "gif" ) suffix.equals( "png" ) ) { viewer.showimage( filename ); else { JOptionPane.showMessageDialog( null, "Felaktig filtyp: " + suffix ); public void eraseimage() { viewer.noimage(); public static void main(string[] args) { ImageChooser chooser = new ImageChooser(); ImageViewer viewer = new ImageViewer(); Controller cont = new Controller( viewer, chooser ); cont.newimage( "C:/bilder/filmlogga.jpg" ); cont.newimage( "C:/bilder/karta.bmp" ); // ej tillåten filtyp cont.eraseimage(); 12
Uppgift 13c delar av klassen package laboration13; public class Uppgift13c extends javax.swing.jframe { private void pnlmovesmouseentered(java.awt.event.mouseevent evt) { setcursor( new java.awt.cursor(java.awt.cursor.crosshair_cursor) ); // Förbättring 2 private void pnlmovesmouseexited(java.awt.event.mouseevent evt) { lblkoordinater.settext( "" ); // Förbättring 1 setcursor( new java.awt.cursor(java.awt.cursor.default_cursor) ); // Förbättring 2 private void pnlmovesmousemoved(java.awt.event.mouseevent evt) { int x = evt.getx(); int y = evt.gety(); lblkoordinater.settext( String.format( "( %1d, %1d )", x, y ) ); // Variables declaration - do not modify private javax.swing.jlabel lblkoordinater; private javax.swing.jpanel pnlmoves; // End of variables declaration Uppgift 13d delar av klassen package laboration13; public class Uppgift13d extends javax.swing.jframe { private void btnmovemousemoved(java.awt.event.mouseevent evt) { // Slumpvärde: min 0, max fönsterbredd - 10 - knappbredd int x = (int)( Math.random() * 280 ); // Slumpvärde: min 0, max fönsterhöjd - 24-10 - knapphöjd int y = (int)( Math.random() * 250 ); btnmove.setlocation( x, y ); // Variables declaration - do not modify private javax.swing.jbutton btnmove; // End of variables declaration 13