10. Swing układanie i malowanie



Podobne dokumenty
Java: otwórz okienko. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Programowanie zdarzeniowe

Programowanie graficznego interfejsu użytkownika. Wykład 8. Maciej Wołoszyn 10 maja 2006

LABORATORIUM 7 Cel: 1_1

Tworzenie elementów graficznych

Podstawy Swing. Tomasz Borzyszkowski

Materiał pomocniczy do kursu Podstawy programowania Autor: Grzegorz Góralski ggoralski.com

Programowanie zdarzeniowe

Wielowątkowość. Programowanie w środowisku rozproszonym. Wykład 1.

Programowanie obiektowe

WYKONANIE APLIKACJI OKIENKOWEJ OBLICZAJĄCEJ SUMĘ DWÓCH LICZB W ŚRODOWISKU PROGRAMISTYCZNYM. NetBeans. Wykonał: Jacek Ventzke informatyka sem.

Wypożyczalnia VIDEO. Technologie obiektowe

Informatyka I. Interfejs GUI wysokiego poziomu. Biblioteka Swing. Programowanie zdarzeniowe. Politechnika Warszawska Wydział Transportu 2018

Programowanie graficznych interfejsów użytkownika

Grafika i komunikacja człowiek komputer Laboratorium. Część 1: Wstęp do grafiki

Interfejsy w Java. Przetwarzanie równoległe. Wątki.

Java - interfejs graficzny

Java biblioteka Swing

SWING. dr Jarosław Skaruz

Marcin Luckner Warsaw University of Technology Faculty of Mathematics and Information Science

Podstawy Języka Java

Wzorce Strukturalne. Adapter: opis. Tomasz Borzyszkowski

Kontenery i komponenty graficzne

Programowanie obiektowe zastosowanie języka Java SE

Architektura interfejsu użytkownika

Klasy abstrakcyjne. Klasę abstrakcyjną tworzymy przy pomocy modyfikatora abstract

Programowanie Obiektowe GUI

Kurs programowania. Wykład 4. Wojciech Macyna. 23 marca 2016

II Tworzenie klasy Prostokąt dziedziczącej z klasy wątku

I Tworzenie prezentacji za pomocą szablonu w programie Power-Point. 1. Wybieramy z górnego menu polecenie Nowy a następnie Utwórz z szablonu

Programowanie w języku Java

Projektowanie obiektowe. Roman Simiński Wzorce projektowe Wybrane wzorce strukturalne

Laboratorium z Grafiki InŜynierskiej CAD. Rozpoczęcie pracy z AutoCAD-em. Uruchomienie programu

Język Java. Rysowanie GUI Określanie wyglądu komponentów

Java Podstawy. Michał Bereta

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

Programowanie w Javie Wykład 6 Okienka w Javie (AWT)

Język JAVA podstawy. wykład 2, część 2. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

PWŚG Ćwiczenia 13. Ukończoną pracę należy przesłać na adres lub

Aplikacja wielowątkowa prosty komunikator

TEMAT : KLASY DZIEDZICZENIE

Laboratorium z informatyki sem.iii/ćw. 4 Wydział Transportu PW /19

Java niezbędnik programisty spotkanie nr 12. Graficzny interfejs użytkownika

1 Wątki 1. 2 Tworzenie wątków 1. 3 Synchronizacja 3. 4 Dodatki 3. 5 Algorytmy sortowania 4

Język Java część 2 (przykładowa aplikacja)

Język JAVA podstawy. Wykład 5, część 1. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Applety Java. Applety są przykładem kodu Java wykonywanego po stronie klienta, ale bez ujawnionej (jak w przypadku skryptu) wersji źródłowej

Wzorce projektowe. dr inż. Marcin Pietroo

Builder (budowniczy) Cel: Przykład:

Programowanie na poziomie sprzętu. Programowanie w Windows API

Współbieżność w środowisku Java

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Rysowanie prostych obiektów graficznych przy użyciu biblioteki AWT (Abstract Window Toolkit)

Dodanie nowej formy do projektu polega na:

PHP 5 język obiektowy

Informatyka i Ekonometria Programowanie komputerów Ćwiczenia Tworzenie aplikacji wykorzystaniem graficznego interfejsu użytkownika - Swing.

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Doskonalsze i nowe komponenty GUI

Java. Wykład. Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Wykład 4 Delegat (delegate), właściwości indeksowane, zdarzenie (event) Zofia Kruczkiewicz

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Aplikacje internetowe i rozproszone - laboratorium

problem w określonym kontekście siły istotę jego rozwiązania

PROJEKTOWANIE ABSTRAKCYJNEJ KLASY FIGURA PRZECHOWUJĄCEJ WSPÓLNE CECHY OBIEKTÓW GRAFICZNYCH

Programowanie w języku Java - Wyjątki, obsługa wyjątków, generowanie wyjątków

Java SE Laboratorium nr 5. Temat: Obsługa zdarzeń

Kurs programowania. Wykład 6. Wojciech Macyna. 7 kwietnia 2016

