Wzorzec projektowy Obserwator idiomatyczne rozwiązanie
Observator Pattern Simply, the Observer pattern allows one object (the observer) to watch another (the subject). The Observer pattern allows the subject and observer to form a publish-subscribe relationship. Through the Observer pattern, observers can register to receive events from the subject. When the subject needs to inform its observers of an event, it simply sends the event to each observer.
Observable /Observator pattern import java.util.observable ; public class Licznik51 extends Observable... public void impuls () { if (TR == true) { stan ++; if (stan > max) { stan=startvalue(); TF = true; setchanged() ; notifyobservers() ;
Observable /Observator pattern import java.util.observer ; import java.util.observable ; public class TestLicznik51 implements Observer... l51 = new Licznik51() ; l51.addobserver(this) ;... public void update(observable subject, Object arg) { message.settext("got event from " + subject) ; // it is like interrupt routine
Organizacja obsługi zdarzeń W Javie obowiązuje delegacyjny model obsługi zdarzeń, (idiomatyczna implementacja wzorca Obsrwator), zgodnie z którym obiekt-źródło zdarzenia zawiadamia obiektodbiorca (słuchacz) o wystąpieniu zdarzenia. Typowym źródłem zdarzeń są komponenty GUI, które mogą zawiadamiać słuchaczy o działaniach uŝytkownika, który poprzez te komponenty przekazuje dane (polecenia) do programu.
Model delegacyjny W istocie źródłem zdarzeń związanych z GUI jest uŝytkownik, który manewrując myszką, klawiaturą... wywołuje generację strumienia zdarzeń przekazywanych do komponentu, który okupuje określony obszar. Komponent zwykle filtruje ten niskopoziomowy strumień zdarzeń i generuje pewien rodzaj zdarzeń semantycznych związanych z przeznaczeniem komponentu. Dowolny obiekt moŝe zostać powiadomiony o zaistnieniu takiego zdarzenia pod warunkiem, Ŝe zostanie zarejestrowany jako słuchacz i potrafi je obsłuŝyć.
Multiple listeners Zdarzenie reprezentowane jest przez obiekt, który dostarcza informacji o samym zdarzeniu oraz identyfikuje źródło zdarzenia. Źródłem zdarzenia jest zwykle komponent GUI, ale inne obiekty równieŝ mogą generować zdarzenia!!! (Wkrótce będziemy projektować generator impulsów)
Interfejs słuchacza zdarzenia Odbiorca musi charakteryzować się umiejętnością obsługi zdarzeń określonego typu. Umiejętności te są określone przez zestaw metod wraz z argumentami stanowiący tzw. interfejs słuchacza zdarzeń określonego typu. Obiekt słuchacz musi implementować ten interfejs tzn. dostarczyć definicji wszystkich zadeklarowanych tam metod. W bibliotece AWT zdefiniowano stosowne interfejsy słuchaczy: (ActionListener, TextListener, ItemListener i inne). Deklaracja implementacji interfejsu: public class MyClass implements ActionListener { oznacza przyjęcie zobowiązania dostarczenia implementacji metod zdefiniowanych w tym interfejsie
Trzy elementy obsługi zdarzeń Deklaracja umiejętności słuchacza : public class MyClass implements ActionListener { Rejestracja słuchacza na komponencieźródle somecomponent.addactionlistener(instanceofmyclass ); Dostarczenie kodu reagującego na zdarzenie (event handler) public void actionperformed(actionevent e) {...//code that reacts to the action...
Modyfikacja klasy TestLicznikaGUI class TestLicznikaGUI extends Frame implements ActionListener { Licznik mylicznik; Button start,...; // konstruktor void makegui() { //... start = new Button( On ); start.addactionlistener(this); //... public void actionperformed(actionevent ae) { mylicznik.start(); // main
Obiekt moŝe obsługiwać zdarzenia z wielu źródeł public void actionperformed(actionevent ae) { Object source = ae.getsource() ; if (source == start) mylicznik.start() ; // przycisk start "odblokowuje" licznik if (source == stop) mylicznik.stop() ; // przycisk stop "blokuje" licznik if (source == inc) { mylicznik.inc() ; // przycisk inc inkrementuje licznik licznik updatestate() ; // zaktualizuj stan GUI
Zdarzenia ItemEvent i TextEvent ItemEvent TextEvent ItemListener TextListener additemlistener addtextlistener itemstatechanged textvaluechanged
WindowListener W interfejsie WindowListener zdefiniowano 7 (słownie siedem) metod!: windowiconified(), windowclosing()... MoŜna w klasie deklarującej implementację tego interfejsu umieszczać puste implementacje nieinteresujących metod windowiconified() { //i skupić się tylko na wybranych public void windowclosing(windowevent we) { we.getwindow().dispose() ; System.exit(0) ; // implements... addwindowlistener(...)
Adaptery class EndTestLicznika extends WindowAdapter { public void windowclosing(windowevent we) { we.getwindow().dispose() ; System.exit(0) ; class... //... addwindowlistener(new EndTestLicznika); Obsługą zdarzeń okienkowych ramy zajmuje się osobny obiekt!
JFrame reftojframe. setdefaultcloseoperation(jframe.exit_on_close);
Osobne obiekty reagujące na zdarzenia od poszczególnych komponentów class ReakcjaNaPrzyciskStart implements ActionListener{ public void actionperformed(actionevent ae) { mylicznik.start() ; // przycisk start "odblokowuje" licznik a następnie zamawiamy dostarczanie zdarzeń: start.addactionlistener(new ReakcjaNaPrzyciskStart()) ; OK?...
Anonimowe klasy wewnętrzne start.addactiolistener(new ActionListener() { public void actionperformed(actionevent ae) { mylicznik.start() ; ) ;
Projektowanie własnych komponentów A component is an object having a graphical representation that can be displayed on the screen and that can interact with the user
public abstract class Component public Color getbackground() public Color getforeground() public Font getfont(), void setfont(font f) public Dimension getpreferredsize() public Dimension getsize(), int getwidth(), getheight public void paint(graphics g) public boolean contains(int x, int y) public void setenabled(boolean b)
paint(graphics g) Argumentem metody paint() jest kontekst graficzny związany z komponentem określający system współrzędnych, kolor, Font... Współrzędne przebiegają między pikselami
paintcomponent (Swing) public void paintcomponent(graphics g) { Graphics2D g2 = (Graphics2D) g;...
Metody klasy Graphics getcolor() - Gets this graphics context's current color setcolor(color) - Sets this graphics context's current color to the specified color. Subsequent graphics operations using this graphics context use this specified color. drawline(int, int, int, int) - Draws a line, using the current color, between two points in this graphics context's coordinate system. drawpolyline(int[], int[], int) - Draws a sequence of connected lines defined by arrays of x and y coordinates. The figure will not be closed if the first point differs from the last point.
Metody klasy Graphics c.d. drawrect(int, int, int, int) - Draws the outline of the specified rectangle using the current color of the graphics context.. fillrect(int, int, int, int) - Fills the specified rectangle with the context's current color. drawroundrect(int, int, int, int, int, int) - Draws an outlined round-cornered rectangle fillroundrect(int, int, int, int, int, int) - Fills the specified rounded corner rectangle draw3drect(int, int, int, int, boolean) - Draws a 3-D highlighted outline of the specified rectangle. The edges of the rectangle are highlighted so that they appear to be beveled and lit from the upper left corner. It is raised when the parameter is true.
Metody klasy Graphics c.d. fill3drect(int, int, int, int, boolean) - Paints a 3-D highlighted rectangle drawoval(int, int, int, int) - Draws the outline of an oval in the current color. When the last two parameters are equal, this method draws a circle. filloval(int, int, int, int) - Fills an oval bounded by the specified rectangle with the current color. drawarc(int, int, int, int, int, int) - Draws the outline of a circular or elliptical arc covering the specified rectangle. fillarc(int, int, int, int, int, int) - Fills a circular or elliptical arc covering the specified rectangle
Metody klasy Graphics c.d. drawstring(string, int, int) - Draws the text given by the specified string, using this graphics context's current font and color. getfont() - Gets the current font. getfontmetrics() - Gets the font metrics of the current font. setfont(font) - Sets this graphics context's font to the specified font.
Rysowanie tekstu fontmetrics = g.getfontmetrics();
Klasa Color Model RGB Color(float r, float g, float b) Color(int r, int g, int b) public static final Color black public static final Color BLACK...(blue, yellow, red, green,... brighter() darker()
Klasa Font Fonty fizyczne (TrueType, Helvetica, Palatino, HonMincho...) implementation dependent Fonty logiczne (Serif, SansSerif, Monospaced, Dialog, and DialogInput)-mapped to physical fonts Font(String name, int style, int size) style PLAIN, BOLD, ITALIC (BOLD ITALIC ) Size -? 12, 14,...
Źródło zdarzeń semantycznych Jeśli projektujemy komponent, który słuŝy uŝytkownikowi do przekazywania danych do programu powinien on wygenerować zdarzenie i rozesłać do zarejestrowanych słuchaczy.
Do wykonania Reagujemy na zdarzenia niskopoziomowe (mysz, klawiatura) Pewne działania uznajemy za wystarczające do wygenerowania zdarzenia semantycznego (uŝytkownik chce za pomocą tego komponentu zawiadomić aplikację i przekazać dane wejściowe)
Do wykonania c.d. Kreujemy obiekt odpowiedniego zdarzenia Rozsyłamy do zarejestrowanych słuchaczy (musimy w tym celu prowadzić rejestr słuchaczy, którzy za pomocą metod add...listener i remove...listener rejestrują się i rezygnują z usługi )
Centralka rejestracji słuchaczy public mycomponent extends Component { ActionListener actionlistener = null; public synchronized void addactionlistener(actionlistener l) { actionlistener = AWTEventMulticaster.add(actionListener, l); public synchronized void removeactionlistener(actionlistener l) { actionlistener = AWTEventMulticaster.remove(actionListener, l);
Klasa ActionEvent public ActionEvent(Object source, int id, String command) String getactioncommand() public static final int ACTION_PERFORMED
Kompas 9
import java.awt.*; import java.awt.event.*; public class Kompas extends Component implements MouseListener{ int azymut = 0; int newazymut; boolean pressed = false; boolean entered = false; ActionListener actionlistener;//refers to a list of ActionListener objects public Kompas() { // konstruktor bezargumentowy this(0); //end of constructor public Kompas(int azymut) { // konstruktor this.azymut = this.newazymut = 0 ; addmouselistener(this); //end of constructor
public void addactionlistener(actionlistener listener) { actionlistener = AWTEventMulticaster.add(actionListener, listener); //end addactionlistener() //----------------------------------------------------------------------- //The following method removes ActionListener objects from the list // described above public void removeactionlistener(actionlistener listener) { actionlistener = AWTEventMulticaster.remove(actionListener, listener); //end removeactionlistener public Dimension getminimumsize() {//overridden getminimumsize() return new Dimension(100, 100); //end getminimumsize() public Dimension getpreferredsize() {//overridden getpreferredsize() return new Dimension(100, 100);
public void paint(graphics g) { Dimension d = getsize(); int r,w,h; r = Math.min(d.height,d.width); h = d.height ; w = d.width ; g.setcolor(color.gray); g.filloval((w-r)/2,(h-r)/2,r-1,r-1); g.setcolor(color.green); if (entered) g.filloval((w-r-2)/2,(h-r-2)/2,r-5,r-5); g.setcolor(color.red); int alfa = 90 - azymut; g.drawline(w/2, h/2, w/2 + (int) (r/2*math.cos(alfa*math.pi/180)), h/2 - (int) (r/2*math.sin(alfa*math.pi/180)) );
// paint c.d. setfont(new Font("Helvetica", Font.BOLD, 14)); g.drawstring("s", w/2-4,(h+r)/2-4); g.drawstring("w", (w-r)/2, h/2 +5); g.setcolor(color.black); g.fill3drect(w/2-20, h/2-20, 40, 20, false); g.setcolor(color.red); setfont(new Font("Helvetica", Font.BOLD, 16)); g.drawstring(new Integer(newAzymut).toString(), w/2-10, h/2-5);
Oddziaływanie na komponent public void setazymut(int newazymut) { this.newazymut = newazymut; repaint(); public int getazymut() { return(newazymut);
Reakcja na operacje myszką public void mouseentered(mouseevent me) { entered = true; repaint(); public void mouseexited(mouseevent me) { entered = false; pressed=false; repaint(); public void mousepressed(mouseevent me) { pressed = true; repaint();
Generacja zdarzenia semantycznego public void mousereleased(mouseevent me) { int x,y; if(pressed == true) { pressed = false; x=me.getx(); y=me.gety(); double yy = (getsize().height/2.0-y); double xx = (x-getsize().width/2.0) ; newazymut = 90 - (int) (180/Math.PI*Math.atan(yy/xx) ); if (xx<0) newazymut+= 180; azymut = newazymut ; repaint(); if(actionlistener!=null) actionlistener.actionperformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,??? ")); // ignorujemy zwolnienie przycisku poza komponentem!
Zdarzenia a klasa Licznik Czy przepełnienie licznika moŝe spowodować wykreowanie zdarzenia (Action) i dostarczenie do zarejestrowanych słuchaczy?