Graafilise kasutajaliidese programmeerimine II Paketid javax.swing ja javax.swing.event Liideseelementide hierarhia Swing ei ole täiesti iseseisev ja AWT-st sõltumatu, nii liideseelemendid kui ka sündmused on seotud AWT-ga. Swing-komponentide klasside nimed algavad J-ga. AWT-komponendid on "tugevad": ei saa paigutada "puhast" swing-komponenti AWT-komponendi "ette". Component (awt) Container (awt) Panel (awt) Applet (applet) JApplet (swing) Window (awt) Dialog (awt) JDialog (swing) Frame (awt) JFrame (swing) JWindow (swing) Box (swing) CellRendererPane (swing) JComponent (swing) AbstractButton (swing) JButton (swing) JMenuItem (swing) JCheckBoxMenuItem (swing) JMenu (swing) JRadioButtonMenuItem (swing) JToggleButton (swing) JCheckBox (swing) JRadioButton (swing) JColorChooser (swing) JComboBox (swing) JFileChooser (swing) JInternalFrame (swing) JLabel (swing) JLayeredPane (swing) JDesktopPane (swing) JList (swing) JMenuBar (swing) JOptionPane (swing) JPanel (swing) JPopupMenu (swing) JProgressBar (swing) JRootPane (swing) JScrollBar (swing) JScrollPane (swing) JSeparator (swing) JSlider (swing) JSplitPane (swing) JTabbedPane (swing) JTable (swing) JTextComponent (swing.text) JEditorPane (swing) JTextPane (swing) JTextArea (swing) JTextField (swing) JPasswordField (swing) JToolBar (swing) JToolTip (swing) JTree (swing) JViewport (swing) Box.Filler (swing) <- Component (awt) ButtonGroup (swing) <- Object (lang) Mitmekihilised elemendid JFrame (swing-paketi raam), samuti JDialog,JWindow ning JApplet, osaliselt ka JInternalFrame, sisaldavad vahendeid mitmekihiliseks joonistamiseks (kasulik näiteks animatsioonide tegemisel). Klassid JLayeredPane, JDesktopPane ning JRootPane ongi kihtidega manipuleerimiseks. Iga JFrame-objekt sisaldab rootPane objekti klassist JRootPane (konteinerit), mis katab kogu raami nähtava osa. Selles konteineris sisaldub veel vähemalt kaks objekti: 1. glassPane (klassist JPanel) 2. layeredPane (klassist JLayeredPane) layeredPane sisaldab contentPane (samuti klassist JPanel) ning mittekohustusliku menüüriba menuBar (klassist JMenuBar). glassPane ja layeredPane katavad kogu rootPane nähtava osa, contentPane on äärte ning menüüriba jagu väiksem. Tavaliselt kasutataksegi seda "tagumist" kihti (numbriga -30000), mille annab getContentPane() nii, nagu AWT puhul konteinerit ennast (paigutushalduri määramine, komponentide lisamine jne.). contentPane vaikimisi paigutushalduriks on BorderLayout. Kihte saab juurde lisada layeredPane "konteinerisse": getLayeredPane().add (komponent, new Integer (number)); Mida suurem number, seda "eespool" vastav kiht on. layeredPane ei oma vaikimisi paigutushaldurit (see on null) ning paigutushalduri seisukohalt on layeredPane üks tervik (kihte ei saa eraldi juhtida). Kihtide vahetamiseks setLayer meetod (JLayeredPane). Kõige "ees" on läbipaistev glassPane (rootPane esimene alluv). Vaikimisi on see nähtamatu ning sündmused "paistavad läbi" selle kihi (saab kasutada näit. "alati peal" olevate komponentide paigutamiseks). Uued sündmused MouseInputAdapter realiseerib MouseInputListener liidese, mis omakorda realiseerib nii MouseListener kui ka MouseMotionListener liidesed awt-paketist. Uued liideseelemendid (näit. JTree) lisavad ka uusi sündmusetüüpe. AncestorListener liides lubab kuulata komponendi "eellastega" juhtunud sündmusi: ancestorAdded (AncestorEvent e) - pöördutakse, kui sündmuse allikas või mõni tema eellastest muudetakse nähtavaks ancestorMoved (AncestorEvent e) - pöördutakse, kui allikat või mõnda tema eellast liigutatakse ancestorRemoved (AncestorEvent e) - pöördutakse, kui allikas või mõni tema eellastest muudetakse nähtamatuks Näit.Kui nupp on raami sees ja nupul on AncestorListener kuular, siis raami liigutamisel saab nupp sellest teada ("allikas" on muide nupp). Kuna swing-komponendid on kõik konteinerid (awt Container alamklassid), siis on niisugune sündmusetüüp mugav. Graphics Graafiline kontekst Komponentide puhul saab ende ülejoonistamist tellida, kui pöörduda repaint()meetodi poole, aga enamasti toimub ülejoonistamine "iseenesest", kui komponendi nähtavus muutub. Ülejoonistamiseks pöördub graafikamootor vastava komponendi update(Graphics_g) meetodi poole, mida saab alamklassides üle katta, et realiseerida komponendi osalist muutmist (nn. clipping region). Terve komponendi ülejoonistamiseks tuleb üle katta paint(Graphics_g)(see on ka update üheks vaikimisi tegevuseks, kui me osalist muutmist ei soovi). Nii update kui ka paint annavad joonistamiseks nn. graafilise konteksti (Graphics klassist). repaint -> update -> clear, paint Joonistamiseks on ette nähtud komponendid Canvas (awt), Panel (s.h. Applet ja JApplet) ja JPanel, aga saab kasutada ka teisi swing-komponente. Graafiline kontekst annab: # joonistamispiirkonna # jooksva muutmispiirkonna (clipping region) (vt. awt Rectangle, samuti awt.geom paketti) # jooksva fondi (vt. awt Font, FontMetrics) # jooksva värvi (vt. awt Color) # joonistamisrezhiimi (paint mode) Iga paint-meetodi poole pöördumine annab uue graafilise konteksti (kontekstis tehtud muutused ei säili, aga konteksti initsialiseerimisel võetakse informatsioon vastavast komponendist). Koordinaadid Koordinaadid on pikselite vahel ja algavad nullist. x-koordinaati arvestatakse vasakult paremale ja y-koordinaati ülalt alla. Koordinaatide alguspunkti saab nihutada translate meetodiga. Joonistamine Joonistamisvahendid on Graphics klassis. draw... meetodid joonistavad piirjooni, fill... meetodid seest täidetud kujundeid. Teksti joonistamiseks on drawString (NB! alusjoone koordinaat, vt. FontMetrics). Näide. Paneeli keskele joonistatakse etteantud fonti (meetod votaFont() olgu defineeritud mujal) kasutades näidissõne. class JFondiNaide extends JPanel { public void paint (Graphics g) { String s = "ABCDabcd1234,.;"; // naide g.setFont (votaFont()); // font //============================================= // Arvutame teksti asukoha fondimeetrika alusel //============================================= FontMetrics fm = g.getFontMetrics(); int tekstikorgus = fm.getHeight(); int tekstilaius = fm.stringWidth (s); int kastikorgus = getSize().height; int kastilaius = getSize().width; if ((kastilaius < tekstilaius) || (kastikorgus < tekstikorgus)) { // throw new RuntimeException ("Ei mahu!"); } int x = (kastilaius - tekstilaius) / 2; int y = (kastikorgus - tekstikorgus) / 2 + fm.getAscent(); // alusjoone saamiseks g.setColor (Color.white); g.fillRect (0, 0, kastilaius, kastikorgus); g.setColor (Color.black); g.drawString (s, x, y); } // paint lopp } // JFondiNaide lopp Joonistamisrezhiimid "Tavaline" joonistamine jooksva värviga toimub setPaintMode() rezhiimis. Pilditöötluses on sageli tarvis nn. XOR-rezhiimis joonistamist, kus joonistatava punkti värv sõltub kolmest komponendist: 1. jooksev värv 2. ülejoonistatava punkti värv 3. setXORMode(altcolor) parameetrina antav n.-ö. alternatiivne värv Punkti uus värv saadakse nende kolme värvi bitiesitustest tehtega XOR (loogiline mitteekvivalents). Töö piltidega, pakett java.awt.image drawImage meetod ImageObserver liides ja imageUpdate meetod MediaTracker klass sünkroniseerimiseks getImage (Applet-klass) ja createImage meetod (Component klass) ImageProducer ja ImageConsumer liidesed, setPixels meetod, MemoryImageSource ja FilteredImageSource klassid ImageFilter ja ColorModel Klasside hierarhia Object (lang) Toolkit (awt) Color (awt) SystemColor (awt) Font (awt) FontMetrics (awt) Cursor (awt) GraphicsDevice (awt) GraphicsEnvironment (awt) Graphics (awt) Graphics2D (awt) DebugGraphics (swing) Dimension2D (awt.geom) Dimension (awt) Point2D (awt.geom) Point (awt) Polygon (awt) RectangularShape (awt.geom) Rectangle2D (awt.geom) Rectangle (awt) PrintJob (awt) MediaTracker (awt) <- Object (lang) Image (awt) ColorModel (awt.image) IndexColorModel (awt.image) PackedColorModel (awt.image) DirectColorModel (awt.image) FilteredImageSource (awt.image) ImageFilter (awt.image) CropImageFilter (awt.image) ReplicateScaleFilter (awt.image) AreaAveragingScaleFilter (awt.image) RGBImageFilter (awt.image) GrayFilter (swing) ImageIcon (swing) MemoryImageSource (awt.image) PixelGrabber (awt.image) Raster (awt.image) WritableRaster (awt.image) Suur näide FondiParameetrid klassi liidese pilt Realiseerime niisuguse fondivaliku dialoogi swing-komponentide abil. /** * Fail FondiParameetrid.java * @author Jaanus Poial * @version 0.4 kevad 99 * @since JDK 1.2 */ //====================================================== // Fondivaliku dialoog - swing //====================================================== import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import java.util.*; public class FondiParameetrid extends Hashtable { //=================================================== // FondiParameetrid on paisktabel (vt. java.util.*) // Loomulikult vo~iks see olla ka midagi muud //=================================================== boolean valmis = false; // su"nkroniseerimiseks FondiParameetrid () { // konstruktor //================================================ // Paisktabeli algva"a"rtustamine //================================================ put ("name", "Dialog"); put ("style", new Integer (Font.PLAIN)); put ("size", new Integer (14)); //================================================ // Va"lise raami loomine ja juhtimine //================================================ JFrame raam = new JFrame(); raam.setSize (350, 300); raam.setTitle ("Vali font"); raam.addWindowListener (new WindowAdapter() { public void windowClosing (WindowEvent e) { valmis = true; } }); //================================================ // Tegeliku liidese ehitamine on eraldi meetodina //================================================ looDialoog (raam.getContentPane()); raam.setVisible (true); valmis = false; while (!valmis) {}; // ootame dialoogi loppu raam.setVisible (false); // eemaldame akna } // konstruktori lopp //=================================================== // Kasutame Graphics-objekti saamiseks u"lekatmist //=================================================== class JFondiNaide extends JPanel { public void paint (Graphics g) { String s = "ABCDabcd1234,.;"; // naide g.setFont (votaFont()); // font //============================================= // Arvutame teksti asukoha fondimeetrika alusel //============================================= FontMetrics fm = g.getFontMetrics(); int tekstikorgus = fm.getHeight(); int tekstilaius = fm.stringWidth (s); int kastikorgus = getSize().height; int kastilaius = getSize().width; if ((kastilaius < tekstilaius) || (kastikorgus < tekstikorgus)) { // throw new RuntimeException ("Ei mahu!"); } int x = (kastilaius - tekstilaius) / 2; int y = (kastikorgus - tekstikorgus) / 2 + fm.getAscent(); // alusjoone saamiseks g.setColor (Color.white); g.fillRect (0, 0, kastilaius, kastikorgus); g.setColor (Color.black); g.drawString (s, x, y); } // paint lopp } // JFondiNaide lopp //=================================================== // Dialoogi loomine etteantud konteinerisse //=================================================== void looDialoog (Container kest) { //================================================ // Aknaelemendid //================================================ JPanel valikutePaneel = new JPanel(); JPanel nimePaneel = new JPanel(); JPanel ryhmaPaneel = new JPanel(); JFondiNaide fondiNaide = new JFondiNaide(); JLabel nimeSilt = new JLabel ("Nimi"); String [] fondid = GraphicsEnvironment. getLocalGraphicsEnvironment(). getAvailableFontFamilyNames(); JList nimed = new JList (fondid); JScrollPane nimePaan = new JScrollPane (nimed); JLabel suuruseSilt = new JLabel ("Suurus"); String [] psuurused = { "6", "8", "10", "12", "14","16","18","20","24","30","36","40" }; JComboBox suurused = new JComboBox (psuurused); JLabel stiiliSilt = new JLabel ("Stiil"); JCheckBox rasvane = new JCheckBox ("Paks kiri"); JCheckBox kaldkiri = new JCheckBox ("Kaldkiri"); JButton okNupp = new JButton ("Valmis"); //================================================ // Ka"itumine //================================================ nimed.addListSelectionListener (new ListSelectionListener() { public void valueChanged (ListSelectionEvent e) { String s = (String)((JList)e.getSource()). getSelectedValue(); put ("name", s); JFondiNaide n = (JFondiNaide) ((Container)((JPanel)((JPanel) ((JScrollPane)((JViewport)((JList)e. getSource()).getParent()).getParent()). getParent()).getParent()).getParent()). getComponent (0); n.repaint(); } } ); suurused.addItemListener (new ItemListener() { public void itemStateChanged (ItemEvent e) { int suurus = Integer.parseInt ((String) ((JComboBox)e.getSource()). getSelectedItem()); put ("size", new Integer (suurus)); JFondiNaide n = (JFondiNaide) ((Container)((JPanel)((JPanel) ((JComboBox)e.getSource()). getParent()).getParent()).getParent()). getComponent (0); n.repaint(); } } ); rasvane.addItemListener (new ItemListener() { public void itemStateChanged (ItemEvent e) { int stiil = ((Integer)(get ("style"))).intValue(); boolean olek = ((JCheckBox)e.getSource()). isSelected(); if (olek) stiil = stiil + Font.BOLD; else stiil = stiil - Font.BOLD; put ("style", new Integer (stiil)); JFondiNaide n = (JFondiNaide) ((Container)((JPanel)((JPanel) ((JCheckBox)e.getSource()).getParent()). getParent()).getParent()). getComponent (0); n.repaint(); } } ); kaldkiri.addItemListener (new ItemListener() { public void itemStateChanged (ItemEvent e) { int stiil = ((Integer)(get ("style"))).intValue(); boolean olek = ((JCheckBox)e.getSource()). isSelected(); if (olek) stiil = stiil + Font.ITALIC; else stiil = stiil - Font.ITALIC; put ("style", new Integer (stiil)); JFondiNaide n = (JFondiNaide) ((Container)((JPanel)((JPanel) ((JCheckBox)e.getSource()). getParent()).getParent()).getParent()). getComponent (0); n.repaint(); } } ); okNupp.addActionListener (new ActionListener() { public void actionPerformed (ActionEvent e) { valmis = true; } } ); //================================================ // Va"ljana"gemine //================================================ nimed.setVisibleRowCount (7); nimePaneel.setLayout (new BoxLayout (nimePaneel, BoxLayout.Y_AXIS)); nimePaneel.add (nimeSilt); nimePaneel.add (nimePaan); ryhmaPaneel.setLayout (new BoxLayout (ryhmaPaneel, BoxLayout.Y_AXIS)); ryhmaPaneel.add (suuruseSilt); ryhmaPaneel.add (suurused); ryhmaPaneel.add (stiiliSilt); ryhmaPaneel.add (rasvane); ryhmaPaneel.add (kaldkiri); ryhmaPaneel.add (okNupp); valikutePaneel.setLayout (new BoxLayout (valikutePaneel, BoxLayout.X_AXIS)); valikutePaneel.add (nimePaneel); valikutePaneel.add (ryhmaPaneel); kest.setLayout (new BoxLayout (kest, BoxLayout.Y_AXIS)); kest.add (fondiNaide); kest.add (valikutePaneel); nimed.setSelectedValue (get ("name"), true); suurused.setSelectedItem (get ("size").toString()); } // looDialoog lopp //=================================================== // Po~hiprogramm -- silumiskest //=================================================== public static void main (String[] parameetrid) { FondiParameetrid fp = new FondiParameetrid(); Font f = fp.votaFont(); System.out.println ("Valiti kiri: " + f); System.exit (0); } // main lopp //=================================================== // Fondi loomine parameetritest //=================================================== public Font votaFont() { return new Font ((String)get ("name"), ((Integer)(get ("style"))).intValue(), ((Integer)(get ("size"))).intValue()); } // votaFont lopp } // FondiParameetrid lopp Selle näite puhul saaks paljusid asju teha teisiti (lihtsamalt), aga on tahetud näidata erinevaid aspekte, mis liideste realiseerimisel ette võivad tulla.