Marcin Luckner Warsaw University of Technology Faculty of Mathematics and Information Science

PWSG Ćwiczenia 12. Wszystkie ukończone zadania należy wysłać na adres: lub

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Programowanie obiektowe i zdarzeniowe

Formularz MS Word. 1. Projektowanie formularza. 2. Formularze do wypełniania w programie Word

Sposoby tworzenia projektu zawierającego aplet w środowisku NetBeans. Metody zabezpieczenia komputera użytkownika przed działaniem apletu.

4. W konstruktorze klasy Grafika wywołaj metodę określającą rozmiary ramki oraz ustaw kolor tła metodą setbackground():

Kompleksowe tworzenie aplikacji klasy Desktop z wykorzystaniem SWT i

STWORZENIE MODUŁU POBIERAJĄCEGO WEKTOROWE

GUI - projektowanie interfejsów cz. II

Programowanie obiektowe

Tworzenie prezentacji w MS PowerPoint

Dodawanie grafiki i obiektów

5. Administracja kontami uŝytkowników

POMOC / INSTRUKCJA OBSŁUGI

INSTRUKCJA DO ĆWICZENIA 5

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

Projekt ZSWS. Instrukcja uŝytkowania narzędzia SAP Business Explorer Analyzer. 1 Uruchamianie programu i raportu. Tytuł: Strona: 1 z 31

Dynamiczne i wydajne tworzenie interfejsu. Piotr Michałkiewicz

Aplikacje w środowisku Java

Aplikacje w środowisku Java

Instrukcja obsługi programu Creative Fotos

INSTRUKCJA OBSŁUGI SKLEPU INTERNETOWEGO. Alu System Plus Sp.J. ul.leśna 2d Chrzanów, tel.(+48-32)

Programowanie obiektowe

Rozdział 7. Drukowanie

Programowanie obiektowe. Literatura: Autor: dr inŝ. Zofia Kruczkiewicz

Decorator (dekorator)

Aplikacja wielow tkowa prosty komunikator

Spis treści. 1 Aplet. 2 Od aplikacji do apletu. 1 Aplet 1. 2 Od aplikacji do apletu 1. 3 Budowa apletu 3. 4 Cykl życia apletu 4

Multimedia JAVA. Historia

Compas 2026 Vision Instrukcja obsługi do wersji 1.07

Transkrypt:

10.1 Zarządzenie układem i malowanie kontenera 10.2 Klasa JComponent i malowanie komponentu 10.3 Wzorce projektowe Composite i Decorator 10.4 Wątek rozdziału zdarzeń W. Kasprzak: Programowanie zdarzeniowe 10-1 10.1 Zarządzanie układem i malowanie kontenera 1) Zarządzanie układem komponentów w kontenerze PoniŜej podano 5 róŝnych układów komponentów. W czasie realizacji kontenera określane są rozmiary i połoŝenia komponentów. 6-ty menadŝer układu to CardLayout układ o charakterze zakładek. MenadŜerowie (zarządcy) układu: BorderLayout, GridLayout, FlowLayout, BoxLayout, GridBagLayout, CardLayout W. Kasprzak: Programowanie zdarzeniowe 10-2

KaŜdy kontener posiada domyślnego menadŝera układu : dla obiektów klasy JPanel jest nim FlowLayout ; dla paneli zawartości (głównych kontenerów w obiektach JApplet, JDialog i JFrame) jest nim BorderLayout. O zastosowanym menadŝerze układu powinniśmy decydować w chwili tworzenia obiektu klasy JPanel lub podczas dodawania komponentów do panelu zawartości. Zmianę menadŝera umoŝliwia metoda kontenera setlayout. Np. fragment kodu ustawiający menadŝera BorderLayout: JPanel pane = new JPanel(); pane.setlayout(new BorderLayout()); BorderLayout Posiada 5 obszarów przeznaczonych dla komponentów: North, South, East, West, Center. W. Kasprzak: Programowanie zdarzeniowe 10-3 BoxLayout Komponenty rozmieszczane są w kolumnie lub wierszu. Zachowane są maksymalne rozmiary komponentu i moŝliwe jest przyleganie komponentów. FlowLayout Wypełnia komponentami wiersze od lewej do prawej i kontynuje w razie potrzeby w dalszym wierszu. GridLayout Normalizuje rozmiary komponetów i wyświetla je w zadanej liczbie wierszy i kolumn. GridBagLayout UmoŜliwia elastyczny dobór rozmiarów wierszy i kolumn. W. Kasprzak: Programowanie zdarzeniowe 10-4

