MATERIAŁY POMOCNICZE DO ĆWICZENIA 2 Klasa JTabbedPane pakietu swing Kontener klasy JTabbedPane umożliwia pracę na wielu stronach z zakładkami służącymi do wyboru strony aktualnie wyświetlanej. Zakładki mogą być umieszczone na dowolnej krawędzi komponentu, zależnie od właściwości tabplacement, która może przyjmować wartości: JTabbedPane.TOP, JTabbedPane.BOTTOM, JTabbedPane.LEFT, JTabbedPane.RIGHT. Na zakładce można wyświetlać napis i/lub ikonę. Strona z zakładką może być dowolnym komponentem np. panelem. Przy projektowaniu w środowisku NetBeans, nowe strony można dodać wybierając z menu podręcznego kontenera JTabbedPane polecenie Add From Palette. W naszej aplikacji nowe strony tworzyliśmy programowo i dodawaliśmy je do pojemnika za pomocą metody add, np.: JTabbePane1.add ( Obrazek, new Obrazek()); Podstawowe metody klasy JTabbedPane: addtab(string s, Component c) tworzy nową zakładkę o tytule s do wyświetlania komponentu c add (String s, Component c) metoda analogiczna jak addtab, tworzy nową zakładkę o tytule s do wyświetlania komponentu c i zwraca obiekt dodany do kontenera remove (int i) usuwa z kontenera zakładkę o numerze i wraz z komponentem z tej zakładki gettabcount() zwraca liczbę zakładek kontenera setselectedindex(int i) ustawia jako aktualnie wyświetlaną zakładkę o numerze i getselectedindex() zwraca numer aktualnie wyświetlanej zakładki Rysowanie w Java Swing Do rysowania można wykorzystać grafikę dowolnego komponentu, pobieraną za pomocą metody getgraphics(). Jednak rysunek tworzony na tej grafice za pomocą metod takich jak na przykład drawline( ) czy filloval( ) nie jest odświeżany automatycznie, gdy komponent jest wyświetlany po częściowym lub całkowitym zakryciu lub minimalizacji okna. Właściwym rozwiązaniem jest utworzenie własnej klasy potomnej komponentu Swing i przesłonięcie w tej klasie metody paintcomponent().w tej metodzie należy umieścić kod rysowania grafiki, wykorzystujący parametr metody, który musi być obiektem klasy Graphics. Metoda paintcomponent() jest wywoływana automatycznie, nie powinniśmy jej wywoływać sami. Odświeżenie komponentu, niezbędne np. do zmiany rysunku, uzyskujemy wywołując metodę repaint()komponentu. W aplikacji utworzyliśmy klasy potomne klas JComponent lub JPanel, na przykład: class Figury extends JComponent { } class Pojazdy extends JPanel { } W każdej z tych klas przesłoniliśmy metodę rysowania komponentu tworząc własną metodę paintcomponent() zawierającą algorytm rysowania komponentu na obiekcie klasy Graphics.Aby uzyskać dostęp do dodatkowych możliwości graficznych jakie daje klasa Graphics2D, rzutowaliśmy parametr g tej metody (który musi być klasy Graphics) na nowy obiekt klasy Graphics2D: protected void paintcomponent(graphics g){ Graphics g2d = (Graphics2D) g; // rzutowanie parametru g na typ Graphics2D // instrukcje rysowania dla obiektu g2d } Wybrane metody klasy Graphics Do tworzenia grafiki w naszej aplikacji wykorzystaliśmy m.in. następujące metody klasy Graphics: setcolor(color k) ustala bieżący kolor grafiki setfont(font f) ustala bieżącą czcionkę drawstring(string s, int x, int y) wyświetla tekst s począwszy od punktu (x, y) drawline(int x1, int y1, int x2, int y2) rysuje odcinek o końcach w punktach (x1, y1) i (x2, y2), używając bieżącego koloru Metodę tę można wykorzystać do rysowania punktu, gdy przyjmiemy x1=x2 i y1=y2 fillrect(int x, int y, int width, int height) wypełnia bieżącym kolorem prostokąt, którego lewy górny wierzchołek ma współrzędne (x, y); width - szerokość, height wysokość drawrect(int x, int y, int width, int height) rysuje bieżącym kolorem prostokąt (łamaną, którą tworzą boki prostokąta); znaczenie parametrów jak w metodzie fillrect() clearrect(int x, int y, int width, int height) wypełnia prostokąt kolorem tła; znaczenie parametrów jak w metodzie fillrect() drawoval(int x, int y, int width, int height) rysuje bieżącym kolorem elipsę wpisaną w prostokąt, którego lewy górny wierzchołek ma współrzędne (x, y); width - szerokość, height wysokość Materiały do użytku wewnętrznego strona 1
filloval(int x, int y, int width, int height) wypełnia bieżącym kolorem elipsę, znaczenie parametrów - jak w metodzie drawoval drawarc(int x, int y, int width, int height, int a0, int a) rysuje bieżącym kolorem łuk elipsy określonej przez parametry x, y, width, height; a0 kąt początkowy, a kąt środkowy łuku (w stopniach); kąty rosną przeciwnie do ruchu wskazówek zegara, a kąt 0 jest na godzinie 3. fillarc(int x, int y, int width, int height, int a0, int a) wypełnia bieżącym kolorem wycinek elipsy, znaczenie parametrów jak w metodzie drawarc drawpolygon(polygon p) rysuje bieżącym kolorem obiekt klasy Polygon (wielobok łamaną zamkniętą) drawpolygon(int[]x, int[]y, int n) rysuje bieżącym kolorem wielobok o n wierzchołkach, współrzędne wierzchołków są zapisane w tablicach x i y fillpolygon(polygon p) wypełnia bieżącym kolorem obiekt klasy Polygon fillpolygon(int[]x, int[]y, int n) wypełnia bieżącym kolorem wielobok o n wierzchołkach drawimage(image img, int x, int y, ImageObserver observer) wyświetla obraz img umieszczając jego lewy górny wierzchołek w punkcie (x, y); parametr observer umożliwia kontrolę stanu obrazu, może być pustym obiektem (null) jak w naszej aplikacji translate(int x, int y) przesuwa rysowaną grafikę o (x, y) Wykorzystanie grafiki dwuwymiarowej Java 2D Rysowanie grafiki na obiekcie klasy Graphics2D umożliwia wykonywanie dodatkowych operacji, takich jak transformacje rysunku obrót, skalowanie, przesunięcie, wypełnianie figur niejednolitym kolorem lub wzorem, zmiana grubości i stylu linii. W aplikacji wykorzystaliśmy wybrane metody klasy Graphics2D: rotate (double d) powoduje obrót rysowanej figury o kąt d (w radianach) setpaint(gradientpaint p) ustala wypełnienie niejednolitym kolorem (gradient), obiekt p może być utworzony za pomocą konstruktora klasy GradientPaint, np. GradientPaint(float x1, float y1, Color k1, float x2, float y2, Color k2) ustala zmianę koloru od k1 w punkcie (x1,y1) do k2 w punkcie (x2,y2) GradientPaint(float x1, float y1, Color k1, float x2, float y2, Color k2, boolean b) parametr b określa, czy zmiana koloru ma powtarzać się cyklicznie (gdy true) setstroke(basicstroke s) ustala styl linii, obiekt s można utworzyć za pomocą konstruktora klasy, np. BasicStroke (float d) tworzy obiekt do rysowania kreski o grubości d setclip(shape s) obcina obszar rysowania, ograniczając go do figury s, obiekt s może być utworzony za pomocą konstruktora klasy implementującej interfejs Shape. W aplikacji utworzyliśmy obiekt klasy Ellipse2D.Double, reprezentujący elipsę i wykorzystaliśmy go do obcięcia obszaru wyświetlania obrazu. Wypełnianie obszarów teksturą - klasa TexturePaint Malowanie teksturą polega na wypełnieniu obszaru przez powielenie własnego utworzonego programowo obrazu bądź istniejącej ikony. Uzyskany obraz graficzny powielany jest we wszystkich kierunkach. Do wypełnienia obszaru tekturą stosuje się metodę setpaint(): setpaint( TexturePaint p) ustala wypełnianie obszaru teksturą w postaci własnego utworzonego obrazu bądź istniejącej ikony. Obiekt p -pędzla teksturowego musi być utworzony za pomocą konstruktora klasy TexturePaint o sygnaturze: TexturePaint(BufferedImage img, Rectangle2D rec); gdzie img - powielany obraz klasy BufferedImage, możliwie małych rozmiarów rec - obszar wewnątrz kontekstu graficznego klasy Rectangle2D, powielony we wszystkich kierunkach. Położenie (x,y) prostokąta rec wpływa na przesunięcie tekstury w kontekście graficznym, natomiast jego rozmiary width i height określają rozmiar wynikowy pojedynczego obrazu i w związku z tym umożliwiają jego skalowanie. Klasa BufferedImage Do utworzenia obrazu graficznego w grafice 2D wykorzystujemy klasę BufferedImage, która jest podklasą znanej klasy Image. Obiekt BufferedImage może być utworzony bezpośrednio w pamięci i użyty do przechowywania i przetwarzania danych utworzonego lub uzyskanego z pliku obrazu. Obiekt klasy BufferedImage, może być wyświetlony poprzez użycie obiektów klasy Graphics2D. W ćwiczeniu użyliśmy jednego z konstruktorów tej klasy, następującej postaci: Materiały do użytku wewnętrznego strona 2
BufferedImage(int width, int height, int imagetype) gdzie :width, height - odpowiednio szerokość i wysokość obrazu, imagatype- jeden predefiniowanych typów tworzonego obrazu Klasa Rectangle2D Abstrakcyjna klasa opisująca prostokąt, wywodząca się z klasy RectangularShape Konstruktor tej klasy został użyty w ćwiczeniu do utworzenia drugiego parametru konstruktora klasy TexturePaint i ma następującą postać: Rectangle2D (double x, double y, double width, double height) gdzie :x, y- położenie prostokąta width, height - odpowiednio jego szerokość i wysokość Typy float i double Java udostępnia 2 typy liczbowe wykorzystywane do reprezentacji liczb rzeczywistych. Różnią się one liczbą bitów przeznaczonych na zapis danych oraz zakresem dopuszczalnych wartości. Wartości typu float(pojedynczej precyzji) zapisywane są na 32 bitach i należą do zakresu od +/-1.4*10-45 do +/-3.4*10 38. Wartości typu double (podwójnej precyzji) zapisywane są na 64 bitach i należą do zakresu od +/-4.9*10-324 do +/-1.8*10 308 Aby literał rzeczywisty był typu float musi być zakończony znakiem f lub F, w przeciwnym przypadku będzie typu double, na przykład: 10.5 - wartość typu double 10.5f - wartość typu float Dostęp do składowych klasy Składowe danej klasy, czyli jej pola i metody, są domyślnie dostępne w pakiecie, w którym znajduje się klasa. Do zmiany dostępu stosuje się słowa kluczowe Javy (tzw. modyfikatory dostępu): public, private, protected. Modyfikator dostępu umieszcza się na początku deklaracji pola lub metody. Znaczenie modyfikatorów jest następujące: public dostęp dla wszystkich klas bez ograniczeń protected dostęp w danej klasie, w jej klasach potomnych i w innych klasach pakietu private dostęp tylko w klasie, w której są zdefiniowane Klasa Font Czcionkę reprezentuje obiekt klasy Font, tworzony za pomocą konstruktora: Font(krój, styl, rozmiar); gdzie: krój to łańcuch tekstowy określający krój czcionki np. Dialog, SanSerif, "Helvetica" styl - jest jedną ze stałych statycznych typu int z klasy Font: Font.BOLD, Font.ITALIC, Font.PLAIN rozmiar - to liczba całkowita określająca wielkość czcionki w punktach Klasa FontMetrics Aby dobrze rozplanować położenie tekstu, należy uzyskać charakterystyki pisma kontekstu graficznego. Umożliwia to metoda g.getfontmetrics(), która zwraca metrykę (obiekt klasy FontMetrics ) dla bieżącej czcionki kontekstu graficznego g. W klasie FontMetrics zdefiniowana jest z kolei metoda o sygnaturze int stringwidth(string s), która zwraca szerokość obszaru zajmowanego przez napis s. Złożenie metod g.getfontmetrics().stringwidth(s) zwróci szerokość napisu s wyświetlonego bieżącą czcionką. Klasa Color Kolor jest obiektem klasy Color, która ma kilka konstruktorów. Instrukcja deklarująca i tworząca określony kolor ma postać: Color nazwa = new Color(r, g, b); Parametry r, g, b są liczbami całkowitymi z zakresu 0..255 i są składowymi reprezentacji RGB (R-red, G-green, B- blue) definiowanego koloru (teoretycznie można otrzymać w ten sposób ponad 16 milionów kolorów). Klasa Color zawiera kilkanaście standardowych wartości kolorów. Są one zadeklarowane jako stałe klasowe, np. public final static Color blue = new Color(0, 0, 255); Zatem można je używać bez tworzenia nowych obiektów klasy Color, np. Color.BLUE, Color.orange itp. Nazwy stałych określających kolory można pisać małymi lub wielkimi literami dzięki dodatkowym deklaracjom, występującym w definicji klasy Color. Klasa Polygon Ta klasa reprezentuje obiekty geometryczne zwane wielokątami lub wielobokami. W ćwiczeniu wykorzystano jej pola (atrybuty) npoints, xpoints[] i ypoints[] oraz konstruktor, tworzący obiekt klasy Polygon: int npoints liczba wierzchołków wielokąta int[] xpoints tablica współrzędnych x wierzchołków wielokąta int[] ypoints tablica współrzędnych y wierzchołków wielokąta Konstruktor klasy, czyli metoda tworząca nowy obiekt klasy Polygon, ma sygnaturę: public Polygon (int[] xpoints, int[] ypoints, int npoints) Materiały do użytku wewnętrznego strona 3
Hierarchia klas i zasada dziedziczenia Mechanizm dziedziczenia pozwala na uporządkowanie hierarchii klas. Na samym szczycie hierarchii klas języka Java znajduje się klasa o nazwie Object i wszystkie pozostałe klasy dziedziczą podstawowe zachowania od tej klasy. Klasa dziedzicząca nazywana jest klasą podrzędną (podklasą). Klasa, z której dziedziczą inne, to klasa nadrzędna, zwana też bazową. Każda klasa może mieć tylko jedną klasę nadrzędną. Od niej dziedziczy pełną funkcjonalność, a oprócz tego może zawierać swoje własne metody i atrybuty. Może też zmieniać (przesłaniać, ang. override) metody klasy nadrzędnej. W nagłówku definicji klasy podrzędnej występuje nazwa klasy nadrzędnej poprzedzona słowem kluczowym extends, np. class Wielobok extends Polygon Jeśli w konstruktorze danej klasy chcemy wykorzystać treść konstruktora klasy nadrzędnej, należy użyć instrukcji super(); lub super(arg1, arg2,...); (gdzie arg1, arg2 argumenty konstruktora klasy nadrzędnej); musi to być pierwsza instrukcja w tworzonym konstruktorze. Tożsamość obiektu; słowo kluczowe this Jeśli podczas tworzenia definicji klasy chcemy odwołać się w metodzie do bieżącego obiektu tej klasy, na rzecz którego metoda będzie wywołana, należy w miejsce nazwy obiektu zastosować słowo kluczowe this. W treści ćwiczenia, w definicji klasy Wielobok, występuje wywołanie metody g.fillpolygon(this); Wywołaliśmy tu metodę fillpolygon() wewnątrz metody Rysuj() zdefiniowanej w klasie Wielobok, a więc zastosowane słowo kluczowe this oznacza odwołanie do bieżącego obiektu klasy Wielobok. Klasa ArrayList z pakietu java.util- zastosowanie w ćwiczeniu Klasa ArrayList umożliwia tworzenie struktur danych w postaci tablicy o dynamicznie zmieniających się rozmiarach. Obiekt klasy ArrayList może przechowywać obiekty dowolnego typu a jego rozmiar zmienia się automatycznie przy wywoływaniu metod dodawania i usuwania elementów z tablicy. Aby zadeklarować i utworzyć nowy obiekt klasy ArrayList o nazwie LK, zastosowaliśmy konstrukcję: List <Samochód> LK = new ArrayList < > (); Zapis <Samochód> oznacza, że elementami listy będą obiekty klasy Samochód W programie wykorzystaliśmy następujące podstawowe metody klasy ArrayList: ArrayList() konstruktor klasy, tworzy pustą listę add( e ) dołącza obiekt e jako nowy element na koniec listy clear() - usuwa z listy wszystkie jej elementy Klasa udostępnia też inne metody, na przykład: remove( e ) - usuwa z listy pierwsze wystąpienie podanego obiektu e remove( k ) - usuwa z listy element o wskazanym indeksie k size() - zwraca rozmiar listy, odpowiednik własności length w przypadku tablic get( k ) zwraca element o wskazanym indeksie k Klasa ArrayList jest implementacją interfejsu List, który wchodzi w skład architektury Java Collections Framework., Jest ona przykładem tzw. kolekcji (kontenera), czyli obiektu przechowującego grupę obiektów (elementów kolekcji) o określonej strukturze i udostępniającego operacje na tej strukturze, takie jak dodawanie i usuwanie elementów, pobieranie elementów, sortowanie itp. Rozszerzona instrukcja pętli for Jeśli chcemy wykonać jakieś operacje dla każdego elementu tablicy lub kolekcji, można zbudować pętlę wykorzystując instrukcję for w rozszerzonej postaci (enhanced for lub for-each). Dla zbioru elementów tego samego typu można do przeglądania elementów zastosować pętlę: for ( typ zmienna : nazwa_zbioru) { // instrukcje wykonywane dla każdego elementu zbiorui} Instrukcje pętli są wykonywane dla kolejnych wartości zmiennej, pobieranych ze zbioru wartości (tablicy lub kolekcji). W naszym programie zastosowaliśmy tę zwięzłą postać instrukcji for aby narysować wszystkie obiekty klasy Samochód zapisane na liście ArrayList o nazwie LK: for(samochód K : LK){ K.rysuj(g); } Materiały do użytku wewnętrznego strona 4
Interfejsy W języku Java w uzupełnieniu do klas występują interfejsy, które stanowią mechanizm interakcji między niepowiązanymi wzajemnie obiektami różnych klas. W ćwiczeniu wykorzystano ten mechanizm do obsługi myszy. Interfejs to nazwany zbiór metod. Klasa może implementować interfejs, czyli zawarte w nim metody, odpowiednio do jej specyfiki. Dzięki takiemu rozwiązaniu różne klasy mogą w indywidualny sposób realizować metody, które z zewnątrz są dostępne w sposób jednorodny, tj. za pośrednictwem nagłówka określonego w definicji interfejsu. Interfejs definiuje zbiór (kolekcję) publicznych metod, zwykle powiązanych tematycznie i realizujących określoną funkcjonalność. W definicji interfejsu mogą ponadto występować stałe statyczne. Interfejs zawiera same nagłówki (sygnatury) definiowanych w nim metod abstrakcyjnych. Klasa implementująca interfejs musi zaimplementować wszystkie jego metody, albo będzie klasą abstrakcyjną. Nazwa interfejsu poprzedzona słowem kluczowym implements występuje na końcu nagłówka klasy, która go implementuje, po ewentualnej specyfikacji klasy nadrzędnej, np.: public class Grafika extends JPanel implements MouseListener Klasa może dziedziczyć tylko z jednej klasy, natomiast może implementować wiele interfejsów. Wówczas ich nazwy, po słowie kluczowym implements, są oddzielone przecinkami. Interfejsy na równi z klasami mogą być organizowane w pakiety oraz tworzyć hierarchie dziedziczenia. Interfejs MouseListener i klasa MouseEvent Poniżej zamieszczono definicję wykorzystanego w ćwiczeniu interfejsu MouseListener, który zawiera metody obsługujące zdarzenia myszy. Interfejs MouseListener należy do pakietu Javy java.awt.event oraz dziedziczy od zdefiniowanego w pakiecie java.util (pustego) interfejsu EventListener. public interface MouseListener extends EventListener { public void mouseclicked(mouseevent e); public void mousepressed(mouseevent e); //kliknięcie myszą //naciśnięcie przycisku myszy public void mousereleased(mouseevent e); //zwolnienie przycisku myszy public void mouseentered(mouseevent e); public void mouseexited(mouseevent e); //nasunięcie kursora myszy //wycofanie kursora myszy } Parametrem każdej z powyższych metod jest obiekt klasy MouseEvent, reprezentujący zdarzenie związane z użyciem myszy. Aby w naszym aplecie sprawdzić, który przycisk myszy został wciśnięty i czy jednocześnie jest wciśnięty klawisz specjalny, wykorzystaliśmy następujące pola i metody klasy MouseEvent: public static final int BUTTON1 pole oznaczające lewy przycisk myszy (button#1) podobnie pola BUTTON2 i BUTTON3 oznaczają przycisk środkowy i prawy public int getbutton() - metoda zwracająca informację, który przycisk wystąpił w zdarzeniu public int getx()- metoda zwracająca współrzędną x położenia kursora myszy public int gety()- metoda zwracająca współrzędną y położenia kursora myszy public boolean iscontroldown()- metoda zwracająca wartość true, gdy wraz z myszą wciśnięty jest klawisz <Ctrl>, w przeciwnym przypadku metoda zwraca wartość false. analogicznie metody isaltdown() i isshiftdown()dotyczą klawiszy <Alt> i <Shift> Materiały do użytku wewnętrznego strona 5