• Nie Znaleziono Wyników

12 Klasy abstrakcyjne i interfejsy

N/A
N/A
Protected

Academic year: 2021

Share "12 Klasy abstrakcyjne i interfejsy"

Copied!
1
0
0

Pełen tekst

(1)

Klasy kompletne (konkretne) to takie w których zawarte są pełne definicje występujących w nich funkcji. Nawet jeśli definicja jest pusta, tzn. ciało funkcji składa się tylko z pary nawiasów klamrowych, funkcję uznaje się za

zdefiniowaną. Aby można było utworzyć obiekt klasy, klasa ta musi być kompletna.

Można jednak nie definiować wszystkich funkcji klasy, a jedynie je

zadeklarować. Po nagłówku niezdefiniowanych funkcji umieszczamy wtedy średnik i nie definiujemy ich ciała. Deklarację ich poprzedzamy modyfikatorem abstract.

Jeśli w klasie występuje choć jedna funkcja zadeklarowana ale nie

zdefiniowana, czyli abstrakcyjna, to taka klasa też nazywa się abstrakcyjna.

Definicja klasy abstrakcyjnej musi być również poprzedzona modyfikatorem abstract. Np.:

public abstract class Klasa {

int metoda1( ) // ta metoda jest zdefiniowana

{

System.out.println("OK");

return 0;

}

void metoda2( ) { } // ta też jest zdefiniowana, choć // ciało funkcji jest puste

abstract int metoda3(String s); // ta metoda jest tylko // zadeklarowana }

Nie można kreować obiektów klas abstrakcyjnych (bo są nie w pełni zdefiniowane), ale można je rozszerzać i w klasach rozszerzających

dodefiniować metody zadeklarowane a nie zdefiniowane w klasie bazowej.

Można natomiast tworzyć odnośniki typu określonego przez klasę abstrakcyjną.

Mogą one zawierać odniesienia do obiektów klas rozszerzających tę klasę.

12 Klasy abstrakcyjne i

interfejsy

(2)

Zauważmy, że jeśli takie odniesienie dało się w ogóle utworzyć, to mamy pewność, że klasa rozszerzająca rzeczywiście dodefiniowała wszystkie funkcje abstrakcyjne z abstrakcyjnej klasy bazowej – w przeciwnym przypadku sama klasa rozszerzająca (pochodna) byłaby wciąż abstrakcyjna i obiekt tej klasy nie mógłby być utworzony (taki błąd wychwycony zostałby już na etapie

kompilacji). Tak więc, jeśli ref jest odnośnikiem typu określonego przez klasę abstrakcyjną i zawiera niepuste odniesienie, to obiektowi temu na pewno można wydawać polecenia zadeklarowane w klasie abstrakcyjnej.

Klasy „czysto” abstrakcyjne – ze wszystkimi metodami abstrakcyjnymi, nazywamy interfejsami. W ich definicji podajemy zamiast słowa kluczowego class słowo kluczowe interface. Ponieważ interfejsy są z założenia w pełni abstrakcyjne, nie trzeba już używać modyfikatora abstract – ani przy definicji klasy (interfejsu), ani przy deklaracjach funkcji. Pola zadeklarowane w

interfejsie są automatycznie public, static, final. Funkcje są automatycznie public i abstract. Interfejsy nie mogą zawierać konstruktorów.

Interfejsy mogą rozszerzać inne interfejsy. Klasy natomiast mogą

implementować interfejs. Zapisuje się to podając po nazwie klasy a przed klamrą otwierającą ciało definicji frazę implements Interfejs, gdzie Interfejs jest nazwą implementowanego interfejsu lub listą oddzielonych przecinkami nazw interfejsów. W klasie implementującej interfejs powinny być zdefiniowane wszystkie metody zadeklarowane w interfejsie – tylko wtedy klasa jest kompletna: jeśli tak nie jest to klasa taka pozostaje abstrakcyjną.

Klasa może jawnie rozszerzać tylko jedną klasę, ale może implementować dowolnie wiele interfejsów.

Podobnie jak to ma miejsce w wypadku klas abstrakcyjnych, można definiować odnośniki typu określonego przez interfejs. Odnośnik taki może zawierać

odniesienie puste lub odniesienie do obiektu klasy implementującej ten interfejs.

Na rzecz tego odnośnika można zatem wywoływać metody zadeklarowane w interfejsie – jest bowiem pewność, że zostały one zdefiniowane w klasie

obiektu, skoro w ogóle możliwe było jego utworzenie, a, dzięki polimorfizmowi, te właśnie metody będą wtedy wywołane.

13 Zdarzenia i ich

obsługa

(3)

Otoczenie jest źródłem zdarzeń (ang. events). Mogą to być zdarzenia

„fizyczne”: naciśnięcie klawisza, kliknięcie myszką, itd.; mogą to też być zdarzenia czysto programowe jak zmiana stanu pewnego obiektu. Każde

zdarzenie ma swoje konkretne źródło: my będziemy się zajmować zdarzeniami którego źródłem są komponenty graficznego interfejsu użytkownika: pola tekstowe, przyciski, panele, itd; są to obiekty klas dziedziczących z

java.awt.Component, w szczególności, jak wiemy, obiekty klas rozszerzających klasę JComponent z pakietu javax.swing.

System operacyjny rozpoznaje zdarzenie (np. naciśnięcie klawisza lub kliknięcie myszką w obszarze komponentu). Informacja o tym zdarzeniu jest zapisywana w „kolejce zdarzeń”.

Osobny wątek naszego programu, zwany wątkiem zdarzeniowym,

„odkolejkowuje” (ang. dequeue) zdarzenia, i

