Programi koji podržavaju GUI (Graphical User Interface) zahtevaju da svaka korisnikova akcija, bio to klik, pritisak tastera,... se smesti u tzv. red događaja (event queue), prema rodosledu pojave događaja, a potom se oni obrađuju, jedan po jedan u smislu da u petlji programa se čitaju događaji iz reda, a switch naredbom raspodjeljuju odgovarajućim delovima programa koji će ih obraditi..
Yavisno od arhitekture operativnog sistema, može postojatii jedan veliki sistemski red događaja ili svaka aplikacija može imati sopstveni red događaja, ali se na OS niovu obezbeđuje da događaji stignu do adekvatnih programa.
U Java programima svaka virtualna mašina ima jedan glavni AWT red događaja.
Događaji niskog nivoa (LLE =Low Level) predstavljaju direktnu komunikaciju
s korisnikom. Na primer: pritisak na taster, klikanje mišem i slično.
java.lang.Object
|
+--java.util.EventObject
|
+--java.awt.AWTEvent
|
+--java.awt.event.ComponentEvent
|
+--java.awt.event.InputEvent
| |
| +--java.awt.event.KeyEvent
| |
| +--java.awt.event.MouseEvent
|
+--java.awt.event.FocusEvent
|
+--java.awt.event.ContainerEvent
|
+--java.awt.event.WindowEvent
Uključene su klase:
java.awt.event.ComponentEvent
komponenta premeštena, promenjena veličina
komponente
java.awt.event.FocusEvent
komponenta dobila ili izgubila fokus
java.awt.event.KeyEvent
pritisnut taster, otpušten taster
java.awt.event.MouseEvent
taster miša pritisnut, podignut, kliknut, miš pomeren, miš povučen
java.awt.event.ContainerEvent
komponenta je dodana kontejneru ili uklonjena iz
njega
java.awt.event.WindowEvent
prozor aktiviran, deaktiviran, otvoren, zatvoren,
ikonificiran, deikonificiran
Događaji visokog nivoa (HLE) ili semantički događaji sadrže u sebi značenje koje je pridruženo komponenti korisničkog interfejsa.
Uključene su klase:
java.awt.event.ActionEvent
izvršena je naredba
java.awt.event.AdjustmentEvent
prilagođena je vrednost (npr. scrollbarom)
java.awt.event.ItemEvent
stanje stavke se promenilo
java.awt.event.TextEvent
vrednost tekstualnog objekta se promenila
Na primer, kad korisnik klikne mišem na GUI dugme i zatim ga otpusti, GUI dugme će primiti tri odvojena događaja nižeg nivoa, tipa MouseEvent (jedan za pritisak tastera miša-mouse down, drugi za otpuštanje tastera-mouse up, treći za eventualno nastalo povlačenje miša-mouse drag). GUI dugme će nakon toga obraditi jedan događaj višeg nivoa, tipa ActionEvent.
Ako korisnik klikne mišem na GUI dugme, pomeri miša van GUI dugmeta
i onda ga otpusti, GUI dugme će dobiti dva odvojena događaja nižeg nivoa
(jedan za mouse down, drugi za mouse drag). Zato će ih ignorisati
i neće učiniti ništa.
Svaka klasa događaja visokog nivoa je podklasa od java.awt.AWTEvent.
java.lang.Object
|
+--java.util.EventObject
|
+--java.awt.AWTEvent
|
+--java.awt.event.ActionEvent
|
+--java.awt.event.ItemEvent
|
+--java.awt.event.AdjustmentEvent
|
+--java.awt.event.TextEvent
|
+--java.awt.event.ComponentEvent
|
+--java.awt.event.InputEvent
| |
| +--java.awt.event.KeyEvent
| |
| +--java.awt.event.MouseEvent
|
+--java.awt.event.FocusEvent
|
+--java.awt.event.ContainerEvent
|
+--java.awt.event.WindowEvent
Za obradu događaja iz reda događaja zadužena je Java runtime. Posebno, ona obezbeđuje da svaki događaj niskog nivoa stigne do odgovarajuće komponente. Java programeri se ne moraju starati o tome kojoj komponenti je koji događaj namenjen, jer OS to rešava automatski. Specijalno, runtime prosleđuje događaj metodi processEvent() klase java.awt.Component:
protected void processEvent(AWTEvent e)
Metoda processEvent() prepoznaje tip događaja i prosleđuje ga jednoj od pet drugih metoda iz iste klase:
protected void processComponentEvent(ComponentEvent e)
protected void processFocusEvent(FocusEvent e)
protected void processKeyEvent(KeyEvent e)
protected void processMouseEvent(MouseEvent e)
protected void processMouseMotionEvent(MouseEvent e)
Svaka od tih metoda pokušava videti da li je neki listener odgovarajućeg tipa registrovan za tu komponentu. Ako jeste, događaj se prosleđuje svakom od registrovanih listenera nepredvidljivim poretkom.
Interno, ove metode koriste objekt tipa java.awt.AWTEventMulticaster kako bi vodile popis registracija listenera uz svaku komponentu.
java.awt.event.ComponentListener
java.awt.event.ContainerListener
java.awt.event.FocusListener
java.awt.event.KeyListener
java.awt.event.MouseListener
java.awt.event.MouseMotionListener
java.awt.event.WindowListener
java.awt.event.ActionListener
java.awt.event.AdjustmentListener
java.awt.event.ItemListener
java.awt.event.TextListener
java.awt.event.AWTEventListener
java.awt.event.HierarchyBoundsListener
java.awt.event.HierarchyListener
java.awt.event.InputMethodListener
Svako od tih interfejsa definiše događaje na koje event listener tog tipa mora biti spreman odgovoriti. Na primer, MouseListener deklariše sledeće metode:
public abstract void mouseClicked(MouseEvent e)
public abstract void mousePressed(MouseEvent e)
public abstract void mouseReleased(MouseEvent e)
public abstract void mouseEntered(MouseEvent e)
public abstract void mouseExited(MouseEvent e)
Kada, na primer, komponenta dobije događaj tipa MouseEvent, njena metoda
processMouseEvent() proverava njegov ID da bi ustanovila da li je miš kliknut,
pritisnut, otpušten, uveden ili izveden. Tada poziva odgovarajuću metodu
u svakom registrirovanom objektu koji implementira MouseListener.
Primer apleta koji crta crveni krug oko mesta gde korisnik klikne mišem.
Kako java.applet.Applet jeste podklasa od java.awt.Component, onda
da bi aplet odgovarao na klikanje mišem, uz njega mora biti registrovan
odgovarajući MouseListener. U takvim apletima najjednostavnije je da sam
applet bude MouseListener. Koordinatni sistem se odnosi na komponentu kojoj
je događaj namenjen, ne nužno na ceo aplet.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class OkoKlika extends Applet implements MouseListener {
/* koordinate pokreta misa */ int x,y;
int brojKlika = 0;
public void init() { this.addMouseListener(this); }
public void mouseClicked(MouseEvent e) {
x = (int) e.getX(); y = (int)
e.getY();
brojKlika++;
this.repaint();
}
// Metodi koje Java programeri moraju da implementiraju, ali
koje ne moraju da urade bilo kakve korisne akcije,
//te je telo prazno
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
// iscrtavanje tackica
public void paint(Graphics g) {
g.setColor(Color.red);
if ( brojKlika > 0 ) g.fillOval(x, y,
28, 28);
}
}
<APPLET CODE="OkoKlika.class" WIDTH=200 HEIGHT=200> </APPLET>
Primer apleta koji crta do 20 tacaka kada korisnik klikne mišem.
Aplert radi sa dogadjajem mouseDown tako da inicajlnp prazan ekran
miruje i čeka akciju korisnika. Svaki klik korisnik na prozor apleta crta
plavu tačku.
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
public class MisTacke extends java.applet.Applet {
final int MAXSPOTS = 20;
int xtacke[] = new int[MAXSPOTS];
int ytacke[] = new int[MAXSPOTS];
int i = 0; /* brojacka promenljiva,
red. br. tekuce tacke */
public void init() {
setBackground(Color.white);
}
public boolean mouseDown(Event evt, int x, int y)
{
if (i < MAXSPOTS) {
dodajTacku(x,y);
return true;
}
else {
System.out.println("Mnogo tackica.");
return false;
}
}
void dodajTacku(int x,int y) {
xtacke[i] = x;
ytacke[i] = y;
i++;
repaint();
}
public void paint(Graphics g) {
g.setColor(Color.blue);
for (int k = 0; k < i;
k++) {
g.fillOval(xtacke[k] - 10, ytacke[k] - 10, 20, 20);
}
}
}
<APPLET CODE="MisTacke.class" WIDTH=250 HEIGHT=250> </APPLET>
Primer apleta koji crta prava linije u prozoru tako da se nalaze izmedju
do 10 tacaka selektovanih kada korisnik klikne mišem.
Aplert radi sa dogadjajem mouseDown kao u prethodnom primerutako da
inicajlno prazan ekran miruje i čeka akciju korisnika.
Ali, ya rayliku od prethodnog apleta, aplet Linije ne cuva podatke
pojedinih celobrojnih koordinata tacki, vec cuva podatke o objektima tacaka
(Point). Za rad sa tačkama koristi se klasa Point (java.awt.Point).
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Point;
public class Linije extends java.applet.Applet {
final int MAXLinije = 10;
Point startneTacke[] = new Point[MAXLinije]; //
start Tacke
Point zavrsneTacke[] = new Point[MAXLinije];
// kraj Tacke
Point polazna; // start tekuce
linije
Point tekTacke; // tekuci kraj linije
int ukupno = 0; // broj linija
public void init() {
setBackground(Color.white);
}
public boolean mouseDown(Event evt, int x, int y)
{
if (ukupno < MAXLinije)
{
polazna = new Point(x,y);
return true;
}
else {
System.out.println("Previse linija.");
return false;
}
}
public boolean mouseUp(Event evt, int x, int y) {
if (ukupno < MAXLinije)
{
dodajLiniju(x,y);
return true;
}
else return false;
}
public boolean mouseDrag(Event evt, int x, int y)
{
if (ukupno < MAXLinije)
{
tekTacke = new Point(x,y);
repaint();
return true;
}
else return false;
}
void dodajLiniju(int x,int y) {
startneTacke[ukupno] = polazna;
zavrsneTacke[ukupno] = new
Point(x,y);
ukupno++;
tekTacke = null;
polazna = null;
repaint();
}
public void paint(Graphics g) {
// crta postojece Linije
for (int i = 0; i < ukupno;
i++) {
g.drawLine(startneTacke[i].x, startneTacke[i].y,
zavrsneTacke[i].x, zavrsneTacke[i].y);
}
// crta tekucu liniju
g.setColor(Color.blue);
if (tekTacke != null)
g.drawLine(polazna.x,polazna.y,
tekTacke.x,tekTacke.y);
}
}
KeyEvent.KEY_PRESSED
Taster je pritisnut
KeyEvent.KEY_RELEASED
Taster je otpušten
KeyEvent.KEY_TYPED
Taster je pritisnut i zatim otpušten
Uz neki KeyEvent je u većini slučaja potrebno saznati i koji taster je pritisnut, što se saznaje pomoću metode getKeyChar():
public char getKeyChar()
Ona vraća Unicode znak koji odgovara pritisnutom tasteru.
Događaji kao KEY_PRESSED ili KEY_RELEASED ne nose samo znak. Oni takođe imaju i kôd tastera. (Za razliku od njih, događaji KEY_TYPED nemaju kôd, odnosno, kôd im je nedefinisan). Ako za neki KeyEvent trebate saznati koji taster je pritisnut, a ne koji je znak upisan, koristan je metod getKeyCode():
public int getKeyCode()
Moguće je izvršiti i konverziju u lokalizovani string kao na primer "END", "F4" ili "Q" pomoću statičke metode KeyEvent.getKeyText():
public static String getKeyText(int keyCode)
Po pravilu na događaje vezanih za tastaturu odgovara se u samoj komponenti, tako da se uz nju registriruje neki KeyListener. Interfejs KeyListener deklariše sledeće metode, po jednu za svaki tip KeyEventa.
public abstract void keyTyped(KeyEvent e)
public abstract void keyPressed(KeyEvent e)
public abstract void keyReleased(KeyEvent e)
Nekonzistentnost tastatura je je jedan od problema s kojima se multiplatformski programski jezik mora na suočiti. Nisu sve tastature konzistene, jer npr. Mac ima tastere za naredbe i opcije, PC ima Alt tasere. Neki imaju numeričku tastaturu, neki ne. Neki imaju Start taser, neki ne. Emacs očekuje Meta taster koja retko postoji kao zaseban, već kao Escape taster...
Klasa Java.awt.event.KeyEvent definiše nešto više od stotinu virtualnih kôdova za tastere koji se mapiraju na različite, uvek prisutne, tastere. KeyEvent.VK_0 do KeyEvent.VK_9 su efekti znakovnih konstanti '0' do '9' (ASCII: 0x30 - 0x39)
KeyEvent.VK_0
KeyEvent.VK_1
KeyEvent.VK_2
KeyEvent.VK_3
KeyEvent.VK_4
KeyEvent.VK_5
KeyEvent.VK_6
KeyEvent.VK_7
KeyEvent.VK_8
KeyEvent.VK_9
KeyEvent.VK_A do KeyEvent.VK_Z su isti kao ASCII 'A' do 'Z'; dakle,
postoji KeyEvent.VK_A, KeyEvent.VK_B, KeyEvent.VK_C, KeyEvent.VK_D,
KeyEvent.VK_E, KeyEvent.VK_F itd.
Tasteri kôdovi od značaja:
KeyEvent.VK_ACCEPT
KeyEvent.VK_ADD
KeyEvent.VK_ALT
KeyEvent.VK_BACK_QUOTE
KeyEvent.VK_BACK_SLASH
KeyEvent.VK_BACK_SPACE
KeyEvent.VK_CANCEL
KeyEvent.VK_CAPS_LOCK
KeyEvent.VK_CLEAR
KeyEvent.VK_CLOSE_BRACKET
KeyEvent.VK_COMMA
KeyEvent.VK_CONTROL
KeyEvent.VK_CONVERT
KeyEvent.VK_DECIMAL
KeyEvent.VK_DELETE
KeyEvent.VK_DIVIDE
KeyEvent.VK_DOWN
KeyEvent.VK_END
KeyEvent.VK_ENTER
KeyEvent.VK_EQUALS
KeyEvent.VK_ESCAPE
KeyEvent.VK_F1
KeyEvent.VK_F2
KeyEvent.VK_F3
KeyEvent.VK_F4
KeyEvent.VK_F5
KeyEvent.VK_F6
KeyEvent.VK_F7
KeyEvent.VK_F8
KeyEvent.VK_F9
KeyEvent.VK_F10
KeyEvent.VK_F11
KeyEvent.VK_F12
KeyEvent.VK_FINAL
KeyEvent.VK_HELP
KeyEvent.VK_HOME
KeyEvent.VK_INSERT
KeyEvent.VK_KANA
KeyEvent.VK_KANJI
KeyEvent.VK_LEFT
KeyEvent.VK_META
KeyEvent.VK_MODECHANGE
KeyEvent.VK_MULTIPLY
KeyEvent.VK_NONCONVERT
KeyEvent.VK_NUM_LOCK
KeyEvent.VK_NUMPAD0
KeyEvent.VK_NUMPAD1
KeyEvent.VK_NUMPAD2
KeyEvent.VK_NUMPAD3
KeyEvent.VK_NUMPAD4
KeyEvent.VK_NUMPAD5
KeyEvent.VK_NUMPAD6
KeyEvent.VK_NUMPAD7
KeyEvent.VK_NUMPAD8
KeyEvent.VK_NUMPAD9
KeyEvent.VK_OPEN_BRACKET
KeyEvent.VK_PAGE_DOWN
KeyEvent.VK_PAGE_UP
KeyEvent.VK_PAUSE
KeyEvent.VK_PERIOD
KeyEvent.VK_PRINTSCREEN
KeyEvent.VK_QUOTE
KeyEvent.VK_RIGHT
KeyEvent.VK_SCROLL_LOCK
KeyEvent.VK_SEMICOLON
KeyEvent.VK_SEPARATER
KeyEvent.VK_SHIFT
KeyEvent.VK_SLASH
KeyEvent.VK_SPACE
KeyEvent.VK_SUBTRACT
KeyEvent.VK_TAB
KeyEvent.VK_UNDEFINED
KeyEvent.VK_UP
Obe klase, KeyEvent i MouseEvent su podklase od java.awt.event.InputEvent čiji glavni zadatak je testiranje dodatnih slova u smislu da li je taster ALT pritisnut zajedno s nekim drugim, ili SHIFT taster istovremeno s mišem i slično.
Sljedeće četiri metode testiraju da li je ili nije pritisnut određen
taster u trenutku kad je događaj poslan.
public boolean isShiftDown()
public boolean isControlDown()
public boolean isMetaDown()
public boolean isAltDown()
Sve se one mogu pozivati i uz MouseEvent i uz KeyEvent objekte.
Postoji i metod getWhen() koji vraća vreme kad je događaj nastao. Vreme se zadaje u milisekundama od ponoći 1.1.1970. UTC.
public long getWhen()
Klase java.util.Date i java.util.Calendar imaju metode pomoću kojih programer može izvršiti konverziju u tradicinalan način zapisivanja datum i vremena. No najčešće će programera zapravo zanimati samo vremenska razlika između dva događaja.
Primer apleta koji u centru prozora prikazuje karakter koji je otkucao korisnik. Karakter se može po ekranu koristeći kursorske tastere za levo. desno, naviše, nadole. Prikazani karakter menja se kucanjem sledećeg karaktera.
import java.awt.Graphics;
import java.awt.Event;
import java.awt.Font;
import java.awt.Color;
public class Tasteri extends java.applet.Applet {
char tekTaster;
int xTastera;
int yTastera;
public void init() {
xTastera = (size().width
/ 2) -8; // podrazumevane
yTastera = (size().height
/ 2) -16;
setBackground(Color.white);
setFont(new Font("Helvetica",Font.BOLD,24));
requestFocus();
}
public boolean keyDown(Event evt, int taster) {
switch (taster) {
case Event.DOWN:
yTastera += 5;
break;
case Event.UP:
yTastera -= 5;
break;
case Event.LEFT:
xTastera -= 5;
break;
case Event.RIGHT:
xTastera += 5;
break;
default:
tekTaster = (char)taster;
}
repaint();
return true;
}
public void paint(Graphics g) {
if (tekTaster != 0) {
g.drawString(String.valueOf(tekTaster), xTastera,yTastera);
}
}
}
InputEvent.SHIFT_MASK
InputEvent.CTRL_MASK
InputEvent.META_MASK
InputEvent.ALT_MASK
InputEvent.ALT_GRAPH_MASK
InputEvent.BUTTON1_MASK
InputEvent.BUTTON2_MASK
InputEvent.BUTTON3_MASK
Moguće ih je saznati pomoću metode getModifiers():
public int getModifiers()
Koristite bitovski operator & kad želite testirati da li je neki flag podignut. Npr,
if (e.getModifiers() & InputEvent.BUTTON2_MASK != 0) { System.out.println("Pritisnut
2. taster");}
U svakom trenutku točno jedna komponenta u apletu ima fokus, odnosno mogućnost primanja ulaza s tastature ili miša. Događaji niskog nivoa će u tom trenutku biti usmereni prema toj komponenti.
Fokus je moguće podesiti na više načina. Npr, kad korisnik pritisne taster Tab, fokus će biti premešten s dotadašnje komponente na sledeću. Ako korisnik pritisne Shift-Tab, fokus će se vratiti na prethodnu komponentu. Ako korisnik izabere komponentu mišem, ona će dobiti fokus.
Bez obzira na koji način komponenta dobije ili izgubi fokus, ona uzrokuje pojavu događaja tipa java.awt.event.FocusEvent. Promena fokusa može biti stalna ili privremena. Stalna promena nastaje kad je fokus direktno premešten s jedne komponente na drugu, bilo pozivanjem komponentine metode requestFocus() ili akcijom korisnika, npr. pomoću Tab tastera. Privremena promena fokusa nastaje kao indirektni rezultat druge operacije, na primer deaktiviranja prozora. U tom slučaju početno stanje fokusa će biti automatski restaurirano kad se operacija završi ili prozor opet aktivira.
Metoda isTemporary() vraća true ako je promena fokusa privremena, a false ako je stalna:
public boolean isTemporary()
Na događaje vezane uz promenu fokusa programer može odgovoriti ako uz
komponentu instalira java.awt.event.FocusListener.
Interfejs deklariše dva metoda:
public abstract void focusGained(FocusEvent e)
public abstract void focusLost(FocusEvent e)
Klasa java.awt.event.ComponentEvent je nadklasa svim klasama događaja koji su do sad pominjani. Ona se odnosi i na nekoliko sopstvenih događaja koji omogućuju reaakciju nakon što se komponenta prikaže, sakrije, premesti ili joj se promeni veličina. Oni su predstavljeni konstantama:
ComponentEvent.COMPONENT_MOVED
ComponentEvent.COMPONENT_RESIZED
ComponentEvent.COMPONENT_SHOWN
ComponentEvent.COMPONENT_HIDDEN
Kao i obično, na te događaje programer može odgovarati ako uz svoju komponentu registruje odgovarajući objekt iz klase koja implementira java.awt.event.ComponentListener u kom su deklarisane četiri metode:
public abstract void componentResized(ComponentEvent
e)
public abstract void componentMoved(ComponentEvent
e)
public abstract void componentShown(ComponentEvent
e)
public abstract void componentHidden(ComponentEvent
e)
Klasa java.awt.event.ComponentEvent nema neke sopstvene naročito korisne
metode, ali programer može koristiti metode iz klase java.awt.Component
kad želite ustanoviti lokaciju na koju je komponenta pomerena ili na koju
veličinu je preoblikovana.
--------------------------------------------------------------------------------
Adapteri
Adapteri su klase koje implementiraju sve metode interfejsa tako da
sve deklarisane metode interfejsa prekriju metodama koje ne čine ništa.
AWT osigurava više adapterskih klasa za različite vrste EventListenera.
To su:
ComponentAdapter
ContainerAdapter
FocusAdapter
KeyAdapter
MouseAdapter
MouseMotionAdapter
WindowAdapter
Na primer, interface MouseListener deklariše sledeće metode:
public abstract void mouseClicked(MouseEvent e)
public abstract void mousePressed(MouseEvent e)
public abstract void mouseReleased(MouseEvent e)
public abstract void mouseEntered(MouseEvent e)
public abstract void mouseExited(MouseEvent e)
Zato odgovarajući adapter, java.awt.event.MouseAdapter izgleda ovako:
package java.awt.event;
import java.awt.*;
import java.awt.event.*;
public class MouseAdapter implements MouseListener {
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
Ako sada napravite podklasu klase MouseAdapter umesto da direktno implementirate
MouseListener, izbeći će se pisanje metoda koje zapravo i ne trebaju. Pregaziće
se jedino one metode koje stvarno želite implementirati. Adapteri su svojevrsno
pojednostavnjenje posla prilikom implementacije interface-a. Mogu se koristiti
ako želi programer, ali i ne moraju.
Primer upotrebe adaptera
Adapter za miša koji proizvodi signal zvona kad korisnik klikne mišem.
import java.applet.Applet;
public class MisZvono extends Applet {
public void init() {
MouseBeeper mb = new MouseBeeper();
this.addMouseListener(mb);
}
}
import java.awt.*;
import java.awt.event.*;
public class MouseBeeper extends MouseAdapter
{ public void mouseClicked(MouseEvent e)
{ Toolkit.getDefaultToolkit().beep();
}
}
<APPLET CODE="MisZvono.class" WIDTH=200 HEIGHT=200>
</APPLET>
Kad MouseBeeper ne bi bio podklasa od MouseAdapter morao bi izgledati ovako:
import java.awt.*;
import java.awt.event.*;
public class MouseBeeper implements MouseListener {
public void mouseClicked(MouseEvent
e)
{
Toolkit.getDefaultToolkit().beep(); }
public void mousePressed(MouseEvent
e) {}
public void mouseReleased(MouseEvent
e) {}
public void mouseEntered(MouseEvent
e) {}
public void mouseExited(MouseEvent e) {}
}