Preferowany rozmiar komponentu Na rzecz komponentu moŝliwe są wywołania metod: setminimumsize, setpreferredsize, setmaximumsize lub nadpisanie metod getminimumsize, getpreferredsize, getmaximumsize. Jedynie menadŝer BoxLayout zwraca uwagę na wymagany przez komponent maksymalny rozmiar. Preferowane połoŝenie komponentu Wywołania metod komponentu: setalignmentx, setalignmenty lub nadpisanie metod getalignmentx, getalignmenty. Jedynie menadŝer BoxLayout zwraca uwagę na wymagane przez komponent połoŝenie. W. Kasprzak: Programowanie zdarzeniowe 10-5 Przebieg ustalania rozmiaru i połoŝenia ramki JFrame Metoda pack() sprawia, Ŝe komponent klasy JFrame posiada preferowany rozmiar. 1. Preferowany rozmiar ramki wynika z dodania rozmiarów brzegu ramki do preferowanego rozmiaru komponentów bezpośrednio zawartych w ramce, czyli równa się sumie preferowanych rozmiarów panelu zawartości ramki ("content pane") i paska menu. 2. Zarządca układu dla "content pane" (zwykle jest nim BorderLayout) określa preferowany rozmiar tego "ukrytego" kontenera. ZaleŜy on od: rozmiarów brzegu panelu zawartości i od preferowanych rozmiarów komponentów, zawartych w tym panelu. Np. zarządca GridLayout stara się dopasować szerokość i wysokość kaŝdego zarządzanego komponentu do najszerszego i najwyŝszego z nich. 3. KaŜdy komponent podaje zarządcy układu swoje preferowane rozmiary lub zwraca rozmiar wynikający ze środowiska "look and feel". W. Kasprzak: Programowanie zdarzeniowe 10-6

4. Po wyznaczeniu rozmiarów komponentów, ustalany jest rozmiar ich kontenera i ten wynik propagowany jest w górę hierarchii komponentów. 2) Malowanie komponentów kontenera Zasady malowania komponentu 1. Proces wykreślania rozpoczyna się od najwyŝszego komponentu w hierarchii, który ma być malowany (po raz pierwszy lub odświeŝony). 2. Komponenty Swing-a domyślnie odświeŝają swój wygląd po modyfikacji. Np. w wyniku wywołania metody settext() komponent zostanie zmodyfikowany i ewentualnie zmienią się jego rozmiary. 3. Jeśli zmiana połoŝenia lub rozmiaru komponentu nie następuje automatycznie to moŝna ją wymusić metodą revalidate() a potem naleŝy wywołać repaint(). 4. Kod malowania wykonuje się w wątku rozdziału zdarzeń. 5. Malowanie jest podwójnie buforowane - najpierw wypełniany jest W. Kasprzak: Programowanie zdarzeniowe 10-7 "wirtualny bufor ekranu" i dopiero po zakończeniu malowania odbywa się opróŝnienie tego bufora i przesłanie jego zawartości do bufora ekranu. Metoda setopaque MoŜna przekazać do zarządcy malowania informację o tym, Ŝe komponent jest nieprzezroczysty - metodą setopaque(true) - co zwiększy efektywność procesu rysowania. Wprawdzie komponentom odpowiadają zawsze prostokątne obszary to jednak przezroczyste komponenty mogą mieć dowolny kształt, odsłaniając fragmenty przykrywanego komponentu. W. Kasprzak: Programowanie zdarzeniowe 10-8

Przykład. Dany jest interfejs uŝytkownika i tworząca go hierarchia kontenerów. Proces malowania powyŝszego graficznego interfejsu: 1. Kontener główny typu JFrame maluje się samodzielnie jako pierwszy. 2. Kontener "content pane" najpierw maluje tło - szary, jednorodny czworokąt. Następnie wzywa on zawarty w nim obiekt typu JPanel do malowania się. Zwróćmy uwagę na fakt, Ŝe panel zawartości powinien być nieprzezroczysty. PoniewaŜ JPanel jest nieprzezroczysty moŝna by go wykorzystać w roli "content pane", tzn. zamiast W. Kasprzak: Programowanie zdarzeniowe 10-9 content=frame.getcontentpanel() i content.add(panel) // dodania naszego panelu do content pane moglibyśmy ustawić content pane na nasz panel dzięki: setcontentpane(panel). 3. Kontener typu JPanel zawarty w content pane maluje najpierw swoje tło - szary, jednorodny prostokąt. Następnie maluje swój brzeg - jest on w tym przypadku typu EmptyBorder brzeg zwiększa preferowany rozmiar panelu. Na koniec panel wzywa "swoje" komponenty aby po kolei same "malowały się". 4. Komponent JButton maluje prostokątne tło (jeśli występuje) i następnie maluje swój tekst. Jeśli przycisk posiada aktualnie kontekst klawiatury to jego wygląd jest zmieniany zgodnie z przyjętym środowiskiem "look-and-feel". 5. Komponent JLabel maluje swoje tło i swój tekst. W. Kasprzak: Programowanie zdarzeniowe 10-10

