5 Användargränssnitt 5.1 AWT och Swing AWT (Abstract Windowing Toolkit) är ett omfattande klassbibliotek för hantering av användargränssnitt i Java. Det är gjort så att applikationerna blir helt plattformsoberoende när det gäller användargränssnittet. Däremot så får fönster, knappar och andra gränssnittskomponenter samma "look and feel" som är standard på respektive plattform. Swing är den nya standarden sedan Java 2, version 1.2. I swing kan man välja om man vill att grässnittskomponenter ska ha respektive plattforms "look and feel" eller om de ska se likadana ut oavsett plattform. Ett program kan till och med ändra sin "look and feel" under exekveringen. Swing-komponenter brukar kallas för lättviktskomponenter medan awt-komponenter kallas för tungviktskomponenter eftersom de är beroende av den lokala plattformens utseende. Det finns även flera tungviktskomponenter i swing, tex de som visar fönster och Applets. De är fortfarande beroende av den lokala plattformens utseende. Swingkomponenter är implementerade enbart i Java. Swing-komponenterna är en del av JFC Java Foundation Classes. JFC är en samling klasser som finns som hjälp för att bygga grafiska användargränssnitt (GUIs). De viktigaste gränssnittskomponenterna i AWT respektive Swing bildar var sin arvshierarki. Utöver dessa finns bl a klasserna Dimension, Point, Rectangle, Polygon, Color, Image, Graphics. Vissa Swing-komponenter ärver från AWT-komponenter och dessa är tungviktskomponenter.
5.1.1 AWT hierarkin Object Component Container List Label Button SequenceIn Choice void play(); void stop(); Checkbox Scrollbar TexComponent Canvas Panel Window TextField TextArea Applet Dialogue Frame 5.1.2 Standard komponenter i AWT Component Klassen Component ligger i toppen av arvshierarkin. En komponent representerar ett individuellt gränssnittselement som kan presenteras, positioneras, visa grafik, fånga händelser etc. Alla klasser nedanför i arvshierarkin ärver dessa egenskaper och "är alltså komponenter". Förutom dessa komponenterna så kan man definiera egna. Container Komponenter kan placeras i en kontainer. En kontainer organiserar och håller samman en uppsättning komponenter. En kontainer är själv en komponent som kan placeras i en annan kontainer. Panel En panel är den enklaste typ av kontainer. det är en yta på vilken andra komponenter kan placeras. Dess default layoutmanager är en flowlayoutmanager. Applet En applet är en kontainer speciellt för applet-program.
Window Ett enkelt fönster utan kantlinjer och utan menylist. Måste läggas i en Frame, Dialog eller annan Window. Dess default layoutmanager är en borderlayoutmanager. Dialog Ett enkelt fönster med kantlinje och titel. Måste läggas i en Frame eller annat Dialogobjekt. Dess default layoutmanager är en borderlayoutmanager. Frame Ett applikationsfönster med kantlinje och titel. Dess default layoutmanager är en borderlayoutmanager. Canvas En komponent som endast tillhandahåller en yta att rita och presentera bilder på. TextField Ett fält med en rad där man kan skriva text. TextArea Ett fält med flera rader där man kan visa text eller skriva in text. LayoutManager-klasserna Button En knapp som kan tyckas ned för att utföra kommandon eller för att besvara frågor från applikationen till användaren. Listbox En listbox är en bläddringsbar lista med textsträngar. En användare väljer en viss rad i listboxen genom att klicka på den. Label En label består endast av text som inte kan väljas eller ändras av användaren. Används endast för led text eller information i ett fönster. 5.1.3 Skapande av komponenter i en applet När man ska bygga upp ett användargränssnitt i Java måste man först instansiera någon form av kontainer-objekt. Sedan kan man instansiera de komponenter man vill ha och lägga in dem i kontainern via anrop av någon av de överlagrade add-metoder som finns. I fallet med en applet så är den i sig själv en kontainer, vilken redan är instansierad när appletens init-metod anropas. Så det är bara att skapa appletens gränssnittskomponenter och anropa add för att lägga in dem i appleten, vilket bör göras i init-metoden. Komponenterna som läggs till en container placeras i en lista i samma ordning som de läggs till. Denna lista är utgångspunkten för en s k tabuleringsordning, d v s när användaren använder Tab-knappen för att förflytta sig mellan olika komponenter i ett fönster så kommer varje Tab-nedtryck att flytta till nästa komponent i listan.
import java.awt.*; import java.applet.*; public class AwtAppl extends Applet public void init() super.init(); resize(300,200); // ändra storlek på appletens fönster Button demobutton = new Button("Knapp"); add(demobutton); Checkbox democheckbox = new Checkbox("Märkruta"); add(democheckbox); 5.1.4 Skapande av komponenter i en applikation När man gör en applikation (till skillnad från en applet) så deklarerar man en klass som ärver klassen Frame. I applikationsklassens konstruktor lägger man lämpligen skapandet av komponenter som ska ligga i applikationsfönstret. Det går även att lägga in nya komponenter under programmets exekvering. För att ta bort komponenter ur en container anropar man remove. import java.awt.*; public class AwtAppl extends Frame public static void main(string[] args) AwtAppl fönster = new AwtAppl(); fönster.setsize(300,200); fönster.show(); public AwtAppl() Button demobutton = new Button("Knapp"); Button tabort = new Button("Borta"); add(demobutton, "South"); Checkbox democheckbox = new Checkbox("Märkruta"); add(democheckbox, "North"); remove(tabort);
5.2 Händelsehantering Händelser i ett användargränssnitt är t.ex. när användaren klickar på en knapp, skriver in text, väljer en meny etc. Sådana händelser betyder någonting och måste hanteras av programmet. Man måste alltså Identifiera vilka händelser som är aktuella att hantera Skriva kod som avgör vad som skall inträffa när händelsen sker Koppla denna kod till händelsens inträffande I Java finns det två olika modeller för hur man hanterar händelser. Den gamla modellen bygger på att samtliga händelser skickas till berörda komponenter (resurskrävande). Den nya modellen är den som rekommenderas och den beskrivs här. Den nya modellen för händelsehantering bygger på principen att gränssnittskomponenterna talar om vilka händelser de är intresserade av, och sedan får de endast dessa händelser. Grundbegrepp som används i modellen är: Händelsekälla (Event Source): ett objekt som kan generera händelser. Händelselyssnare (Event Listener): ett objekt som lyssnar på en viss typ av händelser. Händelse (Event): ett objekt som representerar en viss händelse. 5.2.1 Händelsekälla Händelsekällor som kan generera händelser kopplas till en eller flera händelselyssnare. En händelselyssnare implementeras av en klass som vill kunna fånga en viss typ av händelser som en källa genererar, och lyssnaren registreras hos händelsekällan. När sedan händelsen inträffar kommer källan att meddela detta till alla registrerade lyssnare genom anrop på en händelsespecifik metod i lyssnaren. Händelsekällan måste ha två metoder för varje händelse den kan rapportera. En metod för att ta emot händelselyssnare och en metod för att ta bort händelselyssnare: public void add<typ>listener(<typ>listener l) public void remove<typ>listener(<typ>listener l) <Typ> = Action, Mouse, MouseMotion, Window, Key, En händelsekälla som exempelvis kan generera händelser som berör tangentbordet (en tangent trycks ned när komponenten haft fokus) skulle definiera följande metoder:
public void addkeylistener(keylistener l) public void removekeylistener(keylistener l) 5.2.2 Händelselyssnare Händelselyssnaren måste implementera ett lyssnargränssnitt som är specifikt för varje typ av händelse. Ett sådant gränssnitt innehåller en metod per händelse. KeyListener är ett fördefinierat lyssnargränsnitt som innehåller ett antal metoder som källan kommer att anropa för att rapportera olika händelser: public interface KeyListener extends java.util.eventlistener public void keytyped(keyevent e); public void keypressed(keyevent e); public void keyreleased(keyevent e); Det finns totalt 11 lyssnargränssnitt som alla ärver från java.util.eventlistener: ActionListener, AdjustmentListener, ComponentListener, ContainerListener, FocusListener, ItemListener, KeyListener, MouseListener, MouseMotionListener, TextListener, WindowListener. De har alla metoder som motsvarar relevanta händelser för lyssnaren. 5.2.3 Händelse Händelser är subklasser till java.util.eventobject. Händelse-klasserna innehåller metoder för att ta reda på mer om händelsen som objektet representerar. EX: Klassen MouseEvent har metoder som getx, gety, getclickcount etc, medan klassen WindowEvent har metoder som getwindow, getcomponent etc. Samtliga händelseklasser har alltså fält och attribut för att ge ytterligare information om den händelse som har inträffat. 5.2.4 Exempel import java.applet.*; import java.awt.*; import java.awt.toolkit.*; import java.awt.event.*; public class Beeper extends Applet implements ActionListener
private Button button; public void init() setlayout(new BorderLayout()); button = new Button("Click me"); add("center", button); button.addactionlistener(this); public void actionperformed(actionevent e) if(e.getsource() == button) Toolkit.getDefaultToolkit().beep(); button.setlabel("click me again"); Vid start av Appleten: Efter att man klickat på knappen: Beeper-classen kommer fungera som lyssnare. Knappen button kommer vara händelsekälla som meddelar lyssnaren Beeper när knappen blir nedtryck. Detta sker genom ett anrop till metoden actionperformed som härstammar från interfacet ActionListener. Händelsen representeras av objektet e som är ett ActionEvent
objekt. Från ActionEventobjektet e kan information hämtas om t.ex. vilken händelsekälla (om det existerar flera, vilket det oftast gör) som resulterade i händelsen. Metoden getsource returnerar det objekt som ansvarar för händelsen. 5.3 Layoutmanager De olika komponenter som stoppas in i en container ska ju ritas upp i fönstret på bildskärmen. För att bestämma storlek och placering av komponenterna så använder man en s.k. layoutmanager. Storlek och placering ska kunna anpassa sig efter fönstrets storlek, vilken kan ändras när användaren förändrar fönstret. Varje container har en default layoutmanager, men man kan definiera en egen och ge till containern med metoden setlayout. Man kan också köra utan layoutmanager (setlayout(null)), men då måste komponenterna positioneras statiskt (genom metoderna setbounds, setlocation, setsize, m fl). Statisk placering är inte att föredra om en applikation ska vara plattformsoberoende då storleken på komponenterna kan variera på olika plattformar. Det finns ett antal fördefinierade layoutmanagers i AWT vilka har lite olika filosofi när det gäller att placera ut komponenter: BorderLayout - placerar komponenter i fem fält: North, South, East, West, Center (Default för Window, Frame och Dialog) setlayout(new BorderLayout());... add(new Button("North"), BorderLayout.NORTH); add(new Button("South"), BorderLayout.SOUTH); add(new Button("East"), BorderLayout.EAST); add(new Button("West"), BorderLayout.WEST); add(new Button("Center"), BorderLayout.CENTER);
GridLayout - delar upp skärmen i ett antal rader och kolumner där komponenter läggs in setlayout(new GridLayout(3,2)); add(new Button("1")); add(new Button("2")); add(new Button("3")); add(new Button("4")); add(new Button("5")); add(new Button("6")); FlowLayout - placerar komponenter radvis från vänster till höger (Default för Panel och Applet) public class mybuttons extends Applet Button button1, button2, button3; public void init() button1 = new Button("Ok"); button2 = new Button("Open"); button3 = new Button("Close");
add(button1); add(button2); add(button3); CardLayout - lägger komponenterna ovanpå varandra som korten i en kortlek; bara en syns GridBagLayout - en komplex layoutmanager där man kan styra placeringen detaljerat 5.4 Exempel 1 import java.awt.*; import java.awt.event.*; public class ButtonDemo extends Frame implements ActionListener Button b1, b2, b3; Label l = new Label("Inget knapp nedtryckt!"); public ButtonDemo(String title) settitle(title); setlayout(new FlowLayout());
b1 = new Button("Knapp 1"); b2 = new Button("Knapp 2"); b3 = new Button("Knapp 3"); b1.addactionlistener(this); b2.addactionlistener(this); b3.addactionlistener(this); add(l); add(b1); add(b2); add(b3); public void actionperformed(actionevent event) Object b = event.getsource(); if(b == b1) l.settext("knapp 1"); else if(b == b2) l.settext("knapp 2"); else if(b == b3) l.settext("knapp 3"); public static void main(string[] args) ButtonDemo demo = new ButtonDemo("ButtonDemo"); demo.setsize(200, 150); demo.show(); 5.5 Exempel - 2 import java.awt.*; import java.awt.event.*;
public class GUIWindow extends Frame final String FILEDIALOGMENUITEM = "File dialog..."; private MenuItem filedialogitem; public GUIWindow() Panel bottompanel = new Panel(); Panel centerpanel = new Panel(); setlayout(new BorderLayout()); //Add big things to the center area of the window. centerpanel.setlayout(new GridLayout(1,2)); //Put a canvas in the left column. centerpanel.add(new MyCanvas()); //Put a label and a text area in the right column. Panel p = new Panel(); p.setlayout(new BorderLayout()); p.add("north", new Label("Label", Label.CENTER)); p.add("center", new TextArea("TextArea", 5, 20)); centerpanel.add(p); add("center", centerpanel); //Add small things at the bottom of the window. bottompanel.add(new TextField("TextField")); bottompanel.add(new Button("Button")); bottompanel.add(new Checkbox("Checkbox")); Choice c = new Choice(); c.additem("choice Item 1"); c.additem("choice Item 2"); c.additem("choice Item 3"); bottompanel.add(c); add("south", bottompanel); //Put a list on the right side of the window. List l = new List(3, false); for (int i = 1; i <= 10; i++) l.add("list item " + i); add("east", l); //Set up the menu bar. MenuBar mb = new MenuBar(); Menu m = new Menu("Menu"); m.add(new MenuItem("Menu item 1")); m.add(new CheckboxMenuItem("Menu item 2")); m.add(new MenuItem("Menu item 3"));
m.add(new MenuItem("-")); filedialogitem = new MenuItem(FILEDIALOGMENUITEM); filedialogitem.addactionlistener(new MyMenyListener()); m.add(filedialogitem); mb.add(m); setmenubar(mb); addwindowlistener(new MyWindowListener()); //Inner class - Meny and Window Listener class MyMenyListener implements ActionListener public void actionperformed(actionevent event) if(event.getsource() == filedialogitem) FileDialog fd = new FileDialog(new Frame(), FileDialog"); fd.show(); class MyWindowListener extends WindowAdapter public void windowclosing(windowevent event) System.exit(0); public static void main(string args[]) GUIWindow window = new GUIWindow(); window.settitle("the AWT Components"); window.pack(); window.show(); //We can't just instantiate Canvas, since its default // implementation gives us nothing interesting to look at or do. // So here's a Canvas subclass that draws something slightly //interesting. class MyCanvas extends Canvas public void paint(graphics g) int w = getsize().width; int h = getsize().height; g.drawrect(0, 0, w - 1, h - 1); g.drawstring("canvas", (w - g.getfontmetrics().stringwidth("canvas"))/2, 10);
g.setfont(new Font("Helvetica", Font.PLAIN, 8)); g.drawline(10,10, 100,100); g.fillrect(9,9,3,3); g.drawstring("(10,10)", 13, 10); g.fillrect(49,49,3,3); g.drawstring("(50,50)", 53, 50); g.fillrect(99,99,3,3); g.drawstring("(100,100)", 103, 100);
5.6 Exempel 3 import java.awt.*; import java.awt.event.*; public class DialogWindow extends Frame implements ActionListener boolean inanapplet = true; private SimpleDialog dialog; private TextArea textarea; String newline; public DialogWindow() textarea = new TextArea(5, 40); textarea.seteditable(false); add("center", textarea); Button button = new Button("Click to bring up dialog"); button.addactionlistener(this); Panel panel = new Panel(); panel.add(button); add("south", panel); addwindowlistener(new WindowAdapter() public void windowclosing(windowevent e) if (inanapplet) setvisible(false); dispose(); else System.exit(0); ); newline = System.getProperty("line.separator"); public void actionperformed(actionevent event) if (dialog == null) dialog = new SimpleDialog(this, "A Simple Dialog"); dialog.setvisible(true); public void settext(string text) textarea.append(text + newline);
public static void main(string args[]) DialogWindow window = new DialogWindow(); window.inanapplet = false; window.settitle("dialogwindow Application"); window.pack(); window.setvisible(true); class SimpleDialog extends Dialog implements ActionListener TextField field; DialogWindow parent; Button setbutton; SimpleDialog(Frame dw, String title) super(dw, title, false); parent = (DialogWindow)dw; //Create middle section. Panel p1 = new Panel(); Label label = new Label("Enter random text here:"); p1.add(label); field = new TextField(40); field.addactionlistener(this); p1.add(field); add("center", p1); //Create bottom row. Panel p2 = new Panel(); p2.setlayout(new FlowLayout(FlowLayout.RIGHT)); Button b = new Button("Cancel"); b.addactionlistener(this); setbutton = new Button("Set"); setbutton.addactionlistener(this); p2.add(b); p2.add(setbutton); add("south", p2); //Initialize this dialog to its preferred size. pack(); public void actionperformed(actionevent event) Object source = event.getsource();
if ((source == setbutton) (source == field)) parent.settext(field.gettext()); field.selectall(); field.settext(null); setvisible(false);