Lektion Händelsehanterare Händelsehanterare kallas även lyssnare. En lyssnare har som uppgift att ta hand om olika händelser som kan inträffa. För att lyssnaren skall reagera på händelser måste den registreras hos den aktuella komponenten. Registrering innebär att den kopplas till ett objekt och läggs till i en lista av händelsehanterare. Varje komponent har en sådan lista. Aktionslyssnare En vanlig situation är att vi har en knapp i ett fönster och att något ska hända när vi trycker på knappen. T ex känner vi igen vår ButtonTest : class MyPanel extends JPanel implements ActionListener { public void actionperformed(actionevent evt) { Object source = evt.getsource();... public void paintcomponent(graphics g) {... Tillägget implements ActionListener måste vara med. Vi får alltså kompileringsfel om det inte är med, liksom när det är med och metoden actionperformed saknas. Vi kan göra på samma sätt när det gäller muslyssnare, tangentlyssnare, osv. Men det blir inte lika enkelt för andra händelseklasser har oftast flera metoder som måste definieras. T ex för tangenthändelser måste alla ingående metoderna finnas med även om inte alla får något vettigt innehåll. else if (e.getkeycode() == KeyEvent.VK_DOWN){ else if (e.getkeycode() == KeyEvent.VK_LEFT){ else if (e.getkeycode() == KeyEvent.VK_RIGHT){ public void keyreleased(keyevent e){ public void keytyped(keyevent e){ se hela filen KeyListener_verA.java i slutet på dokumentet Sida 1
Lyssnargränssnitten kräver att alla metoder implementeras, oavsett hur många man egentligen använder. Om man använder bara någon enstaka metod måste en mängd tomma metoder skrivas, vilket inte bara är onödigt utan också leder till svårlästa program. För att minska skrivarbetet finns några s.k. adaptrar, tomma implementationer av lyssnargränssitten, definierade. I stället för att implementera ett gränssnitt kan man skapa en subklass till en adaptor: //Skapa lyssnaren i en komponent-konstruktor (i en JComponent subklass) class TangentLyssnare extends KeyAdapter { else if (e.getkeycode() == KeyEvent.VK_DOWN){ else if (e.getkeycode() == KeyEvent.VK_LEFT){ else if (e.getkeycode() == KeyEvent.VK_RIGHT){ TangentLyssnare tanglyssnare = new TangentLyssnare(); t.addkeylistener(tanglyssnare); Exemplet vi sett har en inre klasser, d.v.s. klasser som helt definieras inne i andra klasser. Inre klasser kan komma åt den omgivande klassens metoder och attribut. En instans av en inre klass kan finnas och fungera bara inuti en instans av sin omgivande klass. se hela filen KeyListener_verB.java i slutet på dokumentet Klassen TangentLyssnare i exemplet har som enda uppgift att implementera händelsehanteringen. Själva namnen är ointressanta och faktum är att Java tillåter icke namngivna inre klasser, s.k. anonyma klasser. Vi skulle kunna förenkla exemplet: //När vi ärver en adapterklass kan vi skriva allt ännu kortare genom tekniken "anonyma klasser" KeyListener klyssnare = new KeyAdapter(){ else if (e.getkeycode() == KeyEvent.VK_DOWN){ else if (e.getkeycode() == KeyEvent.VK_LEFT){ else if (e.getkeycode() == KeyEvent.VK_RIGHT){ ; //OBS! semikolon Ett överdrivet användande av inre och speciellt anonyma klasser gör koden klart oläsbar varför man bör vara sparsam med dem: anonyma klasser bör ha en eller högst två metoder och vara av typen "klass som gör en enda sak" (som adaptern). se hela filen KeyListener_verC.java i slutet på dokumentet Sida 2
Till sist så är det ju lite fult med att knappen ska innehålla tangentlyssnaren så ett exempel på hur man kan lägga det i en panel. Skapar därför en extra panel som får lyssnaren istället för knappen. Den panelklassen blir då en inre klass. se hela filen KeyListener_verD.java i slutet på dokumentet Bra teori att läsa Exemplen nedan. Övningar Övning 1 Valfritt Sida 3
//KeyListener_verA.java class Pan extends JPanel implements ActionListener, KeyListener{ //Knappen är den komponent vi hänger på tangentlyssnaren på JButton t; t.addkeylistener(this); yled=0; xled=0; //I lyssnarklassen KeyEvent finns TRE metoder som måste specificeras // genom att vi använder gränssnittet KeyListener //Vi måste här ange alla tre även om inte alla ska göra något else if (e.getkeycode() == KeyEvent.VK_DOWN){ else if (e.getkeycode() == KeyEvent.VK_LEFT){ else if (e.getkeycode() == KeyEvent.VK_RIGHT){ public void keyreleased(keyevent e){ public void keytyped(keyevent e){ //Metoden för att rita upp allt g.filloval(xled,yled,30,30); class Window extends JFrame{ public class KeyListener_verA{ Sida 4
//KeyListener_verB.java class Pan extends JPanel implements ActionListener{ //Knappen är den komponent vi hänger på tangentlyssnaren på JButton t; //I lyssnarklassen KeyEvent finns TRE metoder som måste specificeras //Ibland utnyttjar man inte alla metoder, t ex så vill vi bara använda en enda. //Då kan det vara mer praktiskt att använda något som kallas adapterklasser(en abstrakt klass som //implementerar motsvarande gränssnitt) och som finns i java.awt.event. //De innehåller bara tomma metoder men ändå definierade. När vi nu skapar en egen lyssnarklass som //en subklass av adapterklassen behöver vi bara definiera de metoder vi vill använda. // //Skapa lyssnaren i en komponent-konstruktor (i en JComponent subklass) class TangentLyssnare extends KeyAdapter { else if (e.getkeycode() == KeyEvent.VK_DOWN){ else if (e.getkeycode() == KeyEvent.VK_LEFT){ else if (e.getkeycode() == KeyEvent.VK_RIGHT){ TangentLyssnare tanglyssnare = new TangentLyssnare(); t.addkeylistener(tanglyssnare); yled=0; xled=0; //Metoden för att rita upp allt g.filloval(xled,yled,30,30); class Window extends JFrame{ public class KeyListener_verB{ Sida 5
//KeyListener_verC.java class Pan extends JPanel implements ActionListener{ //Knappen är den komponent vi hänger på tangentlyssnaren på JButton t; //I lyssnarklassen KeyEvent finns TRE metoder som måste specificeras //Ibland utnyttjar man inte alla metoder, t ex så vill vi bara använda en enda. //Då kan det vara mer praktiskt att använda något som kallas adapterklasser(en abstrakt klass som //implementerar motsvarande gränssnitt) och som finns i java.awt.event. //De innehåller bara tomma metoder men ändå definierade. När vi nu skapar en egen lyssnarklass som //en subklass av adapterklassen behöver vi bara definiera de metoder vi vill använda. // //När vi ärver en adapterklass kan vi skriva allt ännu kortare genom tekniken "anonyma klasser" KeyListener klyssnare = new KeyAdapter(){ else if (e.getkeycode() == KeyEvent.VK_DOWN){ else if (e.getkeycode() == KeyEvent.VK_LEFT){ else if (e.getkeycode() == KeyEvent.VK_RIGHT){ ; t.addkeylistener(klyssnare); yled=0; xled=0; //Metoden för att rita upp allt g.filloval(xled,yled,30,30); class Window extends JFrame{ public class KeyListener_verC{ Sida 6
//KeyListener_verD.java class Pan extends JPanel implements ActionListener{ //Inre klass class RitPanel extends JPanel { g.filloval(xled,yled,40,40); setbackground(new Color(200,200,0)); //Ritpanelen är den komponent vi hänger på tangentlyssnaren på RitPanel rp; JButton b; //Tangentlyssnare genom tekniken "anonyma klasser" KeyListener klyssnare = new KeyAdapter(){ else if (e.getkeycode() == KeyEvent.VK_DOWN){ else if (e.getkeycode() == KeyEvent.VK_LEFT){ else if (e.getkeycode() == KeyEvent.VK_RIGHT){ ; setbackground(color.white); b = new JButton("Clear"); rp = new RitPanel(); add(rp); add(b); rp.setbounds(0,0,600,550); b.setbounds(250,555,100,30); rp.addkeylistener(klyssnare); b.addactionlistener(this); rp.setfocusable(true); rp.requestfocus(); yled=0; xled=0; class Window extends JFrame { setresizable(false); public class KeyListener_verD { Sida 7