10.2 Klasa JComponent i malowanie komponentu Bazowa klasa komponentów Swing-a to klasa JComponent, która dziedziczy z klasy Container, a ta z kolei dziedziczy z klasy Component. Klasa Component zapewnia wszystkie podstawowe funkcje komponentu od ustawiania ich wyglądu po malowanie i zdarzenia. Klasa Container wspomaga dodawanie komponentów do kontenera i zarządzanie rozkładem komponentów. 1) Cechy klasy JComponent Ustawianie wyglądu komponentu Metoda setborder umoŝliwia określenie brzegu komponentu. Inne metody pozwalają na ustawianie kolorów (setforeground, setbackground), czcionki (setfont), przezroczystości (setopaque) i kursora (setcursor). Malowanie komponentu Metoda główna paint nie jest jawnie wywoływana i nie moŝe być W. Kasprzak: Programowanie zdarzeniowe 10-11 przesłaniana. Wywołuje ona metody paintcomponent, paintborder i paintchildren. Z nich jedynie metoda paintcomponent moŝe być przesłaniana i wtedy wyznacza ona własny kod uŝytkownika dla rysowania komponentu. Jawnie wywoływanymi metodami są teŝ: repaint (wymusza, aby całość lub część w podanym prostokącie obszaru komponentu została odmalowana), revalidate (wymusza ponowne obliczenie rozkładu dla kontenera komponentu i prowadzi do odpowiedniego odmalowania). Look and feel KaŜdy obiekt klasy JComponent posiada związany z nim obiekt klasy ComponentUI, który realizuje jego rysowanie, obsługę zdarzeń, wyznacza rozmiary, itp. Rodzaj obiektu ComponentUI specyfikowany jest niejawnie ustawieniem look and feel wynikającym z wykonania w programie metody UIManager.SetLookAndFeel. W. Kasprzak: Programowanie zdarzeniowe 10-12

Wspomaganie rozkładu komponentów Klasa Component posiadała metody pobierania wymagań komponentu, np. getminimumsize, getmaximumsize, getpreferredsize, getalignmenty i getalignmentx, a klasa JComponent dodaje do nich metody umoŝliwiające ustawianie tych własności setpreferredsize, setminimumsize, setmaximumsize, setalignmentx i setalignmenty. Istnieją teŝ metody do ustawiania i pobierania menadŝera układu komponentu (setlayout, getlayout) oraz ustawienia kierunku - zorientowania komponentu (applycomponentorientation). Zarządzanie hierarchią komponentów zawartych w kontenerze Metody dodające komponent do kontenera (add) i usuwające komponent z kontenera (remove). Metody informujące o hierarchii w kontenerze: getrootpane podaje kontener pełniący rolę root pane w kontenerze. getcomponentcount - podaje liczbę komponentów tego kontenera, getcomponent, getcomponents - pobiera jeden (o podanym indeksie) lub wszystkie komponenty tego kontenera, W. Kasprzak: Programowanie zdarzeniowe 10-13 getparent - podaje bezpośredni kontener tego komponentu; gettoplevelancestor - pobiera główny kontener dla tego komponentu. Własności ustawiane przez uŝytkownika UŜytkownik moŝe określić własności (pary nazwa/obiekt ) dla kaŝdego obiektu klasy JComponent. Za ich pomocą moŝe on sterować przetwarzaniem danych swoich komponentów. Metody putclientproperty i getclientproperty słuŝą do nadawania wartości i pobierania wartości własnościom komponentu. Wspomaganie dostępu do stanu komponentu Szereg metod klasy JComponent umoŝliwia pobranie lub ustawianie stanu komponentu. Np. setname, getname, setenabled, isenabled, setvisible, isvisible, isshowing (sprawdza, czy komponent i jego kontener są namalowani na ekranie), settooltiptext. Wspomaganie dostępu do rozmiaru i połoŝenia komponentu Szereg metod pozwala na ustawianie i pobranie rozmiaru i połoŝenia: getwidth, getheight, getsize, setsize (pobierz aktualną szerokość wzgl. W. Kasprzak: Programowanie zdarzeniowe 10-14

wysokość komponentu w pikselach, pobierz wzgl. ustaw rozmiar komponentu), getx, gety, getbounds, setbounds (pobierz aktualną współrzędną x wzgl. y punktu odniesienia komponentu lub pobierz wzgl. ustaw połoŝenie obejmującego prostokąta komponentu, wszystko względem górnego lewego rogu przodka) getlocation, getlocationonscreen, setlocation (pobierz aktualne połoŝenie komponentu względem górnego lewego rogu przodka wzgl. ekranu lub ustaw to połoŝenie względne komponentu), getinsets (pobierz rozmiar brzegu komponentu). Obsługa zdarzeń Metody pozwalające zarejestrować lub wyrejestrować obiekt obsługi zdarzenia komponentu: dla przycisków myszy (addmouselistener, removemouselistener), ruchu myszy (addmousemotionlistener, removemousemotionlistener), klawiatury (addkeylistener, removekeylistener) i zdarzenia związane ze zmianą stanu komponentu ukrycia widoczności, przywrócenia W. Kasprzak: Programowanie zdarzeniowe 10-15 widoczności, przesunięcia połoŝenia, zmiany rozmiarów, itd. (addcomponentlistener, removecomponentlistener). Wspomaganie mechanizmu drag and drop Istnieją metody wspomagające wymianę danych przez schowek systemowy lub mechanizm przeciągania komponentu drag and drop (settransferhandler, gettransferhandler) oraz sprawdzające czy dany punkt ekranu znajduje się wewnątrz obszaru komponentu (contains) albo jaki komponent najwyŝszego poziomu zawiera ten punkt (getcomponentat). Skróty klawiszy Akcje zdefiniowane dla komponentu mogą być aktywowane przyciśnięciami klawiszy, np. jeśli przycisk posiada aktualny kontekst klawiatury to naciśnięcie klawisza spacja jest równowaŝne z kliknięciem myszą. Związanie akcji z klawiszami następuje automatycznie dzięki mechanizmowi look and feel. W. Kasprzak: Programowanie zdarzeniowe 10-16

