|
Część 4.5. Przykładowe aplety
Na zakończenie kursu z podstaw robienia apletów jeszcze dwa przykłady kompletnych apletów, w których wykorzystamy zdobytą dotychczas wiedzę. Pierwszy z nich, to wykres funkcji pokazujący możliwość wykorzystania apletów do celów prezentacyjnych. Drugi z przykładów, to dość prosta animacja. Przy jej okazji raz jeszcze zobaczymy jak korzystać z dziedziczenia klas, budowy metod abstrakcyjnych oraz ich implementowania. Na początek aplet rysujący wykresy funkcji sin(x) i cos(x) dla przedziału [ -2π ; 2π ]. Aplet raczej prosty i nie wymagający specjalnych komentarzy. Pokazuje jednak prezentacyjne możliwości Javy. Warte zwrócenia uwagi jest uzależnienie skali wykresu od rozmiarów apletu zadeklarowanych w znaczniku <APPLET ...>. Oto kod naszego apletu:
import java.awt.*;
import java.applet.*;
public class Wykres extends Applet
{ int scaleW, scaleH, baseY;
public void start ( )
{ // ustawienie skali rysowanego wykresu
scaleW = getSize().width;
scaleH = (int)((getSize().height-50)/2);
baseY = (int)(getSize().height/2+20);
// ustawienie tła apletu
setBackground(Color.white);
}
// funkcja obliczająca współrzędną Y funkcji sin
double fSin (double x)
{ double sin = Math.sin(x) * scaleH;
return (baseY - sin);
}
// funkcja obliczająca współrzędną Y funkcji cos
double fCos (double x)
{ double cos = Math.cos(x) * scaleH;
return (baseY - cos);
}
public void paint (Graphics g)
{ // stała π i krok zmiany X w skali wykresu
double x = -3.14;
double step = (2 * 3.14) / (getSize().width - 60);
// tytuł wykresu
g.drawRect(0, 0, scaleW-1, getSize().height-1);
g.setFont(new Font("Arial", Font.PLAIN, 16));
g.drawString("Wykres funkcji y=sin(x) i y=cos(x)", (int)(scaleW/2-130), 20);
// rysowanie osi Y
g.drawLine(20, baseY, scaleW-20, baseY);
g.drawLine(scaleW-30, baseY-5, scaleW-20, baseY);
g.drawLine(scaleW-30, baseY+5, scaleW-20, baseY);
// rysowanie osi X
g.drawLine((int)(scaleW/2),30, (int)(scaleW/2), getSize().height-2);
g.drawLine((int)(scaleW/2)-5, 40, (int)(scaleW/2), 30);
g.drawLine((int)(scaleW/2)+5, 40, (int)(scaleW/2), 30);
// rysowanie wykresu funkcji sinus
g.setColor(Color.red);
for (int i = 30 ; i < getSize().width-30 ; i++,x+=step )
{ g.drawLine(i, (int)fSin(x), i + 1, (int)fSin(x + step));
}
// rysowanie wykresu funkcji cosinus
g.setColor(Color.blue);
x = -3.14;
for (int i = 30 ; i < getSize().width-30 ; i++,x+=step )
{ g.drawLine(i, (int)fCos(x), i + 1, (int)fCos(x + step));
}
}
}
Kolejny aplet będzie mniej praktyczny, ale za to poznasz dokładniej na czym polega dziedziczenie oraz implementowanie klas abstrakcyjnych. Aplet tworzy 90 figur (po 30 trójkątów, prostokątów i elips) o losowo dobranym położeniu, rozmiarach i kierunku ruchu. Następnie wszystkie figury poruszają się we wnętrzu apletu odbijając się od ścian i zmieniając rozmiar. I to już wszystko. Wykorzystamy w aplecie fakt, że wszystkie rysowane figury maja pewne cechy wspólne: każda posiada rozmiar, kolor, kierunek ruchu. Oczywiście, każda ma cechy i kształt indywidualny. Skorzystamy jednak z możliwości uogólnienia cech wspólnych i nie będziemy tworzyć osobnego opisu wszystkich metod dla każdej figury. Stworzymy klasę Figura, która opisze właściwości i metody wspólne dla wszystkich figur. Następnie stworzymy klasy Kolo, Prostokat i Trojkat, które będą dziedziczyły (rozszerzały) klasę Figura o swoje cechy indywidualne, wykorzystując wspólne pola danych i metody klasy Figura. Zaczniemy od napisania klasy cFigura:
import java.awt.*;
public class cFigura
{ // zmienne do zapamiętania:
// - położenia i rozmiaru figury
// - kierunku ruchu w poziomie i pionie
// - koloru rysowania figury
int X, Y, W, H;
int dX = 1, dY = 1;
Color color = Color.black;
public cFigura(int x, int y,int w, int h)
{ // konstruktor klasy: zapamiętuje położenie i wielkość figury
while (x + w >= 400) w--;
while (y + h >= 400) h--;
X = x;
Y = y;
W = w;
H = h;
}
// metoda zapamiętująca kierunek ruchu figury
public void setDelta(int dX, int dY)
{ dX = dX;
dY = dY;
}
// metoda zapamiętująca kolor figury
public void setColor(Color c)
{ color = c;}
// metoda zmieniająca położenie figury
public void move(Graphics g)
{ paint(g); // zmazanie starej figury
X += dX; // zmiana położenia i wymiarów
Y += dY;
W += dX;
H += dY;
if (H == 0) H = dY;
if (W == 0) W = dX;
if (W < 0)
{ if ((X <= Math.abs(W)) || (X >= 400)) dX = -dX; }
else
{ if ((X <= 0) || (X + W >= 400)) dX = -dX; }
if (H < 0)
{ if ((Y <= Math.abs(H)) || (Y >= 400)) dY = -dY; }
else
{ if ((Y <=0) || (Y + H >= 400)) dY = -dY; }
newPaint(g); // rysowanie figury w nowym położeniu
}
// metody zmazywania i rysowania figury
// każda figura musi je indywidualnie zaimplementować
// dlatego tutaj obie są puste
public void paint(Graphics g) {}
public void newPaint(Graphics g) {}
}
Teraz możemy przystąpić do stworzenia klas dla każdej z indywidualnych figur bazując na posiadanej już klasie cFigura. Napiszemy kolejne trzy klasy dla koła, prostokąta i trójkąta:
/* KLASA mKolo.java */
import java.awt.*;
public class cKolo extends cFigura
{ // konstruktor klasy
public cKolo(int x, int y, int w, int h)
{ // wywołanie konstruktora klasy bazowej - cFigura
super(x, y, w, h);
}
przesłonięcie metody paint klasy bazowej
public void paint(Graphics g)
{ g.setColor(color);
g.drawOval(X, Y, W, H);
}
przesłonięcie metody newPaint klasy bazowej
public void newPaint(Graphics g)
{ g.setColor(color);
g.drawOval(X, Y, W, H);
}
}
/* KLASA mProstokat.java */
import java.awt.*;
public class cProstokat extends cFigura
{ // konstruktor klasy
public cProstokat(int x, int y, int w, int h)
{ // wywołanie konstruktora klasy bazowej - cFigura
super(x, y, w, h);
}
przesłonięcie metody paint klasy bazowej
public void paint(Graphics g)
{ g.setColor(color);
g.drawRect(X, Y, W, H);
}
przesłonięcie metody newPaint klasy bazowej
public void newPaint(Graphics g)
{ g.setColor(color);
g.drawRect(X, Y, W, H);
}
}
/* KLASA mTrojkat.java */
import java.awt.*;
public class cTrojkat extends cFigura
{ // konstruktor klasy
public cTrojkat(int x, int y, int w, int h)
{ // wywołanie konstruktora klasy bazowej - cFigura
super(x, y, w, h);
}
przesłonięcie metody paint klasy bazowej
public void paint(Graphics g)
{ g.setColor(color);
g.drawLine(X, Y+H, X+W, Y+H);
g.drawLine(X+W, Y+H, X+W/2, Y );
g.drawLine(X+W/2, Y, X, Y+H);
}
przesłonięcie metody paint klasy bazowej
public void newPaint(Graphics g)
{ g.setColor(color);
g.drawLine(X, Y+H, X+W, Y+H);
g.drawLine(X+W, Y+H, X+W/2, Y );
g.drawLine(X+W/2, Y, X, Y+H);
}
}
W ten sposób stworzyliśmy klasy opisujące koło, prostokąt i trójkąt. Zwróć uwagę na to, że kod każdej z klas opisujących konkretną figurę jest bardzo mały. Wynika to z wykorzystania jakie daje dziedziczenie po klasie bazowej: większość pól danych oraz prawie wszystkie metody (poza rysowaniem, które jest inne dla każdej z figur) znajdują się w klasie bazowej cFifura. Wykorzystanie tej właściwości znacznie skraca i upraszcza pisanie kodu programu. Mając przygotowane wszystkie klasy opisujące figury możemy przystąpić do napisania samego apletu:
import java.applet.*;
import java.awt.*;
public class cAnim extends Applet implements Runnable
{ int maxFigur = 90; // ilość rysowanych figur
cFigura figury[]; // tablica obiektów do przechowywania figur
Thread thread;
public void init ( )
{ // inicjacja tablicy figur
figury = new cFigura[maxFigur];
setBackground(Color.white);
// generowanie maxFigur figur
for (int i = 0 ; i < maxFigur ; i += 3)
{ // nowy prostokąt
figury[i] = new cProstokat(myRandom(380),
myRandom(380),
myRandom(200),
myRandom(200));
figury[i].setDelta(delta(), delta());
figury[i].setColor(color());
// nowe koło
figury[i+1] = new cKolo(myRandom(380),
myRandom(380),
myRandom(200),
myRandom(200));
figury[i+1].setDelta(delta(), delta());
figury[i+1].setColor(color());
// nowy trójkąt
figury[i+2] = new cTrojkat(myRandom(380),
myRandom(380),
myRandom(200),
myRandom(200));
figury[i+2].setDelta(delta(), delta());
figury[i+2].setColor(color());
}
}
// uruchomienie wątku apletu
public void start ( )
{ if (thread == null)
{ thread = new Thread(this);
thread.start();
}
}
// zatrzymanie wątku apletu
public void stop ( )
{ if (thread != null)
{ thread.stop();
thread = null;
}
}
// obsługa wątku apletu
public void run ( )
{ while (thread != null)
{ try { thread.sleep(20); }
catch (InterruptedException e) { }
repaint();
}
}
// metoda wywoływana przez repaint()
public void update (Graphics g)
{ // ruch figur
g.setXORMode(Color.black);
for (int i = 0; i < maxFigur; i++)
figury[i].move(g);
}
// metoda wywoływana po każdorazowym wykonaniu update()
public void paint (Graphics g)
{ // pobranie obiektu graficznego
g = getGraphics();
g.setColor(getBackground());
g.fillRect(1, 1, 398, 398);
g.setColor(getForeground());
g.drawRect(0, 0, 399, 399);
// zmazywanie i ponowne rysowanie figur
g.setXORMode(Color.black);
for (int i = 0; i < maxFigur; i++)
figury[i].paint(g);
// usunięcie utworzonej zmiennej graficznej
g.dispose();
}
// generowanie liczby losowej z zakresu od 0 do x
int myRandom (int x)
{ return (int)(Math.random() * x + 1); }
// generowanie losowego koloru
Color color()
{ return new Color((int)(Math.random()*255),
(int)(Math.random()*255),
(int)(Math.random()*255)); }
// losowe generowani 1 i -1 - ruch figury
int delta()
{ return (Math.round(Math.random()) == 0) ? -1 : 1; }
}
|