 dla każdego z nich szuka „słuchaczy” (obiektów nasłuchujących,

ang. listeners), czyli obiektów wydelegowanych do obsługi tego rodzaju zdarzeń i pochodzących z danego źródła

 jeśli takich słuchaczy nie ma, to zdarzenie jest (w każdym razie przez nasz program) ignorowane

 jeśli słuchacz jest, to wątek tworzy opisujący zdarzenie obiekt zdarzeniowy (np. klasy MouseEvent, KeyEvent, itd.). Obiekt taki zawiera informacje o źródle zdarzenia i jego parametrach – np.

współrzędne punktu w którym została kliknięta myszka. Następnie oddelegowanym do nasłuchu tego typu zdarzeń pochodzących z danego źródła obiektom-słuchaczom wydawane są polecenia wyrażone przez funkcje obsługi zdefiniowane w klasach nasłuchujących, a więc w tych klasach których obiektami są ci słuchacze (dla zdarzenia „myszkowego”

np. polecenie mousePressed). Aby to było możliwe, klasy słuchaczy muszą te polecenia-metody definiować; wymaga się zatem, aby

implementowały odpowiednie interfejsy

Istnieje wiele rodzajów (klas) zdarzeń. Np. z myszką są związane zdarzenia klasy MouseEvent (tworzone po naciśnięciu przycisku myszki, po jego zwolnieniu, po poruszeniu kursorem myszki), z klawiaturą zdarzenia klasy KeyEvent (naciśnięcie/zwolnienie klawisza), a z przyciskami – obiektami klasy JButton, klatkami – obiektami klasy JTextField, pozycjami menu – obiektami klasy JMenuItem, zdarzenia klasy ActionEvent (np. kliknięcie przycisku, wybranie pozycji z menu).

(4)

Ponadto istnieją zdarzenia zegarowe, zdarzenia związane z wydawaniem poleceń komponentom oblicza aplikacji (zamykanie okna, zmiana zawartości listy, itp.), zdarzenia związane z odtwarzaniem pulpitu, wiodące do wywołania funkcji paint i paintComponent itd.

Obiektem nasłuchującym – „słuchaczem” – zdarzenia kategorii KindEvent (gdzie Kind to np. Mouse, Key, ...) może być dowolny obiekt klasy

bezpośrednio lub pośrednio implementującej interfejs KindListener lub rozszerzającej klasę KindAdapter (klasę „adaptacyjną”).

Delegujemy obiekt listener do obsługi zdarzeń określonej kategorii i których źródłem jest określony komponent wywołaniem metody

klasa java.awt.Component:

void addKindListener(KindListener listener)

na rzecz obiektu klasy java.awt.Component lub pochodnej, któremu zdarzenie może się „przytrafić” (czyli obiektu który wysyła informację o zdarzeniu, a więc jest jego źródłem). Ogólnie zatem instrukcja delegująca ma postać

źródło.addKindListener(słuchacz);

gdzie