2) Malowanie komponentu W celu wymalowania siebie samego obiekt klasy JComponent korzysta z trzech metod, wywoływanych w następującej kolejności: paintcomponent główna metoda malowania domyślnie pierwsza czynność to odtwarzanie tła jeśli komponent jest nieprzezroczysty następnie ewentualny kod malowania stworzony przez programistę. paintborder nakazuje rysowanie siebie brzegowi komponentu (jeśli istnieje) ta metoda nie powinna być wywoływana lub przesłaniania. paintchildren - nakazuje rysowanie siebie komponentom-potomkom zawartym w danym kontenerze (jeśli istnieją) ta metoda nie powinna być wywoływana lub przesłaniania. PowyŜsze trzy metody są wołane w metodzie klasy JComponent - paint, która nie powinna być przesłaniana. Standardowa realizacja malowania komponentu Swinga (ale nie dla samej klasy JComponent) metodą paintcomponent w środowisku look- W. Kasprzak: Programowanie zdarzeniowe 10-17 and-feel jest delegowana do pewnego obiektu (klasy ComponentUI), który realizuje następujące kroki: sprawdza, czy komponent jest nieprzezroczysty, jeśli tak, maluje tło całego obszaru komponentu, ewentualnie maluje elementy wynikające ze środowiska look-and-feel. Podwójne buforowanie zapewnia płynne rysowanie GUI na ekranie. Przykład. Ilustracja kolejności rysowania w kaŝdym komponencie dziedziczonym z klasy JComponent : 1. tło (jeśli nieprzezroczyste) 2. rysowanie według kodu uŝytkownika (jeśli występuje) 3. obramowanie (jeśli występuje) 4. zawarte komponenty (jeśli występują) W. Kasprzak: Programowanie zdarzeniowe 10-18

