10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 1
10. Swing – układanie i malowanie
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
ń
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 2
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
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 3
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
.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 4
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 5
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 6
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
".
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 7
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
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 8
"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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 9
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
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 10
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 11
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
ć
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 12
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
.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 13
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,
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 14
•
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 15
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
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 16
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
”.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 17
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-
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 18
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
ą
)
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 19
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);
}
}
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 20
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 21
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;
...
}
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 22
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);
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 23
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 24
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
:
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 25
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 26
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
ę
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 27
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
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 28
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 u
mo
ż
liwienie
dynamicznego
dodawania funkcjonalno
ś
ci do
obiektu. Stwarza on
elastyczn
ą
alternatyw
ę
dla mechanizmu dziedziczenia
klas.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 29
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
)
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 30
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
.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 31
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 {
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 32
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 33
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) {
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 34
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
.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 35
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
ń
.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 36
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
ń
.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 37
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.
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 38
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.");
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 39
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());
}
};
10. Swing – układanie i malowanie
W. Kasprzak: Programowanie zdarzeniowe
10 - 40
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.