Fejlett Programozási Technikák 2. 14/7
A mai előadás tartalma: JFC és Swing 2/1 Múlt Felépítés Felső szintű konténrek Középső szintű konténerek JFC és Swing 2/2 Elemek Eseménykezelés Rajzolás
JFC (Java Foundation Classes) olyan alkalmazások csoportja melyek segítségével GUI-t készíthetünk. 1997-ben a JavaOne konferencián mutatták be A következő csoportokra osztható: Swing komponenesek (nyomógomb, …) Beépíthető látvány támogatás (Java, Windows, …) Hozzáférést támogató API-k vakok részére Java 2D API (jó minőségű 2D képek készítése) Drag and Drop támogatás
Java Swing Nem tartalmaz natív kódot (az AWT tartalmaz) ezért teljesen platform független A Swing komponensek megjelenítését megadhatjuk (az AWT az aktuális platform megjelenítését használja) Az objektumok nevei J betűvel kezdődnek javax.swing.*; java.awt.*; java.awt.event.*;
A GUI felépítése Felső szintű konténerek helyet biztosítanak a többi komponens számára (JFrame, JDialog, JApplet) tartalmaznak egy content_pane nevű konténert mely közvetleül vagy közvetve tartalmaz minden elemet a menüt kivéve közvetlenül tartalmazhatja a menü elemet Középső szintű általános, speciális konténerek egyszerűbbé teszik az egyes elemek pozicionálását minden elem a menüt kivéve itt helyezkedik el az add() metódust használhatjuk az egyes elemek felhelyezésére Elemi komponensek
Megjelenítés(Layout management ) I. megállapítja az egyes komponensek helyét és méretét öt különböző menedzsert használhatunk: BorderLayout BoxLayout FlowLayout GridBagLayout GridLayout amikor az add() metódust használjuk figyelembe kell vennünk az egyes megjelenítések igényeit is (relatív pozíció) bármikor megválltoztatható JPanel pane = new JPanel(); pane.setLayout(new BorderLayout()); nem kötelező használni (pane.setLayout(NULL)), ekkor minden komponensnek meg kell adnunk a méretét és a helyét, hátránya, hogy nem alkalmazkodik a felső szintű konténre átméretezésekor, nem alkalmazkodik a különböző rendszerekhez, fontméretkehez
Megjelenítés(Layout management ) II. megadhatjuk a komponens minimum, alapértelmezett, maximális méretét (setMinimumSize, setPreferredSize, setMaximumSize ) megadhatjuk a rendezést is (setAlignmentX , setAlignmentY ) a komponensek közötti hely az alábbiaktól függ: Layout manager Láthatatlan komponensek Üres keretek
A méret megállapítása JFrame esetén: miután a GUI össze lett rakva a JFrame pack() metódusát meghívjuk, mely megpróbálja kideríteni a keret méretét a keret megjelenítés menedzselője hozzáadja a keret méretéhez a keret által közvetlenül tartalmazott elemek méretét (content pane, menu bar) a content pane mérete a megjelenítés menedzserétől függ GridLayout esetén például megpróbál minden elemet egyforma méretűre venni (a legnagyobb méretet veszi fel) megkérdezi az általa tartalmazott elemek az alapértelmezett méreteit
Esemény kezelés event source event listener minden eseménytípusra külön osztály használandó: ActionListener WindowListener MouseListener MouseMotionListener ComponentListener FocusListener ListSelectionListener az események objektumként jelennek meg, melyek sok hasznos tulajdonsággal rendelkeznek (esemény forrása, információ az eseményről …) egy esemény forráshoz több esemény kezelő csatlakozhat az esemény kezelésért egy szál a felelős (event-dispatching thread ). Az események kezelése egymás után történik !
Esemény kezelés megvalósítása a megfelelő interfészt meg kell valósítani public class MyClass implements ActionListener { regisztrálni kell a megfelelő esemény forrásnál esemény kezelőnket someComponent.addActionListener(instanceOfMyClass); meg kell valósítani az interfészt public void actionPerformed(ActionEvent e) { ...//code that reacts to the action... } általában névtelen belső osztályt használunk erre a célra button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { numClicks++; label.setText(labelPrefix + numClicks);} } ); célszerű gyors kezelőket írni, vagy ha ez nem oldható meg akkor új szálat indítani !
Rajzolás szintén az event-dispatching szálban van amikor a Swing GUI-t újra kell rajzolni akkor a legfelső komponenst rajzolja újra és halad lefelé a hierarchiában az egyes komponensek automatikusan újrarajzolják, méretezik magukat revalidate(), repaint() a gördülékeny működés érdekében a Swing az ún. double-buffered technológiát alkalmazza. Előbb a bufferbe összerakja a GUI-t és utána innen másolja ki a képernyőre gyorsíthatjuk a rajzolást ha az egyes elemekre beállítjuk az opaque tulajdonságot (setOpaque(true) ). Ilyenkor az alatta lévő objektumokat nem rajzolja ki.
Rajzolási sorrend: Speciális grafika Gyermek objektum Háttér Keret
Swing áttekintés Felirat Gomb Eseménykezelő Alsó Konténer Megjelenés import javax.swing.*; import java.awt.*; import java.awt.event.*; public class SwingApplication { private static String labelPrefix = ”Kattintások száma: "; private int numClicks = 0; public Component createComponents() { final JLabel label = new JLabel(labelPrefix + "0 "); JButton button = new JButton("I'm a Swing button!"); button.setMnemonic(KeyEvent.VK_I); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { numClicks++; label.setText(labelPrefix + numClicks);}}); label.setLabelFor(button); JPanel pane = new JPanel(); pane.setBorder(BorderFactory.createEmptyBorder(30,30,10,30)); pane.setLayout(new GridLayout(0, 1)); pane.add(button); pane.add(label); return pane; } public static void main(String[] args) { try { UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { } JFrame frame = new JFrame("SwingApplication"); SwingApplication app = new SwingApplication(); Component contents = app.createComponents(); frame.getContentPane().add(contents, BorderLayout.CENTER); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);}}); frame.pack(); frame.setVisible(true); } } Swing áttekintés Felirat Gomb Eseménykezelő Alsó Konténer Megjelenés Felső Konténer Eseménykezelő
Swing komponensek I. Felső szintű tárolók Általános célú tárolók Applet Dialog Frame Általános célú tárolók Panel Scroll pane Split pane Tabbed pane Tool bar
Swing komponensek II. Speciális célú tárolók: Belső keret (Internal Frame) Réteges tábla (Layered pane) Gyökér tábla (Root pane)
Swing komponensek III. Egyszerű vezérlők: Gombok Legördülő lista Lista Menü Csúszka (Slider) Szöveg mező
Swing komponensek IV. információ közlő elemek (nem írható): Felirat (Label) Progress bar Tool tip
Swing komponenesek V. információ közlő elemek (írható): Szín választó Fájl választó Táblázat Szöveg Fa
JComponent osztály őse az AWT csomag Container osztálya a felső szintű tárolókat kivéve minden objektum belőle származik szolgáltatásai: eszköz tippek (Tool Tips) keretek (borders) billentyűzet események kezelése alkalmazásfüggő megjelenítés tulajdonságok kezelése (név érték párként , putClientProperty ) megjelenítés definiálása (setMaximumSize, …) speciális hozzáférés támogatása (brail) dupla tároló (double buffer)
Felső szintű tárolók használata I. három általánosan használható felső szintű tároló van. a képernyőn való megjelenéshez szükség van egy konténer hierarchiára melynek gyökere egy felső szintű konténer egy alkalmazásban több konténer szerkezete is lehet (egy főablak két dialógus ablakkal) minden felső szintű vezérlő tartalmaz egy content pane-t tartalmazhatnak menüt is getContentPane() - Container objektumot ad vissza nem JComponent objektumot. ha ki akarjuk használni a JComponent osztály lehetőségeit akkor át kell alakítanunk (cast) vagy egy saját objektumot kell létrehoznunk minden felső szintű tároló rendelkezik egy gyökér lappal is melyet csak speciális esetekben kell használnunk
Felső szintű tárolók használata II. új objektummal: JPanel contentPane = new JPanel(); contentPane.setLayout(new BorderLayout()); contentPane.setBorder(someBorder); contentPane.add(someComponent, BorderLayout.CENTER); contentPane.add(anotherComponent, BorderLayout.SOUTH); topLevelContainer.setContentPane(contentPane); átakalkítva: JComponent x = (JComponent) frame.getContentPane(); x.setLayout(new BorderLayout()); x.setBorder(someBorder); x.add(someComponent, BorderLayout.CENTER); x.add(anotherComponent, BorderLayout.SOUTH);
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class TopLevelDemo { public static void main(String s[]) { JFrame frame = new JFrame("TopLevelDemo"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); JLabel yellowLabel = new JLabel(""); yellowLabel.setOpaque(true); yellowLabel.setBackground(Color.yellow); yellowLabel.setPreferredSize(new Dimension(200, 180)); JMenuBar cyanMenuBar = new JMenuBar(); cyanMenuBar.setOpaque(true); cyanMenuBar.setBackground(Color.cyan); cyanMenuBar.setPreferredSize(new Dimension(200, 20)); frame.setJMenuBar(cyanMenuBar); frame.getContentPane().add(yellowLabel, BorderLayout.CENTER); frame.pack(); frame.setVisible(true);
Keretek (frames) a JFrame osztály példányaként valósítható meg megjelenítése platformfüggő JFrame(”név") getContentPane() pack() setVisible()
Dialógus ablakok I. korlátozottabb képességekkel bírnak mint a keretek minden dialógus egy keretből származik amikor a keretet bezárjuk bezáródnak a hozzá tartozó dialógus ablakok is lehet modális (ekkor csak ő fogadja az inputot) saját dialógus ablakot a JDialog osztály segítségével tudunk létrehozni létrehoz egy gyökér hátlapot támogatja az ablak bezárást hasonló funkciói vannak mint a JFrame-nak
Dialógus ablakok II. egyszerű dialógusablakot a JOptionPane osztályból célszerű példányosítani JOptionPane.showMessageDialog(frame, "Egy minta dialógus ablak"); az alábbi típusokat használhatjuk: alapértelmezett figyelmeztetés (JOptionPane.WARNING_MESSAGE ) kérdés (JOptionPane.QUESTION_MESSAGE ) hiba (JOptionPane.ERROR_MESSAGE ) ikon nélküli (JOptionPane.PLAIN_MESSAGE ) saját ikon (JOptionPane.INFORMATION_MESSAGE, icon ) a kérdés beállítása: DEFAULT_OPTION YES_NO_OPTION, YES_NO_CANCEL_OPTION OK_CANCEL_OPTION.
Dialógus ablak III. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class teszt { public static void main(String s[]) { JFrame frame = new JFrame("TopLevelDemo"); Object[] options = {”Igen", "Nem", "Nem ha mondom!"}; int n = JOptionPane.showOptionDialog(frame, ”Szerinted ez működni fog" + ”minden további nélkül?", ”Jó kérdés", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]); System.exit(0); };}
Appletek JApplet a java.applet.Applet osztályból származik hasonlóan kezelhető mint a keret
Közbülső tárolók arra szolgálnak, hogy más elemeket tartalmazzonak a következő típusai vannak: Panel – A lekgflexibilisebb. Gyakran használják közbülső tárolóként, a JPanel osztály írja le. Az elemek csoportosítására is gyakran használatos. Scroll Pane – görgetősávokat biztosít Split Pane – két részre osztja a területet Tabbed Pane – több komponenst is tartalmazhat de csak egy látható Tool Bar – egy elemcsoportot tartalmaz oszlopba, vagy sorba rendezve melyet a felhasználó áthelyezhet
Hátrészek (Panel) I. általános célú tároló az egyszerű komponensek számára alapértelmezésként csak a saját hátterét rajzolja meg egy speciális függvény felülírásával rajzolhatunk is (paintComponent()) public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(image, 0, 0, this); g.drawImage(image, 90, 0, 300, 62, this); }
Hátrészek (Panel) II. beállíthatjuk az elrendezést JPanel aPanel = new JPanel(); aPanel.setLayout(new BorderLayout()); JPanel aPanel = new JPanel(new BorderLayout()); komponenseket adhatunk hozzá: aFlowPanel.add(aComponent); aFlowPanel.add(anotherComponent); amikor BorderLayout elrendezést használunk akkor specifikélni kell az elem helyét aBorderPanel.add(aComponent, BorderLayout.CENTER); aBorderPanel.add(anotherComponent, BorderLayout.SOUTH);
Hátrészek (Panel) III. Fontosabb metódusok: void add(Component, int) int getComponentCount() Component getComponent(int) void remove(Component) void remove(int) void removeAll() void setLayout(LayoutManager) LayoutManager getLayout()
Scroll Panes a komponens gördíthető nézetét biztosítja megadhatjuk a görgető sávok jelenlétét JScrollPane(Component, int, int) JScrollPane(int, int) VERTICAL_SCROLLBAR_AS_NEEDED HORIZONTAL_SCROLLBAR_AS_NEEDED VERTICAL_SCROLLBAR_ALWAYS HORIZONTAL_SCROLLBAR_ALWAYS VERTICAL_SCROLLBAR_NEVER HORIZONTAL_SCROLLBAR_NEVER
Scroll Panes díszítések: setColumnHeaderView(columnView); setRowHeaderView(rowView); setCorner(JScrollPane.UPPER_LEFT_CORNER, buttonCorner); a Scrollable interfész megvalósításával szemályre szabhatjuk a görgetést a kívánt objektumon getScrollableBlockIncrement getScrollableUnitIncrement getPreferredScrollableViewportSize getScrollableTracksViewportHeight getScrollableTracksViewportWidth
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { //lekérdezi a jelenlegi poziciót int currentPosition = 0; if (orientation == SwingConstants.HORIZONTAL) currentPosition = visibleRect.x; else currentPosition = visibleRect.y; //kiszámítja a pixelek számát a jelenlegi pozíció és a legközelebbi egység között if (direction < 0) { int newPosition = currentPosition - (currentPosition / maxUnitIncrement) * maxUnitIncrement; return (newPosition == 0) ? maxUnitIncrement : newPosition; } else { return ((currentPosition / maxUnitIncrement) + 1) * maxUnitIncrement - currentPosition; }
Példa: köröket helyezhetünk el melyek dinamikusan bővítik a komponens területét
Példa public ScrollDemo2() {… drawingArea.setBackground(Color.white); drawingArea.addMouseListener(new MyMouseListener()); JScrollPane scroller = new JScrollPane(drawingArea); scroller.setPreferredSize(new Dimension(200,200)); setLayout(new BorderLayout()); add(instructionPanel, BorderLayout.NORTH); add(scroller, BorderLayout.CENTER); } class MyMouseListener extends MouseInputAdapter { final int W = 100; final int H = 100; public void mouseReleased(MouseEvent e) { boolean changed = false; ... if (changed) { drawingArea.setPreferredSize(size); drawingArea.revalidate(); drawingArea.repaint(); Példa
public static void main (String args[]) { JFrame frame = new JFrame("ScrollDemo2"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} }); frame.setContentPane(new ScrollDemo2()); frame.pack(); frame.setVisible(true); }
Osztott hátrészek (Split Panes) két objektumot helyez el egymás mellett a felhasználó menet közben dinamikusan változtathatja a határt több részre is feloszthatjuk a területet az egyes elemek egymásbaágyazásával gyakran előbb Scroll Pane-ba helyezzük az objektumokat és csak ezután helyezzük a Split Pane - ba
Megvalósítás splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listScrollPane, pictureScrollPane); splitPane.setOneTouchExpandable(true); splitPane.setDividerLocation(150); splitPane.setDividerLocation(0.25); A minimális méretnél nem lehetnek kisebbek az egyes objektumok Dimension minimumSize = new Dimension(100, 50); listScrollPane.setMinimumSize(minimumSize); pictureScrollPane.setMinimumSize(minimumSize);
Egymásba ágyazás SplitPaneDemo splitPaneDemo = new SplitPaneDemo(); JSplitPane top = splitPaneDemo.getSplitPane(); label = new JLabel("Click on an image name in the list.", JLabel.CENTER); JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, top, label);
Címkés hátlapok (Tabbed Panes) egy helyen több elem (általában Panel) helyezkedhet el JTabbedPane(JTabbedPane.LEFT); (TOP, BOTTOM, LEFT, RIGHT) insertTab(String, Icon, Component, String, int) removeTabAt(int) void setSelectedIndex(int)
import javax.swing.JTabbedPane; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JFrame; import java.awt.*; import java.awt.event.*; public class TabbedPaneDemo extends JPanel { public TabbedPaneDemo() { ImageIcon icon = new ImageIcon("images/middle.gif"); JTabbedPane tabbedPane = new JTabbedPane(); Component panel1 = makeTextPanel("Blah"); tabbedPane.addTab("One", icon, panel1, "Does nothing"); tabbedPane.setSelectedIndex(0); … setLayout(new GridLayout(1, 1)); add(tabbedPane); } protected Component makeTextPanel(String text) { JPanel panel = new JPanel(false); JLabel filler = new JLabel(text); filler.setHorizontalAlignment(JLabel.CENTER); panel.setLayout(new GridLayout(1, 1)); panel.add(filler); return panel; }
public static void main(String[] args) { JFrame frame = new JFrame("TabbedPaneDemo"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} }); frame.getContentPane().add(new TabbedPaneDemo(), BorderLayout.CENTER); frame.setSize(400, 125); frame.setVisible(true); }
Eszköztárak a felhasználó által mozgatható JToolBar(String, int) add(Component) addSeparator() void setFloatable(boolean)
Példa: public ToolBarDemo() { ... JToolBar toolBar = new JToolBar(); addButtons(toolBar); JPanel contentPane = new JPanel(); contentPane.setLayout(new BorderLayout()); contentPane.add(toolBar, BorderLayout.NORTH); contentPane.add(scrollPane, BorderLayout.CENTER); ...} protected void addButtons(JToolBar toolBar) { JButton button = null; button = new JButton(new ImageIcon("images/left.gif")); toolBar.add(button); button = new JButton(new ImageIcon("images/middle.gif")); } Példa:
Belső keretek segítségével JFrame szerű ablakokat tudunk egy másik ablakban megnyitni általában JDesktopPane objektumot használunk gyökér objektumnak
desktop = new JDesktopPane(); createFrame(); setContentPane(desktop); ... desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); ... protected void createFrame() { MyInternalFrame frame = new MyInternalFrame(); frame.setVisible(true); desktop.add(frame); try { frame.setSelected(true); } catch (java.beans.PropertyVetoException e) {}} static int openFrameCount = 0; static final int xOffset = 30, yOffset = 30; public MyInternalFrame() { super("Document #" + (++openFrameCount), true, //resizable true, //closable true, //maximizable true);//iconifiable setLocation(xOffset*openFrameCount, yOffset*openFrameCount);}
Réteges hátlap (Layered Pane) egy harmadik dimmenziót biztosít (z order) minen elem mely tartalmaz root pane – t tartalmaz layered pane – t is. nincs megjelenítés vezérlője (ezt nekünk kell megírnunk) layeredPane = new JLayeredPane(); layeredPane.setPreferredSize(new Dimension(300, 310)); layeredPane.setBorder(BorderFactory.createTitledBorder( "Move the Mouse to Move Duke")); layeredPane.addMouseMotionListener(new MouseMotionAdapter() { ... }); for (int i = 0; i < ...number of labels...; i++) { JLabel label = createColoredLabel(...); layeredPane.add(label, new Integer(i)); }
Gyökér keret négy részből áll: üveg hátlap (glass pane) réteges hátlap (layered pane) tartalom hátlap (content pane) menü (opcionális)
Üveg hátlap (Glass Pane) akkor hasznos ha olyan eseményeket akarunk kezelni melyek egy vagy több elem által elfoglalt területen következtek be több objektum fölé is rajzolhatunk vele
Pél frame.getContentPane()); frame.setGlassPane(myGlassPane); … class MyGlassPane extends JComponent { Point point; public void paint(Graphics g) { if (point != null) { g.setColor(Color.red); g.fillOval(point.x - 10, point.y - 10, 20, 20); } public void setPoint(Point p) { point = p; public MyGlassPane(AbstractButton aButton, JMenuBar menuBar, Container contentPane) { CBListener listener = new CBListener(aButton, menuBar, this, contentPane); addMouseListener(listener); addMouseMotionListener(listener); }} Pél
Egyszerű komponensek I. a JComponent osztályból származnak az összes szolgáltatásával rendelkeznek (keretvonalak, …): Button, Check Box, Radio Button - egyszerűen használható egyszerűen testreszabható gombot nyújtanak Combo Box - Írható és csak olvasható legördülő listát egyaránt biztosít List Displays – elemcsoportot jelenít meg melyek közül a felhasználó választhat Menu - menü, sok különböző elemet tartalmazhat Slider – lehetővé teszi a felhasználó számára, hogy egy folytonos értékhalmazból válasszon Spinner - kicsi le és fel nyilak segítségével tudunk választani egy diszkrét elemhalmazból Text Field – egy szöveg sor bevitelére alkalmas
Egyszerű komponensek II. Néhány elemi komponens csak információ nyújtást biztosít: Label – szöveget és ikonokat tartalmazhat Progress Bar - a célig hátralévő és a megtett munka közötti különbséget mutatja Tool Tip – minden elem fölött megjelenhet egy kis felirat A többi elemi komponens az információ megjelenítésére és bevitelére egyaránt alkalmas: Color Chooser – Szín választó mely dialógus ablakon kívül és belül is lehet File Chooser – Fájl választó Table – Nagyon rugalmas táblázat megjelenítő Text Support - Egy keret szövegek kezelésére Tree – komponens hierarchia megjelenítő