W pracy z komputerem opieranie się na jakichkolwiek zasadach jest błędem.
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.

Wykres funkcji

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

 

 

Animacja figur

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; }
}
« wstecz   dalej »