W świecie komputerowego przetwarzania danych żadna awaria się nie kończy, ponieważ przechodzi zawsze w następną.
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ć:

ZdarzenieMetoda obsługująca zdarzenie
Zdarzenia generowane przez klawiaturę (interfejs KeyListener)
Wciśnięcie klawisza public void keyPressed (KeyEvent e)
Klawisz jest wciśnięty public void keyTyped (KeyEvent e)
Zwolnienie klawisza public void keyReleased (KeyEvent e)
Zdarzenia generowane przez myszką (interfejs MouseListener)
Kursor myszki "najechał" na aplet public void mouseEntered (MouseEvent e)
Kursor myszki opuścił obszar apletu public void mouseExited (MouseEvent e)
Wciśnięcie przycisku myszki public void keyPressed (KeyEvent e)
Zwolnienie przycisku myszki public void mousePressed (MouseEvent e)
Klikniecie przyciskiem myszki public void mouseClicked (MouseEvent e)

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ę:

  • może przechowywać obiekty dowolnego typu (tylko obiekty);
     
  • można w każdym momencie do niej dodać nowy lub usunąć wcześniej wstawiony element;
     
  • pozwala na swobodny dostęp do każdego przechowywanego obiektu poprzez jego indeks;
     
  • posiada również szereg innych metod i właściwości, o których jednak tutaj nie będziemy mówić.
     

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).

« wstecz   dalej »