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; } }
|