Aplety

Transkrypt

Aplety
Aplety
Java jest najczęściej kojarzona z narzędziem przeznaczonym do pisania specjalnych
programów tzw. apletów, umieszczanych na stronach WWW. Do tej pory, struktura Javy
omawiana była na przykładzie aplikacji. W tym rozdziale zajmiemy się strukturą i sposobem
pisania apletów.
Aplety od aplikacji róŜni środowisko, w którym są wykonywane, struktura programu oraz
sposób wykonania. W aplikacji działanie rozpoczyna się od metody main, natomiast aplet nie
zaczyna działania od metody main lecz ma swoją strukturę przedstawioną na rysunku: Cykl
Ŝycia Apletu.
Przykładowa prosta aplikacja w Javie wypisująca na ekranie słowa: "Hello World" ma postać:
class HelloWorldApp
{
public static void main(String[] args)
{
//Wyświetla na ekanie string
System.out.println("Hello World!");
}
}
Natomiast aplet, który ma zrobić to samo, wygląda następująco:
import java.applet.Applet;
import java.awt.Graphics;
public class HelloWorld extends Applet
{
public void paint(Graphics g)
{
g.drawString("Hello world!", 50, 25);
}
}
Umieszczenie apletu na stronie WWW wymaga dodania nowego znacznika (ang. tag)
<APPLET> . . . </APPLET> .
Plik HTML z apletem Javy HelloWorld przyjmuje zatem postać:
<HTML><HEAD><TITLE> Przykładowy aplet </TITLE></HEAD>
<BODY>Tutaj jest wynik działania mojego apletu:
<APPLET CODE="HelloWorld.class" WIDTH=150 HEIGHT=25></APPLET>
</BODY>
</HTML>
gdzie znacznik <APPLET> ma m.in. następujące atrybuty:
•
•
CODE - określa nazwę pliku z kodem bajtowym apletu,
WIDTH i HEIGHT- początkowa szerokość i wysokość okna na stronie WWW, w
którym aplet będzie wykonywany,
•
CODEBASE - określa ścieŜkę do katalogu zawierającego plik z kodem bajtowym
apletu,
Po załadowaniu strony HTML do przeglądarki Internetowej na załadowanej stronie
zobaczymy wynik działania apletu:
Ilustracja 2-12 Wynik działania apletu HelloWorld.
KaŜdy aplet dziedziczy z klasy java.applet.Applet: posiada więc metody zdefiniowane w tej
klasie i odziedziczone z nadklas klasy java.lang.Applet co zobrazowano na poniŜszym
rysunku.
Rysunek 2-7 Hierarchia dziedziczenia klasy Applet
Cykl Ŝycia apletu
Do głównych metod odpowiedzialnych za przepływ sterowania podczas działania apletu
naleŜą: init, start, stop, destroy oraz metoda paint. W przykładowym aplecie, mamy
redefinicję tylko jednej metody: paint (pozostałe są dziedziczone z klasy java.applet.Applet),
która jest wykonywana za kaŜdym razem, gdy zaistnieje potrzeba wykreślenia apletu (metoda
paint, zostanie omówiona dalej w tym rozdziale).
Znaczenie pozostałych metod jest następujące:
•
•
•
•
init - wywoływana tylko raz, gdy strona WWW zawierająca aplet zostanie po raz
pierwszy "załadowana", jeśli opuścimy stronę WWW zawierającą aplet i wrócimy na
nią, metoda init nie będzie wykonana ponownie,
start - metoda jest wykonywana za kaŜdym razem, gdy strona, na której znajduje się
aplet, staje się stroną bieŜącą w przeglądarce,
stop - metoda ta jest wykonywana za kaŜdym razem, gdy do przeglądarki ładowana
jest następna strona WWW,
destroy - wykonywana gdy aplet kończy swoje działanie.
Nie kaŜdy aplet musi redefiniować (ang. override) którąś z tych metod. Dla przykładu, prosty
aplet zdefiniowany wcześniej nie redefiniuje Ŝadnej z tych metod, dlatego, Ŝe słuŜy on tylko
do rysowania na ekranie słów "Hello World!".
Metoda init powinna zawierać kod, który zazwyczaj znajduje się w konstruktorze. Jest tak
dlatego, poniewaŜ w konstruktorze apletu nie jest zagwarantowane, Ŝe dostępne jest całe
środowisko potrzebne do inicjalizacji apletu. Przykładowo, metoda ładująca obrazki nie jest
dostępna w konstruktorze apletu.
Zazwyczaj aplet, musi redefiniować metodę start. Metoda start odpowiada za wykonanie
pracy apletu, lub uruchamia wątki, które wykonują pracę apletu.
Większość apletów, które redefiniują metodę start powinny takŜe redefiniować metodę stop.
Metoda stop wstrzymuje wykonanie apletu, tak więc zasoby systemowe, zajęte przez aplet,
nie są zwracane nawet, jeśli uŜytkownik nie ogląda właśnie strony zawierającej aplet.
Przykładowo, aplet wyświetlający animację powinien w metodzie stop zatrzymać próby
rysowania, gdy uŜytkownik ogląda juŜ następną stronę.
Metody start i stop często uŜywane są do tworzenia, uruchamiania oraz odpowiednio
zatrzymywania i niszczenia wątków (patrz ).
Wiele apletów nie uŜywa metody destroy, poniewaŜ metoda stop przewaŜnie wykonuje
wszystkie operacje potrzebne do zakończenia działania apletu. Mimo tego, uŜycie metody
destroy jest moŜliwe w przypadku apletów, które tego jednak wymagają. Stosując metodę
destroy moŜna, zwolnić zasoby zarezerwowane w metodzie init;
Rysunek 2-8 Cykl Ŝycia Apletu
Ilustracją cyklu Ŝycia apletu jest następujący program:
Przykład 2.28 Kod apletu AppletLifeCycle
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Font;
public class AppletLifeCycle extends Applet
{
private int m_nInit;
private int m_nStart;
private int m_nStop;
private int m_nDestroy;
private int m_nPaint;
private Font font;
public void init()
{
font = new Font("Arial", Font.BOLD + Font.ITALIC, 24);
m_nInit = dodajInfo(m_nInit);
}
public void start()
{
m_nStart = dodajInfo(m_nStart);
}
public void stop()
{
m_nStop = dodajInfo(m_nStop);
}
public void destroy()
{
m_nDestroy = dodajInfo(m_nDestroy);
}
int dodajInfo(int i)
{
repaint();
return ++i;
}
public void paint(Graphics g)
{
g.setFont(font);
//Rysowanie ramki otaczającej aplet
g.drawRect(0, 0, size().width - 1, size().height - 1);
//rysowanie odpowiednich łańcuchów znakowych (w ramce)
g.drawString("m_nInit " + m_nInit,10,20);
g.drawString("m_nStart " + m_nStart,10,40);
g.drawString("m_nStop " + m_nStop,10,60);
g.drawString("m_nDestroy " + m_nDestroy,10,80);
g.drawString("m_nPaint " + ++m_nPaint,10,100);
}
}
PoniŜej przedstawiono efekt przykładowego uruchomienia powyŜszego apletu w środowisku
programu Netscape Communicator 4.0.
Ilustracja 2-13 Jeden z moŜliwych rezultatów wykonania apletu AppletLifeCycle
Metody odpowiadające za rysowanie w aplecie
Jak juŜ wspomniano, za rysowanie odpowiedzialna jest metoda paint (np. aplet HelloWorld
lub aplet AppletLifeCycle w poprzednim punkcie).
Oprócz metody paint moŜemy takŜe redefiniować metodę update, która "czyści" kolorem tła
obszar, na którym aplet rysuje a następnie inicjuje wykonanie metody paint.
Brak redefinicji update moŜe spowodować niekiedy migotanie rysowanego w metodzie paint
obszaru. Jednym ze sposobów wyeliminowania tego niepoŜądanego efektu jest redefinicja
metody update tak, aby "czyściła" tylko te elementy, które mają być usunięte.
Spójrzmy na przykładowy aplet:
Ilustracja 2-14 Efekt działania apletu MojRysownik.
Po uruchomieniu apletu w przeglądarce widzimy przesuwające się napisy: "animowany aplet
Javy" (wraz z kółkami) między lewą a prawą krawędzią apletu oraz "autor: ARTUR
TYLOCH" między górną a dolną krawędzią.
W poniŜszym przykładzie metody update i paint zdefiniowano tak, aby ograniczyć migotanie.
Metoda update "czyści" niepotrzebne elementy starego rysunku (a nie jak standardowo cały
rysunek), a następnie wykreśla nowy rysunek. PoniewaŜ wszystkie operacje związane z
wykreślaniem znajdują się w update, metoda paint zawiera tylko wywołanie metody update.
Jest to potrzebne dla przypadków, gdy metoda paint jest wołana bezpośrednio przez
bibliotekę AWT (np. gdy okno z apletem zostaje przesłonięte przez inne okno).
Klasa MojRysownik implementuje takŜe interfejs Runnable co umoŜliwia, uruchomienie
wątku, który będzie obliczał nowe współrzędne potrzebne przy rysowaniu animacji.
Przykład 2.29 Aplet MojRysownik
A oto kod HTML strony zawierającej aplet:
<html><head><title>MojRysownik</title></head>
<body><hr>
<applet
code=MojRysownik.class
name=MojRysownik
width=300
height=100 >
</applet>
<hr><a href="MojRysownik.java">The source.</a>
</body></html>
i kod zawartego na niej apletu:
import java.applet.*;
import java.awt.*;
public class MojRysownik extends Applet implements Runnable
{
// współrzędne, na podstawie których wykreślane są napisy
private int m_X = 60, m_Y = 50, m_oldX, m_oldY;
// wątek obliczający dane do animacji
private Thread animWatek;
// czcionka w której wyprowadzane są na ekran napisy
private Font font;
public void init()
{
// ustawienie czcionki (typ, rodzaj, wielkość) w której
// będą wyświetlane napisy
font = new Font("Arial", Font.BOLD, 20);
m_oldX=m_X;
m_oldY=m_Y;
}
// metoda start tworzy i uruchamia wątek, którego działanie
// definiuje metoda run
public void start()
{
if (animWatek == null)
animWatek = new Thread(this,"Rysownik");
animWatek.start();
}
// zatrzymanie i usunięcie wątku w przypadku przejścia do innej
// strony w przeglądarce
public void stop()
{
if (animWatek != null)
animWatek.stop();
animWatek=null;
}
// metoda run, oblicza współrzędne potrzebne przy kreśleniu
// napisów
public void run()
{
// zmienna lewo określa czy współrzędne mają być obliczane dla
// przesuwania napisów w lewo(oraz dół) czy w prawo(oraz góra)
boolean lewo = true;
while (Thread.currentThread() == animWatek)
{
if (m_X > 110) lewo = true;
if (m_X < 10) lewo = false;
if (lewo)
{
m_X--; m_Y--;
}
else
{
m_X++; m_Y++;
}
// po obliczeniu nowych współrzędnych Ŝądamy przerysowania
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e){
}
}
}
public void paint(Graphics g)
{
// wołamy update, poniewaŜ AWT niekiedy
// woła paint() bezpośrednio
update(g);
}
public void update(Graphics g)
{
g.setFont(font);
// jesli konieczne to usuwamy stary rysunek,
// (opisany przez współrzędne m_oldX i m_oldY)
// poprzez przemalowanie go kolorem tła
if ((m_oldX != m_X) | (m_oldY != m_Y))
{
g.setColor(getBackground() );
g.drawString("animowany aplet Javy", m_oldX, 20);
g.drawString("autor: ARTUR TYLOCH", 20, m_oldY);
kolka(g, m_oldX);
m_oldX = m_X;
m_oldY = m_Y;
}
// przerysowanie apletu
g.setColor(Color.black);
g.fillRect(10,50,280,25);
g.setColor(Color.white);
g.drawString("animowany aplet Javy", m_X, 20);
g.setColor(Color.yellow);
g.drawString("autor: ARTUR TYLOCH", 20, m_Y);
g.setColor(Color.black);
olka(g, m_X);
}
private void kolka(Graphics g, int x)
{
for (int i = 0; i<7; i++ )
g.drawOval(x+i*21,30,20,20);
}
}
W celu całkowitego usunięcia nieprzyjemnego efektu migotania obrazu na ekranie moŜemy
zastosować buforowanie. Buforowanie polega na rysowaniu do bufora a następnie
wyświetleniu jego zawartości na ekranie.
Odpowiednio zmodyfikowany aplet MojRysownik zawiera następujące zmiany:
•
w klasie MojRysownik definiujemy pola danych:
// Pole danych typu Graphics słuŜy do rysowania na obiekcie
//bufor (typu Image), jest to tzw. kontekst graficzny
// (ang. graphics context)
private Graphics buforGraf;
// Obiekt, na którym rysujemy
private Image bufor;
// Pole danych rozmiarBuf przechowuje rozmiar bufora,
// a pole d rozmiar obszaru rysowania na ekranie
private Dimension rozmiarBuf, d;
•
w ciele metody init() dodajemy:
d = this.size(); // rozmiar na którym aplet moŜe rysować
•
główne zmiany dotyczą metody update, która teraz przybiera postać:
public void update(Graphics g)
{
// zmienna d przechowuje rozmiar obszaru rysowania na ekranie;
// jeśli nie ma bufora lub obszar rysowania uległ zmianie
// tworzymy nowy bufor
if ( (buforGraf == null) || (d.width != rozmiarBuf.width) ||
(d.height != rozmiarBuf.height) )
{
rozmiarBuf = d;
bufor = createImage(d.width, d.height);
// wynikiem metody getGraphics()jest referencja do kontekstu
// graficznego obiektu bufor typu Image.
buforGraf = bufor.getGraphics();
}
// ustawienie fontu, który będzie uŜyty do rysowania
buforGraf.setFont(font);
// ustawienie jako koloru bieŜącego, koloru tła
buforGraf.setColor(getBackground());
// przemalowanie bufora kolorem tła (wyczyszczenie tła)
buforGraf.fillRect(0, 0, d.width, d.height);
//przerysowanie
buforGraf.setColor(Color.black);
buforGraf.fillRect(10,50,280,25);
buforGraf.setColor(Color.white);
buforGraf.drawString("animacja z buforowaniem", m_X, 20);
buforGraf.setColor(Color.yellow);
buforGraf.drawString("autor: arturt tyloch", 40, m_Y);
buforGraf.setColor(Color.black);
kolka(buforGraf, m_X);
//zrzucenie bufora na ekran
g.drawImage(bufor, 0, 0, this);
}
Pozostałe metody klasy MojRysownik pozostają bez zmian.
Zdarzenia
Pierwotnie, model obsługi zdarzeń w Javie (ang. Handle Event) zawarty w bibliotece AWT
(Abstract Window Toolkit) bazował na pojęciu dziedziczenia klas. Ostatnio, w bibliotece
AWT pakietu Sun'a JDK 1.1 (kwiecień 1997) wprowadzono nowy, tzw. delegacyjny model
obsługi zdarzeń, charakteryzujący się większymi moŜliwościami i wydajnością.
W okresie przejściowym oba modele są dopuszczalne, a kompilator po wykryciu tradycyjnej
obsługi zdarzeń generuje ostrzeŜenie o uŜywaniu zdezaktualizowanego modelu.
Tradycyjny model obsługi zdarzeń
PoniŜej przedstawimy przykład apletu, w którym zaimplementowano tradycyjny model
obsługi zdarzeń.
Aplet MojAplet posiada trzy przyciski słuŜące, odpowiednio, do: zwiększania, zmniejszania i
zerowania wyświetlanej przezeń liczby. PoniŜsza ilustracja przedstawia efekt działania apletu
po uruchomieniu w programie appletviewer.exe (dołączanym do pakietów JDK firmy Sun).
Ilustracja 2-15 Wygląd apletu MojAplet
W celu rozmieszczenia komponentów graficznych (przycisków i etykiety) w aplecie uŜyto
"zarządcy rozmieszczenia komponentów" (ang. Layout Manager) typu BorderLayout (uŜycie
zarządców rozkładu komponentów omówione zostanie w punkcie 2.7.1.3).
NajwaŜniejsza w tym przykładzie jest obsługa zdarzeń zdefiniowana w metodzie action.
Przykład 2.30 Obsługa zdarzeń w modelu tradycyjnym
import java.applet.*;
import java.awt.*;
public class MojAplet extends Applet
{
static int m_nWartosc=0;
Label lblWartosc = new Label("0", Label.CENTER);
Button btnPoprzednia = new Button("<< Odejmij"),
btnNastepna = new Button("Dodaj >>"),
btnZerowanie = new Button("Zerowanie");
// wynikiem wykonania tej metody jest panel zawierający dwa
// przyciski o etykietach:
// Odejmij i Dodaj, panel ten zostaje umieszczony u dołu apletu.
Panel gridLayoutPanel()
{
Panel pan = new Panel();
// Jako zarządce rozmieszczenia komponentów w panelu ustawiamy
// GridLayout o 1 wierszu i 2 kolumnach.
pan.setLayout(new GridLayout(1,2));
pan.add(btnPoprzednia);
pan.add(btnNastepna);
return pan;
}
public void init()
{
Panel panel = gridLayoutPanel();
setLayout(new BorderLayout());
add("South",panel);
add("North",btnZerowanie);
add("Center",lblWartosc);
lblWartosc.setFont(new Font("Arial", Font.BOLD, 30));
}
// Obsługa zdarzeń evt generowanych przez uŜytkownika
// (naciśnięcie przycisku)
public boolean action(Event evt, Object arg)
{
// Gdy naciśnięto przycisk btnNastępna
if (evt.target == btnNastepna)
{
lblWartosc.setText(Integer.toString(++m_nWartosc));
return true;
}
else if (evt.target == btnPoprzednia)
{
lblWartosc.setText(Integer.toString(--m_nWartosc));
return true;
}
else if (evt.target == btnZerowanie)
{
m_nWartosc = 0;
lblWartosc.setText(Integer.toString(m_nWartosc));
return true;
}
return false;
}
}
Tradycyjny model obsługi zdarzeń opiera się na dziedziczeniu, tzn. po wystąpieniu zdarzenia
dla obiektu danej klasy jest ono albo obsługiwane przez metodę action (zdarzenia generowane
przez uŜytkownika) lub handleEvent (zdarzenia generowane przez komponenty graficzne) tej
klasy lub przekazane do nadklasy w celu obsłuŜenia.
Osoby zainteresowane tradycyjnym modelem obsługi zdarzeń odsyłamy do Tutoriala
(dokument HTML) firmy Sun, dostępnego w Internecie.
Delegacyjny model obsługi zdarzeń
Nowy model zdarzeń, nazywamy delegacyjnym poniewaŜ do obsługi zdarzeń generowanych
przez Źródło, mogą być delegowane dowolne obiekty, które implementują odpowiedni
interfejs nasłuchujący (ang. listener interface). Jeden lub kilka obiektów klas nasłuchujących
moŜe zostać zarejestrowany do obsługi róŜnego rodzaju zdarzeń pochodzących z róŜnych
Źródeł. Delegacyjny model zdarzeń pozwala zarówno na obsługę jak i na generowanie
zdarzeń.
Spójrzmy jak wygląda klasa wykonująca te same zadania co, klasa z poprzedniego przykładu
ale implementująca model delegacyjny.
Nowe elementy lub zmiany zaznaczono pogrubioną czcionką a mniej istotny, powtarzający
się kod wykropkowano: "...", natomiast metodę action usunięto:
Przykład 2.31 Obsługa zdarzeń w modelu delegacyjnym
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class MojAplet extends Applet implements ActionListener
{
...
Panel gridLayoutPanel()
{
...
}
public void init()
{
...
// nowe elementy metody init:
btnPoprzednia.addActionListener(this);
btnNastepna.addActionListener(this);
btnZerowanie.addActionListener(this);
}
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == btnNastepna)
lblWartosc.setText(Integer.toString(++m_nWartosc));
else if (evt.getSource() == btnPoprzednia)
lblWartosc.setText(Integer.toString(--m_nWartosc));
else if (evt.getSource() == btnZerowanie)
{
m_nWartosc = 0;
lblWartosc.setText(Integer.toString(m_nWartosc));
}
}
}
Ogólne zasady programowania obsługi zdarzeń w modelu delegacyjnym przedstawiają się
następująco:
1. Deklaracja klasy (przeznaczonej do obsługi) implementującej odpowiedni interfejs
nasłuchujący (lub dziedziczącą z klasy, która to implementuje).
Przykład :
public class MojAplet extends Applet implements ActionListener
2. Rejestracja dla danego komponentu obiektu klasy nasłuchującej:
Ogólnie, napis:
ŹródłoZdarzeń.addRodzajListener(obiektKlasyNasłuchującej);
oznacza, Ŝe dla obsługi zdarzeń generowanych przez obiekt ŹródłoZdarzeń, zarejestrowano
obiekt obiektKlasyNasłuchującej implementujący interfejs nasłuchujący
RodzajListener.
W szczególności wywołanie:
btnPoprzednia.addActionListener(this);
jako obiekt klasy nasłuchującej rejestruje sam siebie. Jest to moŜliwe dzięki implementacji
interfejsu nasłuchującego ActionListener przez klasę MojAplet.
3. Implementacja metod z interfejsu nasłuchującego.
Interfejs ActionListener posiada tylko jedną metodę do implementacji:
public void actionPerformed(ActionEvent evt)
{
// deklaracja reakcji na zdarzenia
}
W modelu delegacyjnym mamy bogaty zestaw interfejsów nasłuchujących, metody kaŜdego z
tych interfejsów umoŜliwiają reakcję na zdarzenie określonego typu.
Zdarzenia, jakie mogą być generowane przez komponenty AWT 1.1 zestawiono w tabeli
(kliknij aby zobaczyć).
Tabela 2-6 Zdarzenia generowane przez komponenty AWT.
Natomiast interfejsy nasłuchujące i klasy adaptacyjne przedstawiono w następnej tabeli.
Interfejs nasłuchujący
Klasa adaptacyjna
Nazwa
Metody
ActionListener
actionPerformed(ActionEvent)
brak
AdjustmentListener
adjustmentValueChanged(AdjustmentEvent)
brak
ComponentListener
componentHidden(ComponentEvent)
componentMoved(ComponentEvent)
componentResized(ComponentEvent)
componentShown(ComponentEvent)
ComponentAdapter
ContainerListener
componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
ContainerAdapter
FocusListener
focusGained(FocusEvent)
focusLost(FocusEvent)
FocusAdapter
ItemListener
itemStateChanged(ItemEvent)
brak
KeyListener
keyPressed(KeyEvent) keyReleased(KeyEvent)
KeyAdapter
keyTyped(KeyEvent)
MouseListener
mouseClicked(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)
MouseAdapter
MouseMotionListener
mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
MouseMotionAdapter
TextListener
textValueChanged(TextEvent)
brak
WindowListener
windowActivated(WindowEvent)
windowClosed(WindowEvent)
windowClosing(WindowEvent)
windowDeactivated(WindowEvent)
windowDeiconified(WindowEvent)
windowIconified(WindowEvent)
windowOpened(WindowEvent)
WindowAdapter
Tabela 2-7 Zestawienie interfejsów nasłuchujących i klas adaptacyjnych.
Do rejestracji komponentów, jako przeznaczonych do obsługi wybranych zdarzeń uŜywamy
metod rejestrujących. W poniŜszej tabeli zestawiono metody rejestrujące oraz komponenty,
których dotyczą. NaleŜy pamiętać, Ŝe dana metoda rejestrująca moŜe być uŜyta dla klas
przedstawionych w tabeli oraz klas, które z nich dziedziczą (np. metoda addWindowListener
moŜe być uŜyta takŜe dla obiektów klasy Dialog lub Frame poniewaŜ są one rozszerzeniem
klasy Window).
Metoda rejestrująca
Komponent, którego dotyczą
addActionListener(ActionListener)
Button, List, TextField, MenuItem
addAdjustmentListener(AdjustmentListener)
Scrollbar
addComponentListener(ComponentListener)
Component
addContainerListener(ContainerListener)
Container
addFocusListener(FocusListener)
Component
addItemListener(ItemListener)
Choice, List, Checkbox,
CheckboxMenuItem
addKeyListener(KeyListener)
Component
addMouseListener(MouseListener)
Component
addMouseMotionListener(MouseMotionListener) Component
addTextListener(TextListener)
TextComponent
addWindowListener(WindowListener)
Window
Tabela 2-8 Wykaz metod rejestrujących
Jak juŜ wspomniano, model delegacyjny umoŜliwia obsługę róŜnego rodzaju zdarzeń
pochodzących z róŜnych Źródeł. W poniŜszym aplecie mamy przykład obsługi zdarzeń
pochodzących z róŜnych Źródeł przez jedną klasę nasłuchującą (tu jest to jednocześnie klasa
generująca zdarzenia):
(Uwaga: kod powtarzający się w odniesieniu do wcześniejszego przykładu wykropkowano)
Przykład 2.32 Obsługa wielu zdarzeń, przykład nr 2
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class MojAplet extends Applet implements ActionListener
{
...
//nowy komponent, pole tekstowe w którym będzie wyświtlana
// historia wszystkich wykonanych przez nas w aplecie operacji
TextArea txtHistoria = new TextArea(5,20);
Panel gridLayoutPanel()
{ ... }
public void init()
{
...
// nowe elementy metody init
// dodajemy na "zachodzie" apletu pole tekstowe
add("West",txtHistoria);
txtHistoria.setFont(new Font("Arial", Font.BOLD, 10));
}
public void actionPerformed(ActionEvent evt)
{
...
// w tej metodzie dodajemy jedno polecenie,
// dzieki temu mamy w polu tekstowym txtHistoria wyświetle
// wszystkie operacje przez nas wykonane
// (metoda getActionCommand)
txtHistoria.append(evt.getActionCommand()+" \t"
+.Integer.toString(m_nWartosc)+ "
");
}
}
PoniŜsza ilustracja przedstawia efekt działania powyŜszego apletu po uruchomieniu i
wykonaniu kilku operacji dodawania, odejmowania i zerowania. KaŜde naciśnięcie
dowolnego przycisku powoduje zmianę wyświetlanej liczby i dopisanie nazwy wykonanej
operacji (wraz z aktualną wartością pola m_nWartosc) do pola txtHistoria.
Ilustracja 2-16 Aplet: MojAplet w działaniu.
Obsługa zdarzeń w klasie zewnętrznej
Obsługę zdarzeń moŜna zdefiniować w oddzielnej klasie. Jest to uŜyteczne, gdy np.
definiujemy złoŜony graficzny interfejs uŜytkownika (ang. Graphic User Interface, GUI) i
moŜemy odseparować reakcję na zdarzenia, od kodu odpowiadającego za wygląd apletu.
Zwiększa to czytelność programu. Przykład takiego programowania pokazano na poniŜszym
listingu. (Jest to przekształcona klasa z poprzedniego przykładu)
Przykład 2.33 Obsługa zdarzeń w klasie zewnętrznej
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class MojAplet extends Applet
{
...
Panel gridLayoutPanel()
{
...
}
public void init()
{
...
ZdarzeniaTxt zdarzeniaTxt = new ZdarzeniaTxt(txtHistoria,
m_nWartosc, lblWartosc,
btnPoprzednia, btnNastepna, btnZerowanie);
// Rejestracja obsługi zdarzeń:
btnNastepna.addActionListener(zdarzeniaTxt);
btnPoprzednia.addActionListener(zdarzeniaTxt);
btnZerowanie.addActionListener(zdarzeniaTxt);
}
}
// klasa zewnętrzna w której zdefiniowana jest obsługa zdarzeń
// klasy MojAplet
class ZdarzeniaTxt implements ActionListener
{
int m_nWartosc;
Label lblWartosc;
Button
btnPoprzednia,
btnNastepna,
btnZerowanie;
TextArea txtHistoria;
public ZdarzeniaTxt(TextArea historia, int wartosc, Label label,
Button poprz, Button nast, Button zerow)
{
m_nWartosc = wartosc;
lblWartosc = label;
btnPoprzednia = poprz;
btnNastepna = nast;
btnZerowanie = zerow;
txtHistoria = historia;
}
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == btnNastepna)
lblWartosc.setText(Integer.toString(++m_nWartosc));
else if (evt.getSource() == btnPoprzednia)
lblWartosc.setText(Integer.toString(--m_nWartosc));
else if (evt.getSource() == btnZerowanie)
{
m_nWartosc = 0;
lblWartosc.setText(Integer.toString(m_nWartosc));
}
txtHistoria.append(evt.getActionCommand()+" \t"
+ Integer.toString(m_nWartosc) + "
");
}
}
Obsługa zdarzeń w klasie wewnętrznej
Jak widać obsługa zdarzeń w klasie zewnętrznej wymaga przekazania referencji do
wszystkich obsługiwanych komponentów i wykorzystywanych zmiennych. Aby tego uniknąć
moŜna zdefiniować obsługę zdarzeń w klasie wewnętrznej (klasy wewnętrzne dodano do Javy
w kwietniu 1997 i moŜna je stosować dla programów zgodnych z JDK 1.1).
Przykład 2.34 Obsługa zdarzeń w klasie wewnętrznej
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class MojAplet extends Applet implements ActionListener
{
...
Panel gridLayoutPanel()
{
...
}
public void init()
{
...
// Rejestracja obsługi zdarzeń
ZdarzeniaTxt zdarzeniaTxt = new ZdarzeniaTxt();
btnNastepna.addActionListener(zdarzeniaTxt);
btnPoprzednia.addActionListener(zdarzeniaTxt);
btnZerowanie.addActionListener(zdarzeniaTxt);
}
// klasa wewnętrzna w której zdefiniowano
// reakcję na zdarzenia w aplecie
class ZdarzeniaTxt implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == btnNastepna)
lblWartosc.setText(Integer.toString(++m_nWartosc));
else if (evt.getSource() == btnPoprzednia)
lblWartosc.setText(Integer.toString(--m_nWartosc));
else if (evt.getSource() == btnZerowanie)
{
m_nWartosc = 0;
lblWartosc.setText(Integer.toString(m_nWartosc));
}
txtHistoria.append(evt.getActionCommand() + " \t"
+Integer.toString(m_nWartosc)+ "
");
}
}
}
UŜycie klasy adaptacyjnej
W dotychczas przedstawionych przykładach implementowaliśmy interfejs ActionListener,
posiadający tylko jedną metodę: actionPerformed. Dla wszystkich interfejsów, które
posiadają więcej niŜ jedną metodę zdefiniowano klasy adaptacyjne. W klasach adaptacyjnych
zdefiniowano wszystkie metody związanych z nimi interfejsów nasłuchujących.
Przykładowo, dla interfejsu nasłuchującego MouseListener, musimy zadeklarować wszystkie
metody interfejsu, a klasa implementująca interfejs moŜe przybrać postać:
// W tym przykładzie interesuje nas tyko obsługa zdarzenia
// mouseClicked, musimy jednak zaimplementować, zgodnie z zasadami
// implementacji interfejsów, wszystkie metody zadeklarowane
// w interfejsie MouseListener.
MojaKlasa implements MouseListener
{
...
jakisObiekt.addMouseListener(this);
...
public void mouseClicked(MouseEvent e)
{
...//Tutaj obsługa zdarzenia mouseClicked
}
public void mousePressed(MouseEvent e)
{ /* Pusta definicja metody */ }
public void mouseReleased(MouseEvent e)
{ /* Pusta definicja metody */ }
public void mouseEntered(MouseEvent e)
{ /* Pusta definicja metody */ }
public void mouseExited(MouseEvent e)
{ /* Pusta definicja metody */
}
}
Natomiast zastosowanie klasy adaptacyjnej pozwala nam tę nadmiarowość kodu (deklaracje
metod z pustym ciałem) usunąć. Aby uŜyć klasy adaptacyjnej, deklarujemy klasę jako
podklasę klasy adaptacyjnej, zamiast deklaracji jej jako klasy implementującej interfejs
nasłuchujący.
W poniŜszym przykładzie klasa wewnętrzna ObslugaMyszy jest podklasą klasy
MouseAdapter i dzięki temu nie musimy implementować wszystkich metod interfejsu
MouseListener a tylko jedną, która nas interesuje: MousePressed.
Przykład 2.35 UŜycie klasy adaptacyjnej
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class MojAplet extends Applet
{
TextArea txtHistoria = new TextArea(5,20);
ObslugaMyszy obslugaMyszy;
Panel panel = new Panel();
Label status = new Label();
// klasa wewnętrzna ObslugaMyszy klasy MojAplet
class ObslugaMyszy extends MouseAdapter
implements MouseMotionListener
{
int oldX,oldY;
Graphics KGpanel;
ObslugaMyszy()
{ KGpanel = panel.getGraphics();
}
// redefinicja metody z klasy MouseAdapter
public void mousePressed(MouseEvent evt)
{
oldX=evt.getX();
oldY=evt.getY();
txtHistoria.append(evt.toString()+"
");
}
// implementacje metod interfejsu MouseMotionListener
public void mouseDragged(MouseEvent evt)
{
txtHistoria.append(evt.toString()+"
");
KGpanel.drawLine(oldX,oldY,evt.getX(),evt.getY());
}
public void mouseMoved(MouseEvent evt)
{ status.setText(evt.toString()); }
} // koniec definicji klasy wewnętrznej ObslugaMyszy
public void start()
{
panel.setBackground(Color.yellow);
setLayout(new BorderLayout());
add("North",txtHistoria);
add("Center",panel);
add("South",status);
obslugaMyszy = new ObslugaMyszy();
panel.addMouseListener(obslugaMyszy);
panel.addMouseMotionListener(obslugaMyszy);
}
}
Po uruchomieniu apletu, rysowane są linie o początku w miejscu gdzie naciśnięto klawisz
myszy i końcu znajdującym się na drodze kursora myszki (przy wciąŜ wciśniętym klawiszu).
Jednocześnie, w polu tekstowym wyświetlane są informacje o zdarzeniach, jakie generowane
są przez myszkę.
Ilustracja 2-17 Działanie apletu MojAplet z klasą adaptacyjną
UŜycie klas anonimowych
W przypadku, gdy jesteśmy zainteresowani obsługą przez komponent tylko jednej rodziny
zdarzeń, moŜemy uŜyć klasy anonimowej.
PoniŜszy przykład pokazuje łączne uŜycie klas adaptacyjnych i anonimowych. W przykładzie
tym, dla klasy tworzymy klasę anonimową typu KeyAdapter i redefiniujemy jej metodę
keyReleased (podobnie postępujemy z metodą mousePressed, klasy MouseAdapter).
Przykład 2.36 Wykorzystanie klas anonimowych
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class MojApletKAnonim extends Applet
{
// kontekst graficzny
na którym rysujemy
Graphics KG;
// pozycja myszki, gdzie ostatnio naciśnięto przycisk myszki
int mouseX,mouseY;
TextField txtZrodlo;
String tekst = "BLUM";
public void start()
{
setLayout(new BorderLayout());
txtZrodlo = new TextField("BLUM");
add("South",txtZrodlo);
KG = getGraphics();
txtZrodlo.addKeyListener(
new KeyAdapter()
{
public void keyReleased(KeyEvent evt)
{
tekst = txtZrodlo.getText();
KG.drawString(tekst,mouseX,mouseY);
}
}
);
addMouseListener(
new MouseAdapter()
{
public void mousePressed(MouseEvent evt)
{
mouseX = evt.getX();
mouseY = evt.getY();
if (tekst != "")
{
tekst = txtZrodlo.getText();
KG.drawString(tekst,mouseX,mouseY);
}
txtZrodlo.requestFocus();
}
}
);
}
}
Ilustracja 2-18 Wynik działania apletu MojApletKAnonim
Obsługa zdarzeń w podklasie
W modelu delegacyjnym nie musimy deklarować podklasy komponentu, tak jak w modelu
tradycyjnym, aby umoŜliwić obsługę zdarzeń dla tego komponentu. Deklarujemy tylko
odpowiednią klasę nasłuchującą i rejestrujemy obiekt tej klasy, jako obsługujący zdarzenia
pochodzące od danego komponentu.
W sytuacji jednak, gdy zachodzi potrzeba deklaracji podklasy np. komponentu Button
moŜemy umoŜliwić reakcję w tej podklasie na zachodzące zdarzenia. W tym celu uŜywamy
metody enableEvents. Wywołanie metody enableEvents przyjmuje następującą postać:
obiekt.enableEvents(AWTEvent.MaskaZdarzeń);
gdzie obiekt jest referencją do komponentu, a MaskaZdarzeń określa rodzaj zdarzeń, których
obsługa ma być dostępna dla komponentu.
Do dyspozycji mamy następujące maski zdarzeń:
ACTION_EVENT_MASK
ADJUSTMENT_EVENT_MASK
COMPONENT_EVENT_MASK
CONTAINER_EVENT_MASK
FOCUS_EVENT_MASK
ITEM_EVENT_MASK
KEY_EVENT_MASK
MOUSE_EVENT_MASK
MOUSE_MOTION_EVENT_MASK
TEXT_EVENT_MASK
WINDOW_EVENT_MASK
Gdy chcemy np. zdefiniowanemu przez nas komponentowi będącemu podklasą Button
umoŜliwić obsługę zdarzeń typu ActionEvent, deklaracja klasy komponentu przyjmuje
następującą postać:
class MojButton extends Button
{
MojButton(String etykieta)
{
super(etykieta);
// umoŜliwiamy obsługę zdarzeń typu ActionEvent
enableEvents(AWTEvent.ACTION_EVENT_MASK);
}
protected void processActionEvent(ActionEvent evt)
{
// tu deklarujemy obsługę zdarzeń typu ActionEvent
// generowanych przez nasz komponent
// moŜemy takŜe dodać super.processActionEvent(evt);
// aby umoŜliwić domyślną obsługę zdarzeń tego typu
}
}
PoniŜej podano zestawienie metod, które moŜna wykorzystać przy obsłudze zdarzeń w
podklasie.
Metoda
Komponenty których dotyczy
processEvent
processComponentEvent
processContainerEvent
wszystkie obiekty będące podklasą klasy
processFocusEvent
Component
processKeyEvent
processMouseEvent
processMouseMotionEvent
processActionEvent
Button, List, TextField, MenuItem
processItemEvent
Choice, List, Checkbox,
CheckboxMenuItem*
processTextEvent
TextComponent, TextField, TextArea
processAdjustmentEvent
Scrollbar
processWindowEvent
Dialog, Frame, Window
* To nie jest podklasa klasy Component
Tabela 2-9 Metody obsługujące zdarzenia komponentowe
Metody przedstawione w powyŜszej tabeli nie mogą być wykonane do czasu, aŜ nie spełniony
zostanie jeden z warunków:
a) obiekt nasłuchujący zostanie zarejestrowany dla odpowiedniego typu zdarzeń, (poprzez
uŜycie metody addTypZdarzeńListener() )
b) zdarzenia obsługiwane przez metodę staną się dostępne dzięki uŜyciu metody
enableEvents() z odpowiednią maską zdarzeń.
Ograniczenia i moŜliwości apletów
Jednym z załoŜeń Javy jest to aby uŜytkownik uruchamiając aplet w przeglądarce miał
pewność, Ŝe jego system nie zostanie zaatakowany (przez wirusy lub próba nieuprawnionego
dostępu do zasobów systemu). W tym celu nałoŜono na działanie apletu pewne ograniczenia
związane z bezpieczeństwem. W procesie projektowania apletu naleŜy te ograniczenia wziąć
pod uwagę.
KaŜda przeglądarka zdolna do uruchamiania apletów Javy posiada obiekt zarządcy
bezpieczeństwa (ang. SecurityManager), który sprawdza, czy aplet nie narusza zasad
bezpieczeństwa. Gdy aplet naruszy zasady bezpieczeństwa, generowany jest przez zarządcę
bezpieczeństwa wyjątek typu SecurityException. Wyjątek ten moŜe być przechwycony przez
aplet i moŜe zostać podjęta próba "bezpieczniejszego" wykonania zadania.
Aplety muszą spełniać następujące zasady bezpieczeństwa (spełnienie tych zasad jest
kontrolowane przez zarządców bezpieczeństwa poszczególnych przeglądarek).
•
Aplety nie mogą ładować bibliotek lub definiować podprogramów (metod native).
Aplety mogą uŜywać tylko swego własnego kody Javy oraz elementów Java API
dostarczonych przez przeglądarkę. KaŜda przeglądarka apletów musi udostępnić Java
API zdefiniowane w pakietach java.*.
•
•
•
•
Aplety nie mogą w zwykły sposób czytać i zapisywać plików na komputerze, na
którym są uruchomione. Aplety w przeglądarce mogą czytać pliki wyspecyfikowane
przez URL, zamiast nazwy pliku. Aby zapisać dane na komputerze, z którego aplet
załadowano (serwer) moŜna wysłać dane do aplikacji uruchomionej na serwerze, która
moŜe swobodnie czytać i zapisywać dane na serwerze.
Aplety nie mogą tworzyć połączeń sieciowych poza połączeniami z serwerem, z
którego pochodzą. Nasz aplet moŜe jednak kontaktować się z innymi serwerami
poprzez komunikację z aplikacją działającą na serwerze, z którego aplet załadowano.
Aplikacja moŜe bowiem tworzyć swobodnie połączenia sieciowe.
Aplet nie moŜe uruchamiać Ŝadnych programów na komputerze, na którym został
uruchomiony. Tu takŜe rozwiązaniem moŜe być współpraca z aplikacją pracującą na
serwerze.
Aplet nie moŜe czytać wszystkich właściwości systemu (ang. system properities).
Aplety mogą czytać tylko następujące właściwości systemu:
nazwa właściwości znaczenie
"file.separator"
Separator plików np. "/"
"java.class.version"
Liczba oznaczająca wersję klasy
Javy
"java.vendor"
nazwa dostarczyciela Javy
"java.vendor.url"
URL dostarczyciela Javy
"java.version"
Wersja maszyny wirtualnej Javy
"line.separator"
Separator linii
"os.arch"
Architektura systemu operacyjnego
"os.name"
Nazwa systemu operacyjnego
"path.separator"
Separator ścieŜki np. ":"
Tabela 2-10 Zestawienie właściwości systemu dostępnych do odczytu dla apletów
Okna stworzone przez aplet wyglądają inaczej niŜ te, które stworzyła aplikacja. Okno apletu
ma u dołu tekst ostrzegawczy (tu brzmi on: "Java Applet Window") lub innego rodzaju
informacje pozwalające odróŜnić uŜytkownikowi okno apletu od okna aplikacji, do której
moŜe mieć pełne zaufanie.
Ilustracja 2-19 Okno utworzone przez aplet uruchomiony w przeglądarce Netscape
Communicator 4.0.
PowyŜsze okno utworzone przez aplet na u dołu informacje o tym, iŜ jest to okno apletu.
Okna utworzone przez aplikacje Javy nie zawierają takiej informacji.
Więcej informacji o bezpieczeństwie w apletach moŜna znaleŹć w pracy "Frequently Asked
Questions - Applet Security", która jest dostępna w Internecie pod adresem:
http://java.sun.com/sfaq/
Prócz ograniczeń związanych z bezpieczeństwem, aplety mają takŜe dodatkowe moŜliwości,
których nie posiadają aplikacje. Dodatkowe moŜliwości apletów wynikają z wykorzystywania
przez nie kodu przeglądarki, w której są uruchamiane. Aplet ma dostęp do moŜliwości
przeglądarki poprzez pakiet java.applet, który zawiera klasę Applet oraz interfejsy:
AppletContext, AppletStub, i AudioClip
Dodatkowe moŜliwości apletów to:
•
•
•
aplety mogą odtwarzać dŹwięki,
aplety uruchamiane w przeglądarce Internetowej mogą uŜyć jej do wyświetlenia
dokumentów w HTML; jest to moŜliwe dzięki metodzie
AppletContext.showDocument,
aplety mogą takŜe wołać metody publiczne z innych apletów na tej samej stronie.
Przykład wywołania metody drugiego apletu znajdującego się na stronie:
Zmienna nazwaDrugiegoApletu określa nazwę apletu, którego metodę chcemy wołać.
Applet drugi = getAppletContext().getApplet(nazwaDrugiegoApletu);
ZałóŜmy, Ŝe klasa DrugiAplet definiująca ten aplet posiada metodę jakasMetoda(), to
wywołanie tej metody przybiera postać:
((DrugiAplet)drugi).jakasMetoda();
W celu wywołania metody jakasMetoda() musimy przeprowadzić konwersję referencji typu
Applet do typu DrugiAplet.
Wielowątkowość w apletach
KaŜdy aplet wykonywany jest w kilku wątkach. Metody apletu odpowiedzialne za rysowanie
(paint i update) wołane są z wątku AWT obsługującego rysowanie i obsługę zdarzeń (ang.
event handling). Metody, które stanowią szkielet apletu (tj. init, start, stop, destroy) wołane są
z wątku zaleŜnego od aplikacji, w której uruchomiony jest aplet.
Wiele przeglądarek internetowych dla kaŜdego apletu znajdującego się na stronie przydziela
oddzielny wątek słuŜący do wołania metod stanowiących szkielet apletu. Aby ułatwić zabicie
wszystkich wątków danego apletu, niektóre przeglądarki dla kaŜdego apletu przydzielają
jedną grupę wątków.
Gdy aplet np. podczas inicjalizacji (metoda init) ma np. wczytać obrazki, to nie moŜe
wykonać Ŝadnych innych czynności, zanim nie zakończy swojej inicjalizacji. W takim
przypadku naleŜy takie czasochłonne czynności umieścić w oddzielnym wątku. Nasz aplet
zostanie uruchomiony i moŜe wykonać jakieś inne zadania (np. inny wątek odczyta czas z
serwera - usługa DTime) w czasie oczekiwania na dokończenie operacji ładowania obrazków.
Aplety zazwyczaj wykonują dwa rodzaje czasochłonnych zadań: zadania wykonywane tylko
raz (np. ładowanie obrazków, nawiązanie połączenia sieciowego) i zadania wykonywane w
pętli (np. odgrywanie dŹwięków w tle).
Spójrzmy, jak wygląda uŜycie zadeklarowanych przez nas, niezaleŜnych wątków w aplecie.
Aplet Linie po uruchomieniu tworzy dwa wątki typu Rysuj, które rysują losowo na
wspólnym obiekcie graficznym (ang. canvas) linie. Linie rysowane przez kaŜdy wątek Rysuj
mają początek w punkcie zadeklarowanym przy deklaracji obiektu Rysuj a koniec linii
wybierany losowo.
Przykład 2.37 Prosty aplet wielowątkowy Linie:
import java.util.*;
import java.awt.*;
import java.applet.*;
public class Linie extends Applet
{
// Deklaracja obiektów typu Thread, które będą sterowały wątkami
// obiektów typu Rysuj
Thread watek1 = null;
Thread watek2 = null;
//W metodzie start inicjalizowane są wątki typu Rysuj
public void start()
{
try
{
// sprawdzamy czy wątek na pewno nie istnieje
if(watek1 == null);
{
// Deklaracja i inicjalizaja obiektu typu Rysuj
Rysuj rysuj = new Rysuj( this, 50, 50);
// Przypisanie do zmennej typu Thread nowego wątku
watek1 = new Thread(rysuj);
// Uruchomienie wątku watek1
watek1.start();
}
// dla watek2 wykonane zostaja te same czynności
// co dla watek1
if(watek2 == null);
{
Rysuj rysuj = new Rysuj( this, 150, 50);
watek2 = new Thread(rysuj);
watek2.start();
}
}
catch (Error e)
{ e.printStackTrace(); }
}
// Metoda stop wykonywana jest gdy opuszczamy stronę, usuwamy więc
// uruchomione wątki aby nie zajmowały miejsca w pamięci
public void stop()
{
watek1.stop();
watek2.stop();
watek1 = null;
watek2 = null;
}
}
I klasa Rysuj rysująca linie o wspólnym początku i losowo dobranym końcu. PoniewaŜ
wielowątkowość w tej klasie zaimplementowano poprzez implementacje interfejsu Runnable,
to musimy zadeklarować metodę run odpowiedzialną za działanie wątku.
class Rysuj implements Runnable
{
// Pole statyczne m_nZarodek zadeklarowano w aby kaŜdy nowo
// tworzony obiekt m_r typu Random generował inne liczby
// pseudo-losowe
static int m_nZarodek = 0;
// Deklaracja i inicjalizacja obiektu rand typu Random
Random rand = new Random(m_nZarodek++);
// W zmiennej currentApplet przechowywana jest referencja
// do apletu w którym uruchomiony będzie bieŜący wątek i dzięki
// temu będzie dostępne do rysowania urządzenie graficzne.
Applet currentApplet = null;
int m_x,m_y;
// Konstruktor klasy Rysuj
Rysuj(Applet a, int x, int y)
{
currentApplet = a;
m_x = x;
m_y = y;
}
public void run()
{
while (true)
{
// Usypiamy bieŜący wątek aby inne mogły wykonać swoją pracę
try {Thread.sleep(1000);}
catch (InterruptedException e){}
// Główne zadanie wątku, narysowanie linii na obiekcie
// graficznym apletu.
linia(currentApplet.getGraphics());
}
}
public void linia(Graphics g)
{
g.setColor(Color.blue);
g.drawLine(m_x, m_y, m_r.nextInt(), rand.nextInt());
}
}
Po uruchomieniu apletu efekt działania będzie podobny do przedstawionego na poniŜszej
ilustracji:
Ilustracja 2-20 Aplet Linie w akcji.
Źródło:
Tutorial "Java - nowy standard programowania w Internecie" Artur Tyloch