Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti
Programování v jazyku Java GUI, události
BI-PJV Programování v jazyku Java Katedra teoretické informatiky © Miroslav Balík Fakulta informačních technologií České vysoké učení technické
Kreslení na plátno – třída Graphics
Tento objekt umožňuje v objektech typu Component a tedy i Canvas, Panel, Applet, Window, Frame atd. vytvořit a upravovat kresby, texty a obrázky. Přístup k němu se získá přepsáním zděděné prázdné metody např. takto: public void paint( Graphics g ) { g.drawXYZ( ... ); // kreslí obrys obrazce v barvě BLACK g.setColor( c1 ); // nastaví pero na barvu c1 g.fillXYZ( ... ); // kreslí plný obrazec g.setFont( f ); // nastaví font písma g.setColor( c2 ); // nastaví pero na barvu c2 g.drawString( s, ... ); // nakreslí text dle fontu barvy c2 g.drawImage( im, ... ); // vykreslí obrázek { .gif, .jpg, .png } } Případně též metodou Graphics getGraphics( ), která vytvoří grafický kontext pro skrytý bufer ( off-screen image ). To využívá technika tzv. double–bufferingu pro hladší animace. Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
2/35
Graphics Jednoduché obrazce a čáry a další metody:
drawPolyline() • clearRect( … ) - přemalování na barvu pozadí dle setBackground( … ) • clipRect( … ), getClip( ), setClip( ), ... - vytřihovánky a nalepovánky • copyArea( … ) - kopírování plošky • setFont( … ), getFont( ) - práce s fonty • getFontMetrics( ) - měření nápisů • dispose( ) - uvolnění zdrojů Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
3/35
Modifikace kreseb • Při objevení ( zpočátku, deiconifikaci, odkrytí ) volá awt vlákno metodu g.clearRect( ...), která přebarví pozadí a pak metodu paint( Graphics g ). • Při programovém volání metody repaint( ... ), awt vlákno zavolá metodu update( Graphics g ), která defaultně obsahuje: public void update( Graphics g ) { ... g.clearRect( ... ); paint( g ); } • Metodu update je možné přepsat, např. tak, aby se nepřemalovávalo, čímž se kreslí na předchozí kresbu: public void update( Graphics g ) { paint( g ); } public void paint( Graphics g ) { ... } Vnější změnou atributů užitých v kreslících metodách ( tj. g.draw( ) apod. ) a následném zavolání repaint( ... ) se kresba změní či animuje. Přepočítání atributů či pospávání by mělo konat samostatné vlákno – nikoli awt – thread ( je dost zaměstnáno a sleduje chování uživatele ). Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
4/35
repaint( ), update( ), paint( ) setVisible, unhide, enlarge setBackground, setForeground, setFont, setLayout, validate, add, remove
int x = 333;
computeIt( ... ) { awt thread
x = 777 ; repaint( ... )
...
default
} user thread
user thread idle
idle
awt
system triggered
g in sw
// future call
application triggered
awt thread
update( Graphics g ) { g.clearRect( ... ) ;
partial clearRect( … )
paint( g ); Graphics // option g ) { update( g.draw...( x, ... ) ; } paint( g ); // option
paint( Graphics g ) { g.draw...( x, ... ) ;
}
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
... }
5/35
Vykreslení obrázku public class ImageLoad extends Frame { static Toolkit tk = Toolkit.getDefaultToolkit( ); static Image img = tk.getImage( "C:\\...\\star.gif" ); int width, height ; public ImageLoad( ) throws Exception { MediaTracker mt = new MediaTracker( this ); mt.addImage( img, 0 ); mt.waitForAll( ); width = img.getWidth( this ); height = img.getHeight( this ); this.setBounds( 100, 100, 200, 200); this.setVisible( true ); } public void paint( Graphics g ) { g.drawImage( img, 100, 100, this ); } }
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
6/35
Font Tato třídy podporuje rozmanitá vykreslení textů. Font má konstruktor: public Font ( String name, int style, int size ) kde: name - courier, helvetica, dialog, inputdialog, sanserif, monospaced ... style - tvar znaků zadaný konstantami PLAIN, BOLD, ITALIC size - bodová velikost např. 8 .. 96 Příklad: public void paint( Graphics g ) { g.setFont( new Font( "courier", Font.ITALIC + Font.BOLD, 18 ) ); g.drawString( "Hello, World", 75, 400 ); } Fonty jsou uspořádány v rodinách. Zjistit dostupné fonty lze takto: GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment( ); Font[ ] ff = ge.getAllFonts( ); Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
7/35
FontMetrics Zvláště proporcionální písmomalířství je obtížné. FontMetrics umožňuje písmo daného fontu měřit a zkusmo pak zvolit vhodný font. Metrika písma:
K měření pro daný font slouží metody: getLeading, getAscent, getDescent, getHeight, getMaxAscent, getMaxDescent, getMaxAdvance ... a zejména pratické: • int charWidth( char c ) – vrací odstup znaku od dalšího v řádce. • int stringWidth( String s ) – vrací délku řetězu. FontMetrics fm = g.getFontMetrics( ); int w = fm.stringWidth( "áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ" ); Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
8/35
Události ( Events ) • V rámci GUI je definován model událostí ( event model ) – od Javy 1.1 tzv. delegační model. ( Původní tzv. hierarchický model se již neužívá. ) • Událost je objekt popisující jev a poskytující metody k obsluze. • Zdroj události ( např. komponenta ) je objekt, generující události ( i více typů ) - např. Button po stisku či Frame při pohybu myši - a rozesílá je registrovaným handlerům. Zdroj handlery registruje ve svých dynamických seznamech. Události posílá tak, že zavolá patřičnou metodu handleru s parametrem odkazujícím na událost. • Handler je objekt ( vytvořený programátorem ), který událost přijme a zpracuje. Aby dokázal událost přijmout, musí implementovat jeden či více tzv. listenerů ( tj. posluchačů ) specifických typů. Listener je specifický interfejs pro každý typ události. • Tentýž objekt může být současně zdrojem i handlerem. Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
9/35
Registrace a rozesílání hx = handler for
sx =
XXX
XXXEvent XXXListener
source
hxy = handler for XXX , YYY
sy = YYYEvent
YYYListener
source
hy = handler for YYY
seznam handlerů
registrace
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
rozesílání ( broadcast )
implements 10/35
Zdroje a handlery class HX implements XXXListener { ... /* konkrétní požadované metody */ } class HY implements YYYListener { ...} class HXY implements XXXListener, YYYListener { ... /* konkrétní požadované metody */ } HX hx = new HX( ); /* konstrukce handlerů HY hy = new HY( ); HXY hxy = new HXY( ); ...
sx.addXXXListener( hx ); sx.addXXXListener( hxy );
/* registrace handlerů h u zdrojů s */
...
sy.addYYYListener( hxy ); sy.addYYYListener( hy ); Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
11/35
java.awt.event
java.util
EventObject
ActiveEvent javax.swing.event
InputMethod
Hierarchy
Invocation
Internal
Ancestor
Event
Event
Event
FrameEvent
Event
Component
Action
Adjustment
Item
Text
Event
Event
Event
Event
Event
Input Event
Container
Window
Focus
Paint
Event
Event
Event
Event
java.awt
AWT Event
Mouse
Key
MenuKey
Event
Event
Event
Mouse
MenuDrag
WheelEvent
MouseEvent
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
***Listener ***Listener ***Listener all except
java.awt
AWTEventListener
AWTEvent Multicaster 12/35
Listenery a adaptery java.util InputMethod Listener
EventListener 0
Action Listener javax.swing
Adjustment Listener
1
1
Item Listener
1
Text Listener
1
1
MouseWheel Listener
1
Hierarchy Listener
1
Action 1
Mouse Listener
MouseMotion Listener
5
Mouse
2
MouseMotion
Focus Listener
2
Focus
Key Listener
3
HierarchyBounds Listener 2
Hierarchy
Key
Bounds
Adapter
Adapter
Adapter
Adapter
Adapter
WindowState Listener
Window Listener
Component Listener
Container Listener
AWTEvent Listener
1
WindowFocus Listener
7
Window 2
Adapter
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
10
4
Component
Container
Adapter
Adapter
2
1
AWTEvent ListenerProxy
13/35
Zdroje událostí
* dědí ActionListener ale negeneruje ActionEvent Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
14/35
•
• • •
•
•
Metody listenerů
ActionListener - Button, List, TextField, MenuItem, CheckboxMenuItem actionPerformed ( ActionEvent e ) AdjustmentListener - Scrollbar adjustmentValueChanged ( AdjustmentEvent e ) AWTEventListener eventDispatched ( AWTEvent e ) ComponentListener - všechny třídy ( mimo MenuItem a potomků ) componentHidden ( ComponentEvent e ) componentMoved ( ComponentEvent e ) componentResized ( ComponentEvent e ) componentShown ( ComponentEvent e ) ContainerListener - Container a potomci componentAdded ( ContainerEvent e ) componentRemoved ( ContainerEvent e ) FocusListener - všechny třídy ( mimo MenuItem a potomků ) focusGained ( FocusEvent e ) focusLost ( FocusEvent e )
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
15/35
Metody listenerů •
• •
• •
HierarchyBoundsListener ancestorMoved ( HierarchyEvent e ) ancestorResized ( HierarchyEvent e ) HierarchyListener hierarchyChanged ( HierarchyEvent e ) InputMethodListener – pro znaková písma caretPositionChanged ( InputMethodEvent e ) inputMethodTextChanged ( InputMethodEvent e ) ItemListener – Checkbox, CheckboxMenuItem, Choice, List itemStateChanged ( ItemEvent e ) KeyListener - všechny třídy ( mimo MenuItem a potomků ) keyPressed ( KeyEvent e ) keyReleased ( KeyEvent e ) keyTyped ( KeyEvent e )
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
16/35
Metody listenerů • MouseListener - všechny třídy ( mimo MenuItem a potomků ) mouseClicked ( MouseEvent e ) mouseEntered ( MouseEvent e ) mouseExited ( MouseEvent e ) mousePressed ( MouseEvent e ) mouseReleased ( MouseEvent e ) • MouseMotionListener - všechny třídy ( mimo MenuItem a potomků ) mouseDragged ( MouseEvent e ) mouseMoved ( MouseEvent e ) • TextListener – TextComponent, TextArea a TextField textValueChanged ( TextEvent e )
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
17/35
Metody listenerů • WindowFocusListener windowGainedFocus ( WindowEvent e ) windowLostFocus ( WindowEvent e ) • WindowListener – Window a potomci windowActivated ( WindowEvent e ) windowClosed ( WindowEvent e ) windowClosing ( WindowEvent e ) – pro zavření windowDeactivated ( WindowEvent e ) windowDeiconified ( WindowEvent e ) windowIconified ( WindowEvent e ) windowOpened ( WindowEvent e ) • WindowStateListener windowStateChanged ( WindowEvent e ) Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
18/35
Listening class MyFrame extends Frame implements ActionListener, ItemListener { // tato instance je handler Button b = new Button( “OK” ); Checkbox c = new Checkbox( ); public MyFrame( ) { add( b ); add( c ); b.addActionListener( this ); c.addItemListener( this ); ... } public void actionPerformed( ActionEvent e ) { ... } public void itemStateChanged( ItemEvent e ) { ... } } Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
19/35
Adaptery Adapter je abstraktní třída, která implementuje interfejs ( např. listener ), pouze konkrétními, prázdnými metodami. Potomek adapteru je již konkrétní třída mající potřebné metody přepsané na neprázdné. To je výhodné při implementaci těch listenerů, které definují více metod – ač chceme využít jen některé z nich. K zavření okna ( Window, Dialog, Frame ) stačí metoda windowClosing( ), jež je jedna ze sedmi WindowListeneru - ostatních šest by bylo nutno definovat alespoň jako prázdné. Adapter umožňuje přepsat jen tuto. Zavření okna nemusí znamenat ukončení aplikace. Tu je třeba případně ukončit např. pomocí System.exit(0) Zdroje lze uvolnit metodou dispose().
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
20/35
Ukončení aplikace public class MyFrame extends Frame { public MyFrame( ) { this.addWindowListener( new WindowAdapter( ) { // Handler as anonymous inner class public void windowClosing( WindowEvent e ) { System.exit( 0 ); } } objekt anonymní třídy ); ... } ... } Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
21/35
Ukončení aplikace public class MyFrame extends Frame { public MyFrame( ) { ... this.addWindowListener( new Closer( ) ); // creation and registration ... } ... } class Closer extends WindowAdapter { // handler public void windowClosing( WindowEvent e ) { System.exit( 0 ); } } Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
22/35
Poloha myši
public class Test extends Frame { public Point p; public Test( ) { this.addMouseMotionListener( // registration new Test_this_mouseMotionAdapter( this ) // create handler ); } void this_mouseMoved( MouseEvent e ) { p = e.getPoint( ); } } class Test_this_mouseMotionAdapter extends MouseMotionAdapter { Test adaptee; adaptee Test_this_mouseMotionAdapter( Test adaptee ) { this.adaptee = adaptee;} public void mouseMoved( MouseEvent e ) { // overriding method adaptee.this_mouseMoved( e ); // event receipt } } Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
23/35
Vytvoření anonymních tříd
java.lang
java.util
java.lang
Object
EventListener
Object
XXXListener
anonymous
XXX Adapter
anonymous
pomocí adapteru:
pomocí listeneru:
new XXXAdapter( ) {
new XXXListener( ) {
...
...
}
}
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
24/35
Focus Fokus čili ohnisko určuje komponentu, ke které mají směřovat akce a zprávy ze zdroje, který to sám neurčuje – tj. např. z klávesnice. FocusListener i FocusAdapter obsahují dvě metody: focusGained( FocusEvent e ) a focusLost( FocusEvent e ) umožňující komponentě zjistit zda je v ohnisku. Třída Component definuje metody: public boolean isFocusOwner( ) public void setFocusable( boolean focusable ) protected void processFocusEvent( FocusEvent e )
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
25/35
Architektura MVC = Model-View-Controller http://java.sun.com/blueprints/patterns Model
+ registrace
Zapouzdřuje stav
state query
state change
Vyjevuje funkcionalitu Odpovídá na dotazy o stavu
get
set
Notifikuje View o změně
state change View Vykresluje model Požaduje aktuální data od modelu Akceptuje gesta uživatele
get
+ registrace
Controller Zapouzdřuje chování aplikace
view selection
Převádí akce do modelu
set
Vybírá pohled
Notifikuje listenery Dovoluje vybrat pohled
method call Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
user gesture event notification
reference 26/35
MVC = Model-View-Control Server Model state
state
state
state
state
state
query
change
change
query
change
change
View
View
user
view
user
view
gesture
selection
gesture
selection
Control
Control
Client1
Client2
call method Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
event notify 27/35
Počáteční interakce Application new Model( );
Model
View
new View( m ); register: addListener for state change
Control
new Control( m, v ); register: addListener
for user gesture
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
28/35
Interakce Model
View action1
user gesture
process
state change
set state change
get
Control
state query
process refresh
action2 show
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
user gesture
process
view selection
29/35
Využití MVC pro vlastní komponenty • Swingové komponenty zařídí automaticky pohled a řízení, my vhodně implementujeme model. Nejjednodušeji implementací příslušného abstraktního modelu, nebo zastíněním vhodného předka.
Příklad pro Spinner pro cyklicky organizované pracovní dny: (zde si ukážeme použití vlastní reprezentace dat, přestože by bylo vhodnější využít zděděný list) PracovniTyden prT = new PracovniTyden(); JSpinner jsp = new JSpinner(prT); getContentPane().add(jsp);
- registrace modelu u komponenty
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
30/35
Spinner pro pracovní dny class PracovniTyden extends SpinnerListModel{ List<String> prDny; int index; // předek má list a index a měli bychom je použít public PracovniTyden() { prDny = Arrays.asList( new String[] {"pondělí","úterý","středa","čtvrtek","pátek"}); Neaktualizujeme index!! } To provede setValue(..) public List> getList() { return prDny; } public Object getNextValue() { return (index >= (prDny.size() - 1)) ? prDny.get(0) : prDny.get(index + 1); } public Object getPreviousValue() { return (index <= 0) ? prDny.get(prDny.size() - 1) : prDny.get(index - 1); } Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
31/35
Spinner pro pracovní dny public Object getValue() { return prDny.get(index); } public void setValue(Object elt) { int pomIndex = prDny.indexOf(elt); if (pomIndex == -1) { throw new IllegalArgumentException("invalid sequence element"); }else if (pomIndex != this.index) { this.index = pomIndex; fireStateChanged(); } } } Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
32/35
Fire & listen Třída Component má 3 ( 9 ve verzi 1.5 i pro všechny primitivní typy ) přetížené metody pro vyslání informace o změně nějakého atributu: protected void firePropertyChange ( String propertyName, int oldValue, int newValue ) ( String propertyName, boolean oldValue, boolean newValue ) ( String propertyName, Object oldValue, Object newValue ) Pro připojení a odpojení posluchače má metody: public void addPropertyChangeListener( String propertyName, java.beans.PropertyChangeListener pcl ) public void removePropertyChangeListener( String propertyName, java.beans.PropertyChangeListener pcl ) Balíček java.beans definuje interface PropertyChangeListener obsahující jen metodu: public void propertyChange( java.beans.PropertyChangeEvent evt ) kde parametrem evt lze přistoupit k oldValue i newValue. Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
33/35
Interfejs Action je extenze interfejsu java.awt.event.ActionListener. void actionPerformed( ActionEvent ev ) - zděděná metoda void addPropertyChangeListener( PropertyChangeListener pcl ) void removePropertyChangeListener( PropertyChangeListener pcl ) Object getValue( String key ) void putValue( String key, Object value ) boolean isEnabled( ) setEnabled( boolean b )
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
34/35
AbstractAction implementuje interfejs Action – kromě metody abstract actionPerformed – je vhodným předkem pro tvorbu konkrétních handlerů. Přidává metody: PropertyChangeListener[ ] get PropertyChangeListeners( ) void firePropertyChange(String ptyName, Object oldVal, Object newVal) Object[ ] getKeys( ) Akci lze spouštět i z několika komponent, mohou ukázat tentýž text a ikonu definovanou v handleru. Příkladmo: Action a = new Act1(); JButton b = new JButton(a); JMenuItem m = new JMenuItem(a); JTextField t = new JTextField(...); // no label and icon t.setAction(a); class Act1 extends AbstractAction { public Act1( ... ) { super( label, icon ); ... } public void actionPerformed( ActionEvent ev ) { ... } }
Ing. Miroslav Balík, Ph.D. - BI-PJV- 09
35/35