Część 4.4. Interakcja z użytkownikiem
Nauczyłeś się już, jak można w aplecie napisać jakiś tekst, wyświetlić elementy lub pliki graficzne, a także tworzyć animacje. Kolej na nawiązanie kontaktu z osobą uruchamiającą aplet: nauczysz się teraz jak aplet może reagować na zdarzenia związane z klawiaturą i myszką. Reagowanie przez aplet na zdarzenia związane z klawiaturą i myszką wymaga zaimplementowania mu odpowiednio interfejsów KeyListener i MouseListener. Interfrjsy te zawierają definicję metod obsługujących zdarzenia, które mogą być generowane przez klawiaturę i myszkę. Poniższa tabela przedstawia pełną listę zdarzeń, które mogą wystąpić:
Każda z powyższych metod otrzymuje jako parametr referencję do obiektu reprezentującego zdarzenie. Podstawowe metody i pola danych tych obiektów poznasz w poniższym przykładzie. Włączając do apletu obsługę zdarzeń związanych z klawiaturą i myszką musisz pamiętać zawsze o jednej rzeczy: niezależnie od tego, czy korzystasz z wszystkich, czy tylko z niektórych zdarzeń zawsze musisz napisać własną implementację wszystkich możliwych zdarzeń. Wynika to z faktu, że wszystkie metody obsługi zdarzeń w interfejsie są abstrakcyjne, więc pominięcie choćby jednej z nich w Twoim aplecie spowoduje, że będzie on również traktowany jako klasa abstrakcyjna. Metody, co widać w poniższym przykładzie mogą nie zawierać żadnego kodu. Muszą jednak być zadeklarowane. Pora na praktyczne wykorzystanie powyższych wiadomości. Zaczniemy od apletu, który będzie wyświetlał informację o urządzeniu, które wygenerowało zdarzenie oraz podstawowe parametry zdarzenia. Oto kod takiego apletu: import java.applet.*; import java.awt.*; import java.awt.event.*; // konieczne do obsługi zdarzeń // aplet implementuje interfejsy MouseListener i KeyListener public class Zdarzenia extends Applet implements MouseListener, KeyListener { MouseEvent eM = null; // obiekt reprezentujący zdarzenie myszki KeyEvent eK = null; // obiekt reprezentujący zdarzenie klawiatury public void init () { addMouseListener(this); // dodanie do apletu obsługi zdarzeń myszy i klawiatury addKeyListener(this); // konieczne, aby aplet obsługiwał te zdarzenia setBackground(new Color(255,255,226)); setFont(new Font("Courier", Font.BOLD, 18)); } public void paint (Graphics g) { int iHeight = getSize().height; int iWidth = getSize().width; int x; g.clearRect(0, 0, iWidth, iHeight); g.setColor(Color.blue); for(int n=0 ; n<4 ; n++) g.drawRect(n, n, iWidth-2*n-1, iHeight-2*n-1); // wyświetlanie, gdy zdarzenie wygenerowała klawiaturę if (eK != null) { x = eK.paramString().indexOf(","); g.drawString("Zdarzenie związane z KLAWIATURˇ", 10, 20); // wyświetlenie stałej opisującej typ zdarzenia g.drawString("Zdarzenie : " + eK.paramString().substring(0,x), 10, 60); // wyświetlenie tekstu przypisanego do klawisza generującego zdarzenie g.drawString("Klawisz : " + eK.getKeyText(eK.getKeyCode()), 10, 100); // wyświetlenie kodu klawisza generującego zdarzenie g.drawString("Kod klawisza: " + eK.getKeyCode(), 10, 140); } // wyświetlanie, gdy zdarzenie wygenerowała klawiature myszka if (eM != null) { x = eM.paramString().indexOf(","); g.drawString("ZDARZENIE ZWIˇZANE Z MYSZKˇ", 10, 20); // wyświetlenie stałej opisującej typ zdarzenia g.drawString("Zdarzenie : " + eM.paramString().substring(0,x), 10, 60); // wyświetlenie poziomej współrzędnej kursora myszki g.drawString("Pozycja X myszki: " + eM.getX(), 10, 100); // wyświetlenie pionowej współrzędnej kursora myszki g.drawString("Pozycja Y myszki: " + eM.getY(), 10, 140); } } // METODY OBSŁUGI ZDARZEŃ MYSZKI public void mousePressed (MouseEvent e) { eM = e; eK = null; repaint(); } public void mouseClicked (MouseEvent e) { eM = e; eK = null; repaint(); } public void mouseReleased (MouseEvent e) { eM = e; eK = null; repaint(); } public void mouseEntered (MouseEvent e) { eM = e; eK = null; repaint(); } public void mouseExited (MouseEvent e) { eM = e; eK = null; repaint(); } // METODY OBSŁUGI ZDARZEŃ KLAWIATURY public void keyPressed (KeyEvent e) { eM = null; eK = e; repaint(); } public void keyReleased (KeyEvent e) { eM = null; eK = e; repaint(); } public void keyTyped (KeyEvent e) { eM = null; eK = e; repaint(); } } Prześledź na przykładzie tego apletu kiedy i jakie zdarzenie występuje oraz jakie są jego parametry. Zwróć uwagę na teksty przypisane do klawiszy specjalnych i funkcyjnych.
Spróbujemy wykorzystać praktycznie nowe wiadomości. Napiszemy aplet, który po kliknięciu myszką w dowolnym miejscu będzie tam wyświetlał kółko, a po wciśnięciu dowolnego klawisza wyświetli przypisany do niego tekst. Najprostsza wersja takiego apletu będzie wyglądała następująco: import java.applet.*; import java.awt.*; import java.awt.event.*; public class ZdarzeniaRysowanie1 extends Applet implements MouseListener, KeyListener { String s = ""; public void init () { addMouseListener (this); addKeyListener (this); setBackground (new Color(255,255,226)); setFont (new Font("Courier", Font.BOLD, 14)); } public void mousePressed (MouseEvent e) { } public void mouseClicked (MouseEvent e) { getGraphics().fillOval(e.getX()-2, e.getY()-2, 5, 5); } public void mouseReleased (MouseEvent e) { } public void mouseEntered (MouseEvent e) { } public void mouseExited (MouseEvent e) { } public void keyPressed (KeyEvent e) { } public void keyReleased(KeyEvent e) { } public void keyTyped (KeyEvent e) { s += e.getKeyText(e.getKeyCode()); getGraphics().drawString(s, 10, 20); } } Jak widzisz, nie jest to nic trudnego. Po zaimplementowaniu obsługi zdarzeń przez aplet reagujemy tylko na dwa z nich: kliknięcie myszką i wciśnięcie klawisza z klawiatury. W pierwszym przypadku rysowane jest kółko w miejscu aktualnego położenia kursora, w drugim - dopisywany tekst przypisany do wciśniętego klawisza. Aplet w zasadzie robi to co chcieliśmy, ale... Spróbuj przesłonić część lub cały aplet innym oknem. Gdy wrócisz ponownie do apletu, to zauważysz, że jego zasłonięta część została całkowicie wyczyszczona. Wiąże się to z brakiem procedury odświeżającej aplet (odrysowywanie apletu metod paint).
Spróbujemy poprawić niedoskonałości poprzedniego apletu. Jednocześnie zmienimy trochę jego działanie: nadal będziemy rysować kółka w miejscu kliknięcia myszką, ale dla zdarzeń klawiatury wykorzystamy tylko 3 litery do zmiany koloru rysowania. Podstawowym problemem pozostaje jednak odświeżanie apletu. Aby działało ono poprawnie, musimy zapamiętywać wszystkie narysowane kółka i w razie potrzeby rysować je od nowa. Jak je zapamiętać? Moglibyśmy użyć do tego celu tablicy, w której przechowywane byłyby współrzędne środka i kolor każdego kółka. Powstaje jednak problem: jak wielką tablicę zarezerwować? 10, 100 a może tysiąc elementów? Problem w zasadzie nie do rozstrzygnięcia. Każda tablica w końcu zapełni swoje wszystkie elementy. A użytkownik może chcieć rysować dalej. Poza tym ogromna tablica zużywa sporo zasobów systemowych, a może okazać się, że nie będzie wykorzystana nawet w drobnym ułamku. Java nie zaś daje możliwości dynamicznego rozszerzania tablic. Aby rozwiązać ten problem skorzystamy z klasy Vector. Jest to klasa, która w dużym stopniu przypomina dynamiczną tablicę:
Jak wspomnieliśmy, klasa Vector przechowuje jedynie obiekty. Dlatego też w naszym aplecie utworzymy wewnętrzną klasę reprezentująca pojedynczy kółko, a w obiekcie typu Vector będziemy przechowywać kolekcję naszych kółek. Dopiszemy również do apletu metodę paint (), która będzie automatycznie odświeżać aplet. Kod tak zmodyfikowanego apletu będzie następujący: import java.applet.*; import java.awt.*; import java.awt.event.*; import java.util.*; // tam zadeklarowana jest klasa Vector public class ZdarzeniaRysowanie1 extends Applet implements MouseListener, KeyListener { Vector kola = new Vector(); // obiekt do przechowywania kolekcji kół Color Kolor = Color.blue; // do zapamiętania aktualnego koloru rysowania public void init () { addMouseListener (this); addKeyListener (this); setBackground (new Color(255,255,255)); } public void paint (Graphics g) { Kolo mKolo; // obiekt klasy Kolo g.setColor(Color.black); for (int n = 0 ; n < 3 ; n++) g.drawRect(n, n, getSize().width-2*n-1, getSize().height-2*n-1); // odrysowywanie wszystkich zapamiętanych kół for (int n = 0 ; n < kola.size() ; n++) { mKolo = (Kolo) kola.elementAt(n); // pobranie pojedynczego koła z wektora g.setColor(mKolo.Kolor); // ustawienie koloru koła g.fillOval(mKolo.X, mKolo.Y, 7, 7); // rysowanie zapamiętanego koła } } public void mousePressed (MouseEvent e) { } // dodanie nowego koła do kolekcji i odświeżenie apletu public void mouseClicked (MouseEvent e) { kola.addElement(new Kolo(e.getX(), e.getY(), Kolor)); repaint(); } public void mouseReleased (MouseEvent e) { } public void mouseEntered (MouseEvent e) { } public void mouseExited (MouseEvent e) { } public void keyPressed (KeyEvent e) { } public void keyReleased(KeyEvent e) { } // ustawienie aktualnego koloru klawiszami B, R lub G public void keyTyped (KeyEvent e) { if (e.getKeyText(e.getKeyCode()).compareTo("B") == 0) Kolor = Color.blue; if (e.getKeyText(e.getKeyCode()).compareTo("R") == 0) Kolor = Color.red; if (e.getKeyText(e.getKeyCode()).compareTo("G") == 0) Kolor = Color.green; } } // deklaracja klasy Kolo // klasa posiada tylko zmienne publiczne do zapamiętania danych koła // oraz konstruktor pobierający i zapamiętujący dane koła // klasa będzie widoczna tylko w klasach tego samego pliku class Kolo { public int X, Y; public Color Kolor; public Kolo(int x, int y, Color kolor) { X = x - 3; Y = y - 3; Kolor = kolor; } }
Jak widać w ten niezbyt skomplikowany sposób rozwiązaliśmy nasz problem, a dodatkowo wzbogaciliśmy aplet o możliwość wyboru koloru rysowania (oczywiście możesz znacznie rozszerzyć ilość możliwych kolorów).
|