3) Metoda paintcomponent Przykład 10.1 Wizualizacja obrazu, o normalnym rozmiarze i rozciągniętego wszerz. class ImagePanel extends JPanel {... public void paintcomponent(graphics g) { super.paintcomponent(g); // Rysuj tło // Obraz o normalnym rozmiarze g.drawimage(image, 0, 0, this); // Rozmiar obrazu to 85 x 62 // Obraz rozciągnięty wszerz g.drawimage(image, 90, 0, 300, 62, this); W. Kasprzak: Programowanie zdarzeniowe 10-19 W podanym przykładzie do rysowania tła wykorzystano paintcomponent z nadklasy. MoŜna teŝ jawnie w kodzie paintcomponent podać na początku sekwencję instrukcji: g.setcolor(getbackground()); g.fillrect(0, 0, getwidth(), getheight()); g.setcolor(getforeground()); Układ współrzędnych komponentu współrzędne całkowite z zakresu (0, 0) do (szerokość - 1, wysokość - 1). Ewentualne obramowanie zmniejsza obszar malowania komponentu. W. Kasprzak: Programowanie zdarzeniowe 10-20

Informację o komponencie uzyskujemy za pomocą metod: getwidth, getheight i getinsets (dla informacji o brzegu). Np. public void paintcomponent(graphics g) {... Insets insets = getinsets(); int currentwidth = getwidth() - insets.left - insets.right; int currentheight = getheight() - insets.top - insets.bottom;... W. Kasprzak: Programowanie zdarzeniowe 10-21 4) Metoda repaint Argumenty metody repaint void repaint() - nakaz odmalowania całego obszaru komponentu. void repaint(int, int, int, int) nakaz odmalowania jedynie podanego obszaru prostokątnego w komponencie - X, Y, szerokość, wysokość. Przykład 10.2 Program obliczający obszar dla odmalowania. class SelectionArea extends JLabel {... public SelectionArea(ImageIcon image,...) { super(image); // Komponent wyświetla obraz.......// W obsłudze zdarzenia mouse-dragged : Rectangle totalrepaint = recttodraw.union(previousrectdrawn); W. Kasprzak: Programowanie zdarzeniowe 10-22

repaint(totalrepaint.x, totalrepaint.y, totalrepaint.width, totalrepaint.height);... public void paintcomponent(graphics g) { super.paintcomponent(g); // Maluje tło i obraz... // Maluje prostokąt na obrazie. g.setcolor(color.white); g.drawrect(recttodraw.x, recttodraw.y, recttodraw.width - 1, recttodraw.height - 1);...... Nad obrazem malowana jest ramka wybrana przez uŝytkownika. Tylko obszar komponentu odpowiadający tej ramce jest odmalowywany. Ten sam prostokąt przekazany do metody repaint odzwierciedlony jest w obiekcie Graphics przekazanym do metody paintcomponent. W. Kasprzak: Programowanie zdarzeniowe 10-23 Metoda getclipbounds umoŝliwia pobranie malowanego obszaru: public void paintcomponent(graphics g) { Rectangle cliprect = g.getclipbounds(); if (cliprect!= null) { // punkt odniesienia = (cliprect.x, cliprect.y) // szerokość, wysokość = cliprect.width, cliprect.height else {... Obiekt Graphics Obiekt Graphics zawiera informacje (stan kontekstu graficznego kolor, czcionkę, obszar malowania) i metody potrzebne do malowania (np. drawimage, drawstring, drawrect, fillrect). Ustawianie i pobieranie kontekstu, np. getcolor, getfont, setcolor, setfont, setclip, getclipbounds. Po zredukowanym malowaniu naleŝy odtworzyć obszar malowania komponentu jeszcze w metodzie paintcomponent: W. Kasprzak: Programowanie zdarzeniowe 10-24

Rectangle oldclipbounds = g.getclipbounds(); Rectangle clipbounds = new Rectangle(...); g.setclip(clipbounds);...// Malowanie... g.setclip(oldclipbounds); // Odtworzenie obszaru malowania PowyŜsze wynika z faktu, Ŝe nie mamy pełnej kontroli nad obszarem malowania w programie: - wiele kolejnych wywołań repaint moŝe być połączonych w jedno wywołanie paintcomponent; - metoda paintcomponent moŝe być wołana przez domyślny system malowania, bez jawnego wołania w aplikacji repaint; np. pierwsza prezentacja GUI, odsłonięcie okna komponentu przez zabranie innego przesłaniającego okna. W. Kasprzak: Programowanie zdarzeniowe 10-25 10.3 Wzorce projektowe Composite i Decorator 1) Wzorzec struktury Composite Composite jest wzorcem słuŝącym do reprezentacji struktur drzewiastych typu całość-część w taki sposób, aby sposób zarządzania strukturą nie zaleŝał od jej złoŝoności. Ten wzorzec jest implementowany w obiektowych bibliotekach AWT i Swing do realizacji struktury komponent kontener i do zarządzania jej wizualizacją. Z punktu widzenia obiektu-klienta wzorzec Composite umoŝliwia zarządzanie całością za pomocą wywołania operacji dla jednego obiektu korzenia drzewa. Niepotrzebna jest mu wiedza o rozmiarze drzewa, poniewaŝ wywołanie operacji (np. odmalowania kontenera) zostanie przekazane automatycznie do wszystkich jego elementów. Centralnym elementem wzorca jest interfejs Component, który reprezentuje dowolny obiekt w strukturze drzewiastej. Posiada on moŝliwości dodawania i usuwania swojego obiektu potomnego (oczywiście, takŝe typu Component) oraz odwołania się do wybranego potomka. Zawiera on takŝe metodę W. Kasprzak: Programowanie zdarzeniowe 10-26

operation(), którą naleŝy wykonać na kaŝdym węźle struktury. Interfejs Component posiada dwie implementacje: Leaf oraz Composite. Klasa Leaf reprezentuje obiekty, które nie posiadają potomków (czyli liście w strukturze), natomiast Composite jest dowolnym węzłem pośrednim. PoniewaŜ kaŝdy węzeł pośredni zarządza takŝe poddrzewem, którego jest korzeniem, dlatego metoda operation(), poza wykonaniem operacji W. Kasprzak: Programowanie zdarzeniowe 10-27 specyficznych dla kaŝdego węzła, wywołuje swoje odpowiedniki w obiektach potomnych, w ten sposób propagując wywołanie. 2) Wzorzec struktury Decorator Celem wzorca jest umoŝliwienie dynamicznego dodawania funkcjonalności do obiektu. Stwarza on elastyczną alternatywę dla mechanizmu dziedziczenia klas. W. Kasprzak: Programowanie zdarzeniowe 10-28

Klient wysyła komunikat do obiektu Decorator, który przekazuje go obiektowi ConcreteComponent i wykonuje dodatkowe operacje ( dekoracje ). Component jest wspólnym interfejsem dla wszystkich klas, które moŝna dekorować. Implementują go zarówno klasa ConcreteComponent, która jest odpowiedzialna za podstawową funkcjonalność oferowaną klientowi, jak i dekoratory. KaŜdy dekorator posiada referencję (oznaczoną jako kompozycję, aby zaznaczyć obowiązkowość i siłę tej relacji) do innego obiektu Component, którym moŝe być ponownie dekorator lub ConcreteComponent. Otrzymując Ŝądanie wykonania określonej operacji, dekorator deleguje je do swojego wewnętrznego obiektu Component, a następnie wykonuje specyficzną dla siebie dodatkową funkcjonalność. Czyli dodanie do obiektu nowej funkcjonalności polega na utworzeniu dekoratora i przekazaniu mu tego obiektu. Kiedy kaŝdy dekorator (klasy ConcreteDecoratorA i ConcreteDecoratorB) W. Kasprzak: Programowanie zdarzeniowe 10-29 dodaje do dekorowanego obiektu tylko jedną funkcję, wówczas dekorując obiekt wielokrotnie uzyskujemy efekt osiągnięcia Ŝądanej sumarycznej funkcjonalności. Pod względem typu udekorowany obiekt nie róŝni się od obiektu nieudekorowanego (klient widzi go przez interfejs Component), dlatego zastosowanie tego wzorca nie wymaga istotnych zmian w kodzie klienta. Obiekt ConcreteComponent, aby mógł uczestniczyć w tym wzorcu, musi definiować interfejs Component, którego alternatywną implementacją są dekoratory. WaŜne jest teŝ, aby dekoratory odpowiednio delegowały swoje metody do wewnętrznych obiektów typu Component. W. Kasprzak: Programowanie zdarzeniowe 10-30

10.4 Wątek rozdziału zdarzeń 1) "Event-dispatching thread" Kod obsługi zdarzeń wykonywany jest zawsze w jednym wątku, tzw. wątku rozdziału zdarzeń ("event-dispatching thread"). Dzięki temu Ŝadna obsługa zdarzenia w programie nie moŝe się rozpocząć zanim nie zakończy się obsługa poprzedniego zdarzenia. Np. kod metody actionperformed wykonuje się w wątku rozdziału zdarzeń. RównieŜ kod malowania komponentów na ekranie wykonuje się w tym unikalnym wątku rozdziału zdarzeń. Czyli odświeŝanie komponentu nie moŝe się odbyć dopóki wykonywana jest obsługa zdarzenia wcześniej zainicjalizowana niŝ nakaz odświeŝenia wyglądu komponentu GUI. Zasady współpracy programu z GUI Dla apletu naleŝy konstruować GUI w metodzie init( ). Dla aplikacji bezpieczne jest stosowanie następującego schematu: public class MyApplication { W. Kasprzak: Programowanie zdarzeniowe 10-31 public static void main(string[] args) { JFrame f = new JFrame(...);...// Dodaj komponenty do ramki.. f.pack(); // Realizuj ramkę f.setvisible(true); // Wyświetl ramkę // Teraz nie konstruuj juŝ Ŝadnych elementów GUI. // Wszystkie operacje na GUI - np. settext, gettext, itd. // wykonywane są podczas obsługi zdarzeń, np. actionperformed().... Stosuj zasadę pojedynczego wątku "Po realizacji komponentu Swing-a kaŝdy kod w programie, który zaleŝy od stanu komponentu lub wpływa na zmianę stanu komponentu powinien być wykonywany w wątku rozdziału zdarzeń." Realizacja komponentu oznacza, Ŝe jest on przygotowany do namalowania go na ekranie. W. Kasprzak: Programowanie zdarzeniowe 10-32

Realizacja głównego okna nastąpi na skutek wywołania dla niego jednej z metod: setvisible(true), show(), pack(). Po realizacji okna jego komponenty są teŝ zrealizowane. Jeśli dodajemy komponent do zrealizowanego kontenera to automatycznie następuje realizacja tego komponentu. Pierwsza realizacja ramki zwykle polega na wywołaniu pack. Następnie ramka jest wyświetlana wywołaniem setvisible (lub show). Odstępstwa od zasady jednego wątku: Pewne metody w API są zabezpieczone przed wątkami mają wbudowaną synchronizację dostępu. GUI aplikacji moŝe być skonstruowany, zrealizowany i wyświetlony po raz pierwszy w głównym wątku. Przypomnijmy poprzedni przykład: public static void main(string[] args) { W. Kasprzak: Programowanie zdarzeniowe 10-33 JFrame f = new JFrame(...);...// Dodaj komponenty do ramki... f.pack(); f.setvisible(true); // Dalej nie definiuj więcej GUI. W tym przykładzie GUI skonstruowano w głównym wątku. MoŜliwe jest bowiem skonstruowanie (ale nie wyświetlenie) GUI w dowolnym wątku, jeśli tylko nie ma w nim odwołań do (lub modyfikacji) juŝ "zrealizowanych" komponentów. Teoretycznie wywołanie setvisible nie jest zabezpieczone przed wątkami, gdyŝ komponenty zostały właśnie zrealizowane wywołaniem pack. Jednak jeśli program nie posiada jeszcze widocznego GUI, jest mało prawdopodobne, aby nastąpiło wywołanie paint zanim metoda setvisible powróci. GUI apletu moŝe być skonstruowany i wyświetlony w metodzie init. W. Kasprzak: Programowanie zdarzeniowe 10-34

Przeglądarki nie wołają paint dla apletu zanim nie wykonają się metody init i start. Dlatego bezpieczne jest skonstruowanie GUI w metodzie apletu init, jeśli tylko nie wywołuje się w niej show() lub setvisible(true) dla obiektu apletu. Metody klasy JComponent - repaint i revalidate - mogą być wołane z kaŝdego wątku. Te metody przekazują swoje wywołania do kolejki obsług dla wątku rozdziału zdarzeń. Listy z obserwatorami zdarzeń mogą być modyfikowane przez kaŝdy wątek - moŝna wszędzie wywołać metody addlistenertypelistener i removelistenertypelistener te operacje nie mają wpływu na aktualnie realizowany rozdział zdarzeń. W. Kasprzak: Programowanie zdarzeniowe 10-35 2) Jak naleŝy wykonywać kod w wątku rozdziału zdarzeń? Operacje na GUI po jego inicjalizacji prowadzone są przez wątek rozdziału zdarzeń. Programy sterowane są zdarzeniami pochodzącymi od widocznych komponentów takich jak przyciski lub operacje myszą. Jednak są teŝ sytuacje, gdy program wykonuje operacje na GUI, które nie są inicjowane zdarzeniami. Oto przykłady takich sytuacji. - Program wykonuje dłuŝszą inicjalizację na raty Taki program zwykle tworzy i pokazuje "zaczątki" swojego GUI podczas inicjalizacji, a potem prowadzi zmianę i modyfikację swojego GUI. Uzupełniająca inicjalizacja nie powinna mieć miejsca w wątku rozdziału zdarzeń, gdyŝ blokowałaby przyjmowanie zdarzeń i odświeŝanie ekranu dla programu. - Programy, których GUI musi być zmodyfikowany w wyniku niestandardowych zdarzeń. W. Kasprzak: Programowanie zdarzeniowe 10-36

Np. metoda programu serwera moŝe zostać wywołana przez nieznany wątek wykonywany na innej maszynie. Jednak w celu modyfikacji GUI metoda ta powinna wywołać kod wykonywany w wątku rozdziału zdarzeń. 3) Asynchroniczne wywołania metod w wątku rozdziału zdarzeń Klasa SwingUtilities posiada 2 metody przeznaczone do współpracy programu z wątkiem rozdziału zdarzeń: invokelater Metoda spowoduje wykonanie zadanego kodu w wątku rozdziału zdarzeń. Metoda nie czeka na wykonanie się tego kodu lecz powraca natychmiast. invokeandwait Działa podobnie jak metoda invokelater ale czeka na wykonanie się zadanego kodu. W. Kasprzak: Programowanie zdarzeniowe 10-37 Deklaracja metody invokelater( ) public static void invokelater(runnable dorun) Powoduje asynchroniczne wykonanie metody dorun.run() w wątku rozdziału zdarzeń, po wcześniejszym wykonaniu wszystkich oczekujących w kolejce metod obsługi zdarzeń. Ta metoda moŝe być teŝ wołana z samego wątku rozdziału zdarzeń. Za obsługę ewentualnych wyjątków zgłaszanych podczas wykonania zleconego zadania odpowiada wątek rozdziału zdarzeń. Przykład. Wywołanie zadania obiektu dohelloworld i wydruk komunikatu: Runnable dohelloworld = new Runnable() { public void run() { System.out.println("Hello World w " + Thread.currentThread()); ; SwingUtilities.invokeLater(doHelloWorld); // Wykonaj później System.out.println("Ten komunikat moŝe pojawić się wcześniej niŝ górny."); W. Kasprzak: Programowanie zdarzeniowe 10-38

Deklaracja metody invokeandwait( ) public static void invokeandwait(runnable dorun) throws InterruptedException, InvocationTargetException TakŜe ta metoda powoduje asynchroniczne wykonanie metody dorun.run() w wątku rozdziału zdarzeń, po wcześniejszym wykonaniu wszystkich oczekujących w kolejce metod obsługi zdarzeń. Jednak wołająca metoda zostaje zablokowana w oczekiwaniu na wykonanie się zleconego zadania. Metody invokeandwait() nie moŝna wywołać w wątku rozdziału zdarzeń. Przykład. Wywołanie metody invokeandwait() z nowo tworzonego wątku w aplikacji w celu wypisania napisu w wątku rozdziału zdarzeń, a następnie po powrocie metody następuje wypisanie pochodzące z nowego wątku aplikacji. final Runnable dohelloworld = new Runnable() { public void run() { System.out.println("Hello World w " + Thread.currentThread()); ; W. Kasprzak: Programowanie zdarzeniowe 10-39 Thread appthread = new Thread() { public void run() { try { SwingUtilities.invokeAndWait(doHelloWorld); catch (Exception e) { e.printstacktrace(); System.out.println("Koniec w " + Thread.currentThread()); ; appthread.start(); Jeśli metoda run() wołanego obiektu implementującego Runnable zgłasza wyjątek nieobsługiwany przez wątek rozdziału zdarzeń to jest on zamieniany na wyjątek typu InvocationTargetException i przekazywany do obsługi przez wołający wątek. Wyjątek typu InterruptedException zgłaszany jest wtedy, gdy oczekiwanie naszego wątku na zakończenie się zleconego zadania zostaje przerwane. W. Kasprzak: Programowanie zdarzeniowe 10-40