Laboration Gui Avsikten med denna laboration är att du ska träna på att bygga grafiska användargränssnitt. Spara dina resultat i paketet laborationgui. Längst bak i laborationen finns fullständiga lösningar på uppgifterna. Uppgift 1 - Ärva JPanel I denna uppgift ska du bygga en komponent som består av två JLabel och två JTextField. Den ska se ut som figuren till höger när den är färdig. Det behövs en JPanel i vilken komponenterna kan placeras. Klassen kommer att ärva denna panel. Strukturen för klassen ska vara så här: public class NamnYrke extends JPanel { // Instansvariabler public NamnYrke() { : // designmetoder // get-metoder public String getnamn() { : public String getyrke() { : Instansvariabler De grafiska komponenterna ska vara instansvariabler i klassen: public class NamnYrke extends JPanel { private JTextField tfnamn = new JTextField(); private JTextField tfyrke = new JTextField(); private JLabel lblnamn = new JLabel("Namn"); private JLabel lblyrke = new JLabel("Yrke"); Konstruktorn Det kommer att behövas några extra paneler för att bygga designen, en till vänster som innehåller JLabel-komponenterna (labelpanel nedan) och en till höger som innehåller JTextField-komponenterna (inputpanel nedan). Båda panelerna kommer att skapas och designas med hjälp av metoder (getlabelpanel() resp. getinputpanel()). Layout-manager i den ärvda panelen ska vara BorderLayout och hjälppanelerna ska placeras i WESTspektive CENTER. För att ange bredden på panelen i WEST anropas metoden setpreferredsize. DA129A Programmering 1 1
public NamnYrke() { setlayout(new BorderLayout()); JPanel labelpanel = getlabelpanel(); JPanel inputpanel = getinputpanel(); labelpanel.setpreferredsize(new Dimension(50,0)); add(labelpanel, BorderLayout.WEST); add(inputpanel, BorderLayout.CENTER); Metoder som returnerar JPanel-objekt /********* Design-metoder **********/ private JPanel getlabelpanel() { JPanel panel = new JPanel(new GridLayout(2,1)); Font dialogb12 = new Font("Dialog", Font.BOLD, 12); lblnamn.setfont(dialogb12); lblyrke.setfont(dialogb12); panel.add(lblnamn); panel.add(lblyrke); return panel; private JPanel getinputpanel() { JPanel panel = new JPanel(new GridLayout(2,1)); panel.add(tfnamn); panel.add(tfyrke); return panel; Metoder för att avläsa inmatade värden - getters // get-metoder public String getnamn() { return tfnamn.gettext(); public String getyrke() { return tfyrke.gettext(); Ett fönster att placera NamnYrke-panelen i Till slut behövs det ett fönster för att testa NamnYrke-komponenten. Fönstret består dessutom av en JTextArea och en JButton. När användaren klickra på knappen så skrivs information från NamnYrke-panelen i JTextArea-komponenten. När användaren klickar på knappen så uppdateras innehållet i JTextArea-komponenten. import java.awt.event.*; public class TestNamnYrke extends JFrame implements ActionListener { private JButton update = new JButton("Uppdatera textytan"); private JTextArea textyta = new JTextArea(); private NamnYrke namnyrke = new NamnYrke(); 2
public TestNamnYrke() { Container c = getcontentpane(); setsize(400,200); setdefaultcloseoperation(jframe.exit_on_close); textyta.seteditable(false); update.addactionlistener(this); c.add(namnyrke, BorderLayout.NORTH); c.add(textyta, BorderLayout.CENTER); c.add(update, BorderLayout.SOUTH); setvisible(true); public void actionperformed(actionevent e) { textyta.settext("namn: " + namnyrke.getnamn() +"\nyrke: " + namnyrke.getyrke()); TestNamnYrke tny = new TestNamnYrke(); 3
Uppgift 2 Ärva JPanel I denna uppgifts ska du på samma sätt som i föregående uppgift skriva en klass som är en JPanel, dvs. som ska ärva av en JPanel. Denna panel ska innehålla ett antal olika grafiska komponenter, däribland 2 st JRadiobutton (till vänster) och en JComboBox n(till höger). I lösningen används två hjälppaneler, en som innehåller JRadioButton-komponenterna till vänster och en som innehåller Hobby och JComboBox-komponenten till höger. Panelerna genereras genom anrop till metoderna getcivilstandpanel respektive getovrigtpanel. Dessa metoder är givna och kommenterade nedan. Din uppgift är att färdigställa komponenten CivilstandOvrigt och sedan testköra den med det givna testprogrammet. import javax.swing.event.*; import java.awt.event.*; public class CivilstandOvrigt extends JPanel { private JRadioButton rbogift = new JRadioButton("ogift"); private JRadioButton rbgift = new JRadioButton("gift"); private JComboBox cbhobbies; public CivilstandOvrigt() { // komplettera med kod public JPanel getcivilstandpanel() { // se nedan public JPanel getovrigtpanel() { // se nedan // komplettera med nödvändiga get-metoder för att avläsa värden i radiobuttons (isselected()) // och comboboxen (getselecteditem()). Studera testprogrammet! RadioButtons Nu är det dags att skriva metoden som returnerar panelen med JRadioButton-komponenter. Följande gäller för panelen: Layoutmanager: GridLayout, 2 rader och 1 kolumn Komponenter: 2 st JRadioButton vilka placeras i GridLayouten. Border ButtonGroup Dekorerar panelen Gör att JRadioButton-komponterna fungerar tillsammans, dvs endast en vald åt gången 4
Metoden getcivilstandpanel (). private JPanel getcivilstandpanel () { JPanel panel = new JPanel(new GridLayout(2,1)); ButtonGroup grupp = new ButtonGroup(); rbogift.setselected(true); grupp.add(rbogift); grupp.add(rbgift); panel.setborder(borderfactory.createtitledborder("civilstånd")); panel.add(rbogift); panel.add(rbgift); return panel; Kommentarer ButtonGroup-objektet ser till att endast en av knapparna är vald åt gången. Referens till JRadioButton-komponenterna behövs när deras värde ska avläsas. Därför är referenserna till komponenterna instansvariabler (placerade överst i klassen). Många grafiska komponenter kan förses med en Border. Border-implementeringar får man genom att anropa klass-metoder i klassen BorderFactory. Det finns ett antal olika Border-implementeringar vilka du kan testa. JComboBox Nu är det dags att skapa panelen med JComboBox-komponenten. Följande gäller för panelen: Layoutmanager: GridLayout, 2 rader och 1 kolumn Komponenter: JLabel Placeras överst i GridLayouten JComboBox Placeras underst i GridLayouten Border Dekorerar panelen Metoden getovrigtpanel() private JPanel getovrigtpanel() { JPanel panel = new JPanel(new GridLayout(2,1)); JLabel lblhobby = new JLabel("Hobby:"); String[] hobbies = {"familjen", "idrott", "vävning", "programmering", "huset", "frimärken"; lblhobby.setfont(new Font("Dialog", Font.BOLD, 12)); cbhobbies = new JComboBox(hobbies); try { cbhobbies.setselectedindex(0); catch (IllegalArgumentException e) { panel.setborder(borderfactory.createtitledborder("övrigt")); panel.add(lblhobby); panel.add(cbhobbies); return panel; 5
Kommentarer Sträng-arrayen används för att initiera JComboBox-komponenten med värden. Referens till JComboBox-komponenterna behövs när valt värde ska avläsas, Därför är refernsvariabeln till JcombpBox-komponenten instansvariabel i klassen. Däremot skapas komponenten i denna metod. När man ska sätta ett av alternativen i comboboxen som förvalt så måste man fånge ett undantag. Kopiera koden som den är så får du lära dig mer om undantag i F18. Ett fönster att placera CivilstandOvrigt -panelen i Klassen TestCivilstandOvrigt ärver inte JFrame utan fönstret är en instansvariabel i klassen. I konstruktorn designas fönstret i vanlig ordning. Enda skillnaden mot att ärva JFrame att fönstret måste skapas med new och referensvariabeln till fönstret (frame) måste användas vid anrop till fönster-metoderna. import java.awt.event.*; public class TestCivilstandOvrigt implements ActionListener { private JFrame frame = new JFrame(); private JButton update = new JButton("Uppdatera textytan"); private JTextArea textyta = new JTextArea(); private CivilstandOvrigt co = new CivilstandOvrigt(); /** Creates a new instance of TestCivilstandOvrigt */ public TestCivilstandOvrigt() { Container c = frame.getcontentpane(); frame.setsize(300,200); frame.setdefaultcloseoperation(jframe.exit_on_close); textyta.seteditable(false); update.addactionlistener(this); c.add(co, BorderLayout.NORTH); c.add(textyta, BorderLayout.CENTER); c.add(update, BorderLayout.SOUTH); frame.setvisible(true); public void actionperformed(actionevent e) { textyta.settext("gift: " + co.getcivilstand() +"\nhobby: " + co.gethobby()); TestCivilstandOvrigt tny = new TestCivilstandOvrigt(); 6
Uppgift 3 Ärva JLabel Det går utmärkt att ärva av andra komponenter än JFrame och JPanel. I denna uppgift ska vi skapa en något specialiserad JLabel-komponent. Följande gäller för komponenten Den skapas med Dialog som typsnitt Texten är centrerad från början Bakgrundsfärgen syns När komponenten skapas måste man ange: text som ska visas stil storlek textfärg bakgrundsfärg För övrigt är komponenten som vilken JLabel-komponent som helst. public class Rubrik extends JLabel { public Rubrik(String content, int style, int fontsize, Color text, Color background) { Font font = new Font("Dialog", style, fontsize); settext(content); sethorizontalalignment(jlabel.center); setfont(font); setbackground(background); setopaque(true); setforeground(text); Kommentarer Klassen innehåller endast en konstruktor i vilka inställningarna görs: settext(content); texten anges sethorizontalalignment(jlabel.center); texten ska vara centrerad setfont(font); typsnittet anges setbackground(background); bakgrundsfärgen anges och komponenten görs ickesetopaque(true); transparent setforeground(text); textfärgen anges main-metod med fönster För att test utseendet på Rubrik-komponenten kan du skriva nedanstående main-metod (välj själv vilken klass den ska vara i). Som du ser så skapas bl.a. ett fönster i mainmetoden. Fönstret ges storlek, görs stängbart och synligt. Innan det görs synligt så placeras en komponent av typen Personuppgiter i fönstret. Komponenten kommer att fylla hela fönstret. 7
JFrame frame = new JFrame(); Rubrik rubrik1 = new Rubrik("Rött och svart", Font.BOLD, 24, Color.red, Color.black); Rubrik rubrik2 = new Rubrik("Blå text - gul bakgrund", Font.PLAIN, 16, Color.blue, Color.yellow); Rubrik rubrik3 = new Rubrik("Denna rubrik är i SOUTH", Font.ITALIC + Font.BOLD, 12, Color.white, Color.black); Container c = frame.getcontentpane(); frame.setsize(500,150); frame.setdefaultcloseoperation(jframe.exit_on_close); c.add(rubrik1, BorderLayout.NORTH); c.add(rubrik2, BorderLayout.CENTER); c.add(rubrik3, BorderLayout.SOUTH); frame.setvisible(true); 8
Uppgift 4 Ärva JPanel Nu ska du använda klasserna i Uppgift 1 Uppgift 2 och Uppgift 3 för att skapa en panel, Personuppgifter, med utseendet till höger. Du får själv avgöra vilken av panelens komponenter som ska växa/krympa om panelens höjd ändras. Nedanstående klass kan du komplettera med kod: public class Personuppgifter extends JPanel { private Rubrik rubrik = new Rubrik(); private NamnYrke ny = new NamnYrke(); private CivilstandOvrigt co = new CivilstandOvrigt(); public Personuppgifter() { public String getnamn() { public String getyrke() { public boolean getcivilstand() { public String gethobby() { main-metod med fönster För att test utseendet på panelen kan du skriva nedanstående main-metod (välj själv vilken klass den ska vara i). JFrame frame = new JFrame(); Personuppgifter personuppgifter = new Personuppgifter(); Container c = frame.getcontentpane(); frame.setsize(300,180); frame.setdefaultcloseoperation(jframe.exit_on_close); c.add(personuppgifter, BorderLayout.CENTER); frame.setvisible(true); 9
Uppgift 5 Samla information med dialog, del 1 En panel som är byggd för att samla in den användar-information som programmet behöver kan placeras i en dialog. När användaren är klar med inmatningen klickar hon på OK. Vill hon avbryta inmatningen så klickar hon på avbryt. Panelen i Uppgift 4, Personuppgifter, ska vi använda för att samla lite information om en person. Så här fungerar koden i testprogrammet: Användaren får se en dialog som innehåller en Personuppgifter-panel. Om användaren klickar på OK så Dialogen stängs Värden avläses med hjälp av getmetoder De avlästa värdena visas i en ny dialog (jmf. med Output.meddelande()). Annars Dialogen stängs En ny dialog meddelar att användaren klickade på Avbryt Klassen DialogExempel visar hur man använder dialoger för att samla/visa information. Efter programmet kan du läsa mer om metoden JOptionPane.showConfirmDialog(...). import javax.swing.joptionpane; public class DialogExempel { public void exempel() { Personuppgifter persinfo = new Personuppgifter(); String namn, yrke, hobby, meddelande; boolean gift; int result = JOptionPane.showConfirmDialog(null, persinfo, "Fönstrets titel är här", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if( result == JOptionPane.OK_OPTION ) { // Klick på OK namn = persinfo.getnamn(); yrke = persinfo.getyrke(); hobby = persinfo.gethobby(); gift = persinfo.getcivilstand(); meddelande = "Namn: " + namn + "\nyrke: " + yrke + "\ngift: " + gift + "\nhobby: " + hobby; JOptionPane.showMessageDialog(null, meddelande); else if (result == JOptionPane.CANCEL_OPTION ) { // Avbryt JOptionPane.showMessageDialog(null, "Användaren klickade på Avbryt"); DialogExempel de = new DialogExempel(); de.exempel(); 10
Lite information om dialoger I program har man ofta användning av att öppna en dialog för att få information av användaren. I klassen JOptionPane finns det metoder som stödjer detta, bl.a. showconfirmdialog(component parentcomponent, Object message, String title, int optiontype, int messagetype) Som du ser har just denna version många parametrar i parameterlistan. Det finna andra varianter av parameterlista (både koratare och längre) för denna metod. Detta kan du finna mer information om i API-dokumentationen (http://java.sun.com/javase/6/docs/api/index.html). Samtidigt kan du passa på att studera de andra metoderna som öppnar dialoger show???dialog där??? kan vara Confirm, Input, Message och Option. Förklaring av vad parametrarna innebär: parentcomponent message title optiontype Om du skickar med värdet null vid anropet så kommer dialogen att öppnas mitt på skärmen. I detta exempel kommer message att vara en JPanel-komponent som visar sig i dialogen. Titel på fönstret Anger vilka kanppar som ska vara synliga (4 värden möjliga) JOptionPane.DEFAULT_OPTION JOptionPane.YES_NO_OPTION JOptionPane.YES_NO_CANCEL_OPTION JOptionPane.OK_CANCEL_OPTION messagetype Anger vilken typ av bild som ska visas i dialogen (5 värden möjliga kan vara ndra bilder) JOptionPane.ERROR_MESSAGE JOptionPane.INFORMATION_MESSAGE JOptionPane.WARNING_MESSAGE JOptionPane.QUESTION_MESSAGE JOptionPane.PLAIN_MESSAGE (ingen bild) 11
Uppgift 6 Samla information med dialog, del 2 (Extra) Om panelen man använder endast är till för att visa i en speciell dialog kan man ha JPanelkomponenten som attribut i klassen (ej arv) och lägga till en metod för att visa dialogen. Skapa klassen Personuppgifter2 och skriv en instansvariabel av typen Jpanel. public class Personuppgifter2 { private JPanel panel = new JPanel(new BorderLayout()); // lägg till Personuppgifter1-klassens innehåll här // ändra konstruktorns namn // ändra i konstruktorn så att attributet panel designas Kopiera hela Personuppgifter1 till klassen Personuppgifter2. Ändra namnet på konstruktorn till Personuppgifter2(). Skriv metoden showdialog. public int showdialog() { return JOptionPane.showConfirmDialog(null, panel, "Fönstrets titel är här", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); fördelen med denna versionen är att det publika gränssnittet endast består av: get-metoderna + showdialog det som ärvs från Object Testkod av Personuppgifter2 Personuppgifter2 persinfo = new Personuppgifter2(); String meddelande; int result = persinfo.showdialog(); if( result == JOptionPane.OK_OPTION ) { // Klick på OK meddelande = "Namn: " + persinfo.getnamn() + "\nyrke: " + persinfo.getyrke() + "\ngift: " + persinfo.getcivilstand() + "\nhobby: " + persinfo.gethobby(); JOptionPane.showMessageDialog(null, meddelande); else if (result == JOptionPane.CANCEL_OPTION ) { // Avbryt JOptionPane.showMessageDialog(null, "Användaren klickade på Avbryt"); 12
Uppgift 7 Korrekt sätt att konstruera fönster (Extra) För att konstruera fönster på ett säkert och korrekt sätt ska alltid grafik- och händelse-tråden sköta konstruktionen. Detta gör amn med ett enkelt handgrepp som blir lättare att förstå när du studerat avsnittet om trådar (F20). Satserna som skapar eller anropar metoder i grafiska komponenter ska bäddas in i ett anrop till metoden SwingUtilities.InvokeLater: javax.swing.swingutilities.invokelater(new Runnable() { public void run() { // anrop till grafiska komponenter ); Kommentar Som argument till invokelater skapas en klass som implementerar Runnable (run-metoden måste implementeras i klassen). run-metoden anropar de grafiska komponenterna. Exempel : Uppgift 1 javax.swing.swingutilities.invokelater(new Runnable() { public void run() { TestNamnYrke tny = new TestNamnYrke(); ); Exempel : Uppgift 3 javax.swing.swingutilities.invokelater(new Runnable() { public void run() { JFrame frame = new JFrame(); Rubrik rubrik1 = new Rubrik("Rött och svart", Font.BOLD, 24, Color.red, Color.black); Rubrik rubrik2 = new Rubrik("Blå text - gul bakgrund", Font.PLAIN, 16, Color.blue, Color.yellow); Rubrik rubrik3 = new Rubrik("Denna rubrik är i SOUTH", Font.ITALIC + Font.BOLD, 12, Color.white, Color.black); Container c = frame.getcontentpane(); frame.setsize(500,150); frame.setdefaultcloseoperation(jframe.exit_on_close); c.add(rubrik1, BorderLayout.NORTH); c.add(rubrik2, BorderLayout.CENTER); c.add(rubrik3, BorderLayout.SOUTH); frame.setvisible(true); ); 13
Exempel på lösningar Uppgift 1 NamnYrke public class NamnYrke extends JPanel { private JTextField tfnamn = new JTextField(); private JTextField tfyrke = new JTextField(); private JLabel lblnamn = new JLabel("Namn"); private JLabel lblyrke = new JLabel("Yrke"); public NamnYrke() { setlayout(new BorderLayout()); JPanel labelpanel = getlabelpanel(); JPanel inputpanel = getinputpanel(); labelpanel.setpreferredsize(new Dimension(50,0)); add(labelpanel, BorderLayout.WEST); add(inputpanel, BorderLayout.CENTER); /********* Design-metoder **********/ private JPanel getlabelpanel() { JPanel panel = new JPanel(new GridLayout(2,1)); Font dialogb12 = new Font("Dialog", Font.BOLD, 12); lblnamn.setfont(dialogb12); lblyrke.setfont(dialogb12); panel.add(lblnamn); panel.add(lblyrke); return panel; private JPanel getinputpanel() { JPanel panel = new JPanel(new GridLayout(2,1)); panel.add(tfnamn); panel.add(tfyrke); return panel; /********** get-metoder ***********/ public String getnamn() { return tfnamn.gettext(); public String getyrke() { return tfyrke.gettext(); 14
Uppgift 2 CivilstandOvrigt import javax.swing.event.*; import java.awt.event.*; public class CivilstandOvrigt extends JPanel { private JRadioButton rbogift = new JRadioButton("ogift"); private JRadioButton rbgift = new JRadioButton("gift"); private JComboBox cbhobbies; public CivilstandOvrigt() { setlayout(new GridLayout(1,2)); add(getcivilstandpanel()); add(getovrigtpanel()); private JPanel getcivilstandpanel() { JPanel panel = new JPanel(new GridLayout(2,1)); ButtonGroup grupp = new ButtonGroup(); rbogift.setselected(true); grupp.add(rbogift); grupp.add(rbgift); panel.setborder(borderfactory.createtitledborder("civilstånd")); panel.add(rbogift); panel.add(rbgift); return panel; private JPanel getovrigtpanel() { JPanel panel = new JPanel(new GridLayout(2,1)); JLabel lblhobby = new JLabel("Hobby:"); String[] hobbies = {"familjen", "idrott", "vävning", "programmering", "huset", "frimärken"; lblhobby.setfont(new Font("Dialog", Font.BOLD, 12)); cbhobbies = new JComboBox(hobbies); try { cbhobbies.setselectedindex(0); catch (IllegalArgumentException e) { panel.setborder(borderfactory.createtitledborder("övrigt")); panel.add(lblhobby); panel.add(cbhobbies); return panel; // true = gift, false = ogift public boolean getcivilstand() { return rbgift.isselected(); public String gethobby() { return (String)cbHobbies.getSelectedItem(); 15
Uppgift 4 Personuppgifter public class Personuppgifter extends JPanel { private Rubrik rubrik = new Rubrik("Personuppgifter", Font.BOLD, 16, Color.white, Color.black); private NamnYrke ny = new NamnYrke(); private CivilstandOvrigt co = new CivilstandOvrigt(); public Personuppgifter() { setlayout(new BorderLayout()); add(rubrik, BorderLayout.NORTH); add(ny, BorderLayout.CENTER); add(co, BorderLayout.SOUTH); public String getnamn() { return ny.getnamn(); public String getyrke() { return ny.getyrke(); public boolean getcivilstand() { return co.getcivilstand(); public String gethobby() { return co.gethobby(); JFrame frame = new JFrame(); Personuppgifter personuppgifter = new Personuppgifter(); Container c = frame.getcontentpane(); frame.setsize(300,180); frame.setdefaultcloseoperation(jframe.exit_on_close); c.add(personuppgifter, BorderLayout.CENTER); frame.setvisible(true); 16
Uppgift 6 Samla information med dialog, del 2 (Extra) public class Personuppgifter2 { private JPanel panel = new JPanel(new BorderLayout()); private Rubrik rubrik = new Rubrik("Personuppgifter", Font.BOLD, 16, Color.white, Color.black); private NamnYrke ny = new NamnYrke(); private CivilstandOvrigt co = new CivilstandOvrigt(); public Personuppgifter2() { panel.add(rubrik, BorderLayout.NORTH); panel.add(ny, BorderLayout.CENTER); panel.add(co, BorderLayout.SOUTH); public String getnamn() { return ny.getnamn(); public String getyrke() { return ny.getyrke(); public boolean getcivilstand() { return co.getcivilstand(); public String gethobby() { return co.gethobby(); public int showdialog() { return JOptionPane.showConfirmDialog(null, panel, "Fönstrets titel är här", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); Personuppgifter2 persinfo = new Personuppgifter2(); String meddelande; int result = persinfo.showdialog(); if( result == JOptionPane.OK_OPTION ) { // Klick på OK meddelande = "Namn: " + persinfo.getnamn() + "\nyrke: " + persinfo.getyrke() + "\ngift: " + persinfo.getcivilstand() + "\nhobby: " + persinfo.gethobby(); JOptionPane.showMessageDialog(null, meddelande); else if (result == JOptionPane.CANCEL_OPTION ) { // Avbryt JOptionPane.showMessageDialog(null, "Användaren klickade på Avbryt"); 17