 źródło jest odnośnikiem do obiektu-źródła, a więc komponentu

 nazwa metody wskazuje na typ zdarzeń

 słuchacz jest odnośnikiem do obiektu nasłuchującego, a więc obiektu klasy implementującej interfejs KindListener

Rodzajem zdarzenia, Kind, może być, jak mówiliśmy, Mouse, Key, ale również Component, Focus, Action, Change, itd.

W poniższym przykładzie klasa Klasa rozszerza klasę JPanel, która jest klasą potomną w stosunku do java.awt.Component. Jej obiekty więc mogą być źródłem zdarzeń, np. „myszkowych”. Klasa Klasa implementuje też interfejs MouseListener, a więc jej obiekty mogą pełnić rolę słuchaczy zdarzeń typu MouseEvent. Ponieważ implementuje interfejs MouseListener, więc musi definiować wszystkie funkcje zadeklarowane w tym interfejsie, których jest pięć (i są wymienione poniżej). Oczywiście, jeśli którejś z tych funkcji nie

potrzebujemy, możemy jej ciało pozostawić puste – jak pamiętamy bezrezultatowa funkcja z pustym ciałem jest dobrze określoną, w pełni

zdefiniowaną funkcją. Widzimy z tego przykładu, że ten sam obiekt może pełnić rolę źródła zdarzeń i jednocześnie ich słuchacza.

(5)

class Klasa

extends JPanel,

implements MouseListener { Klasa( )

{ ...

addMouseListener(this); // = this.addMouseListener(this);

… }

public void mouseExited(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseClicked(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mousePressed(MouseEvent e) {

...

} ...

...

}

Innym rozwiązaniem mogłoby tu być zdefiniowanie osobnej klasy obiektów nasłuchujących zdarzenia klasy MouseEvent i w programie oddelegowanie obiektu tej klasy do obsługi tych zdarzeń; np.:

class Watcher

implements MouseListener {

public void mouseExited(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseClicked(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mousePressed(MouseEvent e) {

...

} }

i w programie teraz

(6)

class Klasa

extends JPanel { Klasa( )

{ ...

Watcher w = new Watcher( );

addMouseListener(w); // = this.addMouseListener(w);

// albo krócej: addMouseListener(new Watcher( ) );

… } ...

...

}

Funkcje obsługi zadeklarowane w interfejsach KindListener są wszystkie publiczne i bezrezultatowe. Ich parametrem jest odnośnik do obiektu klasy zdarzeniowej. Niektóre z interfejsów są opisane w tabeli poniżej: dla podanych tu interfejsów wymienione są wszystkie funkcje obsługi (a więc te które muszą być zdefiniowane w klasach implementujących te interfejsy):

interfejs klasa zdarzeń funkcje obsługi

ActionListener ActionEvent actionPerformed

KeyListener KeyEvent keyPressed

keyReleased keyTyped

MouseListener MouseEvent mousePressed

mouseReleased mouseClicked mouseEntered mouseExited MouseMotionListener MouseEvent mouseMoved

mouseDragged Wymienione funkcje wywoływane są przez wątek zdarzeniowy na rzecz

„słuchaczy” w następujących systuacjach:

interfejs MouseMotionListener

void mouseMoved(MouseEvent evt)

(7)

Przemieszczono kursor, nie naciskając przycisku myszki (przesunięto kursor).

Obiekt evt udostępnia, między innymi, współrzędne punktu w którym znalazł się kursor myszki w obszarze komponentu (włączając w to obrzeże).

void mouseDragged(MouseEvent evt)

Przemieszczono kursor przy naciśniętym przycisku myszki (przeciągnięto kursor). Udostępnione w obiekcie evt współrzędne dotyczą punktu, który może się znajdować poza komponentem (mogą być ujemne!) jeśli przeciąganie

zaczęło się w obszarze komponentu. W trakcie przeciągania funkcja wywoływana jest z pewną częstotliwością wiele razy.

UWAGA: nie ma klasy MouseMotionEvent, jest natomiast interfejs

MouseMotionListener i adapter MouseMotionAdapter. Zdarzenia polegające na przesuwaniu myszki generują obiekty klasy MouseEvent.

interfejs MouseListener

void mousePressed(MouseEvent evt)

Naciśnięto przycisk myszki. W obiekcie wskazywanym przez evt dostępna jest informacja np. o miejscu kliknięcia.

void mouseReleased(MouseEvent evt) Zwolniono przycisk myszki.

void mouseClicked(MouseEvent evt)

Naciśnięto, a następnie, nie przemieszczając kursora, zwolniono przycisk myszki.

void mouseEntered(MouseEvent evt)

Przemieszczono kursor w obszar komponentu (np. pulpitu - panelu).

Udostępnione współrzędne dotyczą zazwyczaj punktu w obszarze komponentu, a nie na jego obrzeżu.

void mouseExited(MouseEvent evt)

Przemieszczono kursor poza obszar komponentu (np. pulpitu). Udostępnione współrzędne dotyczą zazwyczaj punktu poza obszarem komponentu, a nie na jego obrzeżu.

(8)

Uwaga: Jeśli przeciąganie zaczęło się poza obszarem komponentu, to po przejściu kursora przez jego obrzeże zostanie wywołana funkcja

mouseEntered. Nie będzie natomiast wywołana funkcja mouseDragged ani funkcja mouseReleased, chyba że obiekt nasłuchujący zostanie dodatkowo oddelegowany do nasłuchiwania zdarzeń, które zachodzą poza komponentem.

interfejs KeyListener

void keyPressed(KeyEvent evt) Naciśnięto klawisz klawiatury.

void keyReleased(KeyEvent evt) Zwolniono klawisz klawiatury.

void keyTyped(KeyEvent evt)

Naciśnięto, a następnie zwolniono klawisz, któremu odpowiada znak Unikodu (niektórym klawiszom nie odpowiada żaden znak, np. klawiszowi Shift). We wszystkich przypadkach można wewnątrz metody „zapytać” obiektu evt o to który klawisz został naciśnięty.

Dla interfejsów które deklarują więcej niż jedną funkcję obsługi istnieją odpowiadające im klasy adapterowe o nazwie KindAdapter. Klasy te implementują odpowiednie interfejsy i dostarczają definicji wszystkich ich metod. Definicje te są puste. Na przykład klasa MouseMotionAdapter miałaby postać

public class MouseMotionAdapter implements MouseMotionListener {

public void mouseDragged(MouseEvent e) { } public void mouseMoved(MouseEvent e) { } }

W ten sposób zamiast na przykład implementować interfejs MouseListener, co powoduje konieczność zdefiniowania wszystkich jego pięciu funkcji, można rozszerzyć klasę MouseAdapter i przedefiniować tylko te metody których potrzebujemy. Pozostałe (o ciele pustym) będą dziedziczone z klasy

MouseAdapter. Jeśli zatem chcemy aby program reagował na naciśnięcie myszki i jej przesuwanie, to można klasę obiektu nasłuchującego zdefiniować na przykład tak (MouseListener zawiera pięć funkcji, a MouseMotionListener

(9)

tylko dwie, więc jest bardziej rozsądne rozszerzać MouseAdapter a implementować MouseMotionListener – trzeba bowiem pamiętać, że rozszerzać można tylko jedną klasę):

class Watcher

extends MouseAdapter

implements MouseMotionListener {

public void mousePressed(MouseEvent e) { // ... obsługa naciśnięcia myszki

}

public void mouseDragged(MouseEvent e) { } public void mouseMoved(MouseEvent e) {

// ... obsługa przesuwania myszki }

}

W ten sposób uniknęliśmy konieczności definiowania czterech funkcji obsługi z interfejsu MouseListener.

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class Draw extends JFrame { public static void main(String[ ] args) {

new Draw( );

}

public Draw( ) {

setDefaultCloseOperation(EXIT_ON_CLOSE);

setTitle("Tytuł");

setLocation(50,50);

setResizable(false);

setBackground(Color.black);

Figure figure = new Figure( );

getContentPane( ).add(figure);

pack( );

8:Draw

(10)

figure.init( );

setVisible(true);

} }

class Figure extends JPanel

implements MouseMotionListener, MouseListener { int xold,yold;

Graphics pDC;

Figure( ) {

setPreferredSize(new Dimension(400,400));

setBackground(Color.black);

}

void init( ) {

addMouseListener(this);

addMouseMotionListener(this);

pDC = getGraphics( );

pDC.setColor(new Color(255,0,100));

}

public void mousePressed(MouseEvent e) {

xold = e.getX();

yold = e.getY();

}

public void mouseMoved(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { }

public void mouseExited(MouseEvent e) {

repaint( );

}

public void mouseDragged(MouseEvent e) {

int x = e.getX( );

(11)

int y = e.getY( );

pDC.drawLine(xold,yold,x,y);

xold = x;

yold = y;

}

public void paintComponent(Graphics g) {

super.paintComponent(g);

} }

Klasy obiektów zdarzeniowych (MouseEvent, KeyEvent, ActionEvent, itd.) do których to obiektów odnośnik jest zawsze parametrem metod obsługi

zdarzenia, są wyposażone są w szereg metod przydatnych podczas obsługiwania zdarzenia wewnątrz tych metod (np. dla zdarzeń „myszkowych” w metodach mousePressed, mouseClicked, itd. możemy się dzięki tym obiektom dowiedzie, gdzie i kiedy dokładnie została kliknięta myszka, dla zdarzeń

klawiszowych obiekty te niosą informacje o naciśniętym klawiszu). Wymienimy niektóre z metod w klasach zdarzeniowych odpowiadających różnym typom zdarzeń. Klasy tych obiektów nają następującą hierarchię:

j a v a . a w t .e v e n t .A c t i o n E v e n t

j a v a . a w t . e v e n t .K e y E v e n t j a v a . a w t .e v e n t .M o u s e E v e n t j a v a . a w t . e v e n t . I n p u t E v e n t

j a v a . a w t . e v e n t . C o m p o n e n t E v e n t j a v a . a w t . A W T E v e n t

j a v a . u t i l . E v e n t O b j e c t j a v a . l a n g . O b j e c t

klasa java.util.EventObject Object getSource( )

dostarcza odniesienie do źródła zdarzenia (komponentu) klasa java.awt.event.ActionEvent

(12)

String getActionCommand( )

dostarcza odniesienie do napisu związanego ze źródłem (np. za pomocą metody setActionCommand)

int getModifiers( )

dostarcza liczbę całkowitą której poszczególne bity niosą informację o stanie klawiszy modyfikujących (shift, control, meta) w momencie zajścia zdarzenia klasa java.awt.event.InputEvent

long getWhen( )

dostarcza informację o czasie zdarzenia (w milisekundach od godz. 00:00 dnia 1 stycznia 1970 roku)

boolean isAltDown( ) isControlDown( ) isMetaDown( ) isShiftDown( )

dostarcza informację o tym czy w momencie zdarzenia był naciśnięty klawisz alt, control, itd.

klasa java.awt.event.MouseEvent int getX( )

int getY( )

dostarcza informację o współrzędnych kliknięcia myszką w obrębie komponentu który był źródłem zdarzenia

boolean isPopupTrigger( )

dostarcza informację o tym czy na danej platformie zdarzenie to jest zdarzeniem wyzwalającym pojawienie się wyskakującego menu typu pop-up (menu

podręczne)

int getClickCount( )

dostarcza informację o tym ile razy kliknięto myszką – w tym samym miejscu, w odstępach czasu nie przekraczających limitu charakterystycznego dla danej platformy

klasa java.awt.event.KeyEvent int getKeyChar( )

dostarcza znak (kod Unicode’u) związany z danym klawiszem (np. 'A' gdy naciśnięto shift+a). Dostarcza znak KeyEvent.CHAR_UNDEFINED gdy danemu klawiszowi nie odpowiada żaden znak Unicode’u.

(13)

int getKeyCode( )

dostarcza kod naciśniętego klawisza. Kody klawiszy są zdefiniowane w postaci stałych całkowitych zadeklarowanych jako statyczne, finalne stałe w klasie KeyEvent

static String getKeyString(int keyCode)

dostarcza w postaci napisu opis klawisza o kodzie keyCode.

Kody niektórych klawiszy to:

Klawisz Nazwa kodu

A VK_A

B VK_B

… …

Z VK_Z

Fn VK_Fn

→ VK_RIGHT

← VK_LEFT

↑ VK_UP

↓ VK_DOWN

Enter VK_ENTER

Space VK_SPACE

Del VK_DELETE

Ins VK_INS

NumTab + VK_ADD

NumTab – VK_SUBTRACT Shift VK_SHIFT

Ctrl VK_CONTROL

Alt VK_ALT

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class MovingFigures extends JFrame { public static void main(String[ ] args)

{

8:MovingFigures

(14)

new MovingFigures( );

}

public MovingFigures( ) {

Figure figure = new Figure( );

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setContentPane(figure);

pack( );

setVisible(true);

} }

class Figure extends JPanel {

private final int step = 4, maxR = 150, minR = 10;

private int radius = 10, xcoor = 10, ycoor = 10;

private int width,height;

private Color kolor = Color.red;

private boolean blk = false, cir = true, fil = true;

Figure( ) {

setBackground(Color.black);

setPreferredSize(new Dimension(600,500));

addMouseListener(new MouseAdapter( ) {

public void mousePressed(MouseEvent e) {

if ( e.isMetaDown( ) ) { blk = true;

} else {

xcoor = e.getX();

ycoor = e.getY();

blk = false;

}

repaint( );

} });

addKeyListener(new KeyAdapter( ) {

public void keyPressed(KeyEvent e)

(15)

{

switch ( e.getKeyCode( ) ) { case KeyEvent.VK_UP :

ycoor = (height + ycoor - step) % height;

break;

case KeyEvent.VK_DOWN :

ycoor = (ycoor + step) % height;

break;

case KeyEvent.VK_LEFT :

xcoor = (width + xcoor - step) % width;

break;

case KeyEvent.VK_RIGHT : xcoor = (xcoor + step) % width;

break;

case KeyEvent.VK_ADD :

radius = Math.min(maxR,radius + step);

break;

case KeyEvent.VK_SUBTRACT :

radius = Math.max(minR,radius - step);

break;

case KeyEvent.VK_ENTER : cir = !cir;

break;

case KeyEvent.VK_R : kolor = Color.red;

break;

case KeyEvent.VK_G : kolor = Color.green;

break;

case KeyEvent.VK_B : kolor = Color.blue;

break;

case KeyEvent.VK_Y : kolor = Color.yellow;

break;

case KeyEvent.VK_SPACE : fil = !fil;

break;

default:

return;

}

repaint( );

} });

}

(16)

public void paintComponent(Graphics g) {

super.paintComponent(g);

requestFocus( );

int x, y, b;

width = getWidth( );

height = getHeight( );

if ( !blk ) {

g.setColor(kolor);

x = xcoor - radius;

y = ycoor - radius;

b = 2*radius;

if ( cir ) {

if ( fil ) { g.fillOval(x,y,b, b); } else { g.drawOval(x,y,b-1,b-1); } } else {

if ( fil ) { g.fillRect(x,y,b, b ); } else { g.drawRect(x,y,b-1,b-1); } }

} } }

W powyższym programie użyliśmy tzw. klas anonimowych. Ich definicja nie zawiera słowa kluczowego class i następującej po nim nazwy. Ciało (definicję) takiej klasy umieszcza się bezpośrednio po fabrykatorze, odwołującym się do klasy albo interfejsu. Odpowiednio do tego, klasa anonimowa stanowi

rozszerzenie podanej klasy albo stanowi rozszerzenie klasy Object i implementuje podany interfejs. UWAGA: Użycie klasy anonimowej do sfabrykowania obiektu nasłuchującego jest możliwe jedynie w przypadku obsługiwania zdarzeń tylko jednej kategorii (np. tylko MouseEvent lub tylko KeyEvent, ale nie jednocześnie obu!). Na przykład:

addMouseListener(new MouseAdapter( ) {

public void mousePressed(MouseEvent e) {

… }

(17)

});

Zauważmy, że w powyższym przykładzie konieczne jest skierowanie celownika (ang. focus) na panel w metodzie paintComponent (wywołanie

requestFocus). Panel bowiem został wydelegowany jako słuchacz zdarzeń klawiszowych, a dla takich zdarzeń przyjmuje się, że źródłem zdarzenia jest ten komponent, na który w danym momencie skierowany jest celownik. Jeśli zatem naciśniemy w naszym przypadku klawisz, gdy celownik nie będzie nastawiony na panel, to zdarzenie będzie zignorowane! Przy tym trzeba pamiętać, że

celownik można nastawiać wyłącznie na komponenty aktualnie widoczne na ekranie (czyli wywołanie musi mieć miejsce po pack i po setVisible).

Oczywiście nic nie stoi na przeszkodzie, aby w jednym programie zdefiniować więcej głównych ramek (komponentów ciężkich). W poniższym przykładzie jest ich wiele. W tym programie nie musimy właściwie dbać o położenie celownika, gdyż nie obsługujemy zdarzeń klawiszowych. Tym niemniej w metodzie

paintComponent skierowujemy celownik na panel, aby uniknąć umieszczania go na którymś z przycisków (co powoduje pojawienie się ramki na przycisku):

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

import java.util.*;

public class Frames extends JFrame { public static void main(String[ ] args) {

new Frames( );

}

public Frames( ) {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setTitle("Frames");

setLocation(50,0);

setResizable(false);

OurPanel panel = new OurPanel();

getContentPane( ).add(panel);

pack( );

show( );

} }

8:Frames

(18)

class OurPanel extends JPanel {

final int NUMBER_OF_FRAMES = 6;

ArrayList ramki = new ArrayList( );

ArrayList guziki = new ArrayList( );

Color[ ] kol = new Color[ ] {

Color.red, Color.yellow, Color.magenta, Color.green, Color.cyan, Color.blue };

OurPanel( ) {

setPreferredSize(new Dimension(400,400));

setBackground(Color.black);

ActionListener action = new ActionListener( ) {

public void actionPerformed(ActionEvent e) {

getRid(guziki.indexOf(e.getSource( )));

} };

JButton b;

for ( int i = 0; i < NUMBER_OF_FRAMES; i++ ) { ramki.add(new Ramka(i,455,90*i));

b = new JButton("F - " + (i+1));

b.addActionListener(action);

guziki.add(b);

add(b);

} }

void getRid(int i) {

((JButton)guziki.remove(i)).setEnabled(false);

((Ramka)ramki.remove(i)).dispose();

repaint( );

}

class Ramka extends JFrame { final int num;

public Ramka(int i, int x, int y) {

super("Frame " + (i+1));

(19)

num = i + 1;

setLocation(x,y);

getContentPane( ).setBackground(kol[i%6]);

addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {

getRid(ramki.indexOf(e.getSource( )));

dispose( );

} } );

setSize(170,90);

show( );

}

} //~end class Ramka

public void paintComponent(Graphics gDC) {

super.paintComponent(gDC);

requestFocus( );

int n, ile = ramki.size( );

if (ile == 0) System.exit(0);

gDC.setColor(kol[2]);

gDC.drawString("Zostaly okienka:",150,70);

for ( int i = 0; i < ile; i++ ) { gDC.drawString("Frame " +

((Ramka)ramki.get(i)).num, 170,150+i*45);

} } }

Cytaty

Powiązane dokumenty

Kiedy dana klasa implementuje interfejs, musi ona obsługiwać zdarzenia za pomocą metody, która jest wtedy wywoływana automatycznie, natomiast w programie trzeba ją

• Za prawidłowo wykonane ćwiczenie uzyskujesz liczbę punktów wskazaną w kolumnie punktacja zadania.. Ostatnią kolumnę tabeli

• Za prawidłowo wykonane ćwiczenie uzyskujesz liczbę punktów wskazaną w kolumnie PUNKTACJA ZADANIA.. Za brak odpowiedzi lub niepełne rozwiązanie – nie

• Za prawidłowo wykonane ćwiczenie uzyskujesz liczbę punktów wskazaną w kolumnie PUNKTACJA ZADANIA.. Za brak odpowiedzi lub niepełne rozwiązanie – nie

– zastępuje element znajdujący się na pozycji indeks obiektem o, zwraca zastąpiony obiekt (starą wartość z pozycji indeks),.. List&lt;Typ&gt; subList(int pocz,

Obsługuje zdarzenia generowanego przez obiekt klasy Timer l ub generowane przez użytkownika aplikacji na rzecz danego składnika interfejsu (np. klikniecie przycisku)..Posiada

mikrokontrolerze ARM7 oraz mikrokontrolerze Atme- u- Qt, w której napisano graficzny interfejs sterowania robotem. suwak, okno, przycisk lub pole

Dostarcza ilość bajtów faktycznie wczytanych (koniec strumienia mógł zostać napotkany przed wczytaniem count bajtów) lub –1 jeśli od razu napotkano koniec strumienia i żaden