{"id":360,"date":"2011-11-03T12:11:17","date_gmt":"2011-11-03T10:11:17","guid":{"rendered":"http:\/\/www.kristiansborg.dk\/bibliotek\/?page_id=360"},"modified":"2011-11-03T12:22:47","modified_gmt":"2011-11-03T10:22:47","slug":"kapitel-2-videre-med-swing","status":"publish","type":"page","link":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/","title":{"rendered":"Kapitel 2 Videre med Swing"},"content":{"rendered":"<div style=\"border: 1px solid red; color: red\">\n<b>Du l\u00e6ser en gammel bog<\/b><br \/>\nDen bog, du l\u00e6ser her, er fra 1998, og mange ting kan have \u00e6ndret sig siden da.<br \/>\nVi h\u00e5ber, at du stadig kan finde relevant information i den.<br \/>\nHvis du vil l\u00e6se aktuelle oplysninger om de avancerede dele af Java, anbefaler vi<br \/>\nbogen <a href=\"http:\/\/clk.tradedoubler.com\/click?p(197229)a(2029446)g(19172914)url(http:\/\/www.adlibris.com\/dk\/product.aspx?isbn=0132354799)\"  target=\"_blank\">Core Java &#8211; Advanced Features<\/a><img decoding=\"async\" src=\"http:\/\/impdk.tradedoubler.com\/imp?type(inv)g(19172914)a(2029446)\" \/>\n<\/div>\n<p>Forrige kapitel beskrev de grundl\u00e6ggende aspekter i forbindelse med Swing. I dette kapitel vil denne gennemgang forts\u00e6tte med de mere avancerede komponenter.<\/p>\n<p>&nbsp;<\/p>\n<h1>Menuer<\/h1>\n<p>&nbsp;<\/p>\n<p>En central del af enhver grafisk applikation er menuen. Her har brugeren mulighed for at v\u00e6lge blandt de funktioner, programmet stiller til r\u00e5dighed, og vedkommende har ogs\u00e5 mulighed for at se hvilke genvejstaster, der er knyttet til de forskellige menupunkter.<\/p>\n<p>&nbsp;<\/p>\n<p>Menuer i Swing minder i h\u00f8j grad om de menuer, der kendes fra AWT. Hvis man er kendt med opbygningen af disse, vil man hurtigt kunne s\u00e6tte sig ind i de nye muligheder, Swing giver.<\/p>\n<p>&nbsp;<\/p>\n<p>Swing indeholder en r\u00e6kke klasser, der hver repr\u00e6senterer en del af menuen.<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen JMenuItem repr\u00e6senterer et punkt i en menu.<\/p>\n<p>&nbsp;<\/p>\n<p>public class JMenuItem extends java.awt.swing.AbstractButton implements java.awt.accessibility.Accessible, java.awt.swing.MenuElement<\/p>\n<p>{<\/p>\n<p>public java.awt.swing.JMenuItem();<\/p>\n<p>public java.awt.swing.JMenuItem(java.awt.swing.Icon);<\/p>\n<p>public java.awt.swing.JMenuItem(java.lang.String);<\/p>\n<p>public java.awt.swing.JMenuItem(java.lang.String,int);<\/p>\n<p>public java.awt.swing.JMenuItem(java.lang.String,java.awt.swing.Icon);<\/p>\n<p>&nbsp;<\/p>\n<p>public java.awt.swing.KeyStroke getAccelerator();<\/p>\n<p>public java.awt.Component getComponent();<\/p>\n<p>public java.awt.swing.MenuElement[] getSubElements();<\/p>\n<p>public boolean isArmed();<\/p>\n<p>public void menuSelectionChanged(boolean);<\/p>\n<p>public void processKeyEvent(java.awt.event.KeyEvent, java.awt.swing.MenuElement[], java.awt.swing.MenuSelectionManager);<\/p>\n<p>public void processMouseEvent(java.awt.event.MouseEvent, java.awt.swing.MenuElement[], java.awt.swing.MenuSelectionManager);<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>public void setAccelerator(java.awt.swing.KeyStroke);<\/p>\n<p>public void setArmed(boolean);<\/p>\n<p>public void setEnabled(boolean);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Som det ses af metoderne getAccelerator og setAccelerator, kan man i Swing koble en genvejstast til et menupunkt. Genvejstaster vil blive beskrevet senere i dette kapitel. Funktionen getArmed angiver, om der er ved at blive klikket p\u00e5 menupunktet. Hvis musen er blevet trykket ned over menupunktet og endnu ikke sluppet, vil getArmed returnere sandt. Hvis musen bliver sluppet over menupunktet, vil den handling, der er tilknyttet punktet, blive udf\u00f8rt. Hvis musen bliver sluppet et andet sted, sker der intet, og setArmed vil atter returnere falsk.<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen JCheckBoxMenuItem nedarver fra JMenuItem. JCheckBoxMenuItem angiver et menupunkt, der enten kan v\u00e6re markeret eller ikke markeret. Ligesom den tilsvarende komponent JCheckBox er JCheckBoxMenuItem uafh\u00e6ngig af andre menupunkter.<\/p>\n<p>&nbsp;<\/p>\n<p>public class JCheckBoxMenuItem extends java.awt.swing.JMenuItem implements java.awt.swing.SwingConstants, java.awt.accessibility.Accessible<\/p>\n<p>{<\/p>\n<p>public java.awt.swing.JCheckBoxMenuItem(java.lang.String,java.awt.swing.Icon,boolean);<\/p>\n<p>public java.awt.swing.JCheckBoxMenuItem(java.lang.String,boolean);<\/p>\n<p>&nbsp;<\/p>\n<p>public synchronized java.lang.Object[] getSelectedObjects();<\/p>\n<p>public boolean getState();<\/p>\n<p>public synchronized void setState(boolean);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Allerede i constructoren kan man angive, om menupunktet skal v\u00e6re markeret. Senere kan man bruge metoderne getState og setState til henholdsvis at se og \u00e6ndre menupunktets tilstand. Metoden getSelectedObjects er speciel. Den returnerer et array med l\u00e6ngden 1, hvis menupunktet er markeret. Indholdet af array\u2019et er navnet p\u00e5 den tekst, der st\u00e5r ved menupunktet. Hvis menupunktet ikke er markeret, returneres null.<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen JRadioButtonMenuItem nedarver ogs\u00e5 fra JMenuItem. Klassen afspejler komponentet JRadioButton, der som bekendt er en knap i en gruppe, hvoraf kun \u00e9n kan v\u00e6re markeret. Klassen har samme signatur som JMenuItem \u2013 dog er enkelte af metoderne redefineret. Til at angive hvilke objekter, der h\u00f8rer sammen, bruger man klassen ButtonGroup. Denne bruges p\u00e5 pr\u00e6cis samme m\u00e5de som ved almindelige radioknapper (alternativknapper). Anvendelsen af ButtonGroup-klassen blev gennemg\u00e5et i kapitel 1.<\/p>\n<p>&nbsp;<\/p>\n<p>De forskellige menupunkter skal naturligvis samles i en menu. Det kan man g\u00f8re ved hj\u00e6lp af klassen JMenu.<\/p>\n<p>&nbsp;<\/p>\n<p>public class JMenu extends java.awt.swing.JMenuItem implements java.awt.accessibility.Accessible,<\/p>\n<p>&nbsp;<\/p>\n<p>java.awt.swing.MenuElement<\/p>\n<p>{<\/p>\n<p>public java.awt.swing.JMenu();<\/p>\n<p>public java.awt.swing.JMenu(java.lang.String);<\/p>\n<p>public java.awt.swing.JMenu(java.lang.String,boolean);<\/p>\n<p>&nbsp;<\/p>\n<p>public java.awt.Component add(java.awt.Component);<\/p>\n<p>public java.awt.swing.JMenuItem add(java.awt.swing.Action);<\/p>\n<p>public java.awt.swing.JMenuItem add(java.awt.swing.JMenuItem);<\/p>\n<p>public void add(java.lang.String);<\/p>\n<p>public void addMenuListener(java.awt.swing.event.MenuListener);<\/p>\n<p>public void addSeparator();<\/p>\n<p>public int getDelay();<\/p>\n<p>public java.awt.swing.JMenuItem getItem(int);<\/p>\n<p>public int getItemCount();<\/p>\n<p>public java.awt.Component getMenuComponent(int);<\/p>\n<p>public int getMenuComponentCount();<\/p>\n<p>public java.awt.Component[] getMenuComponents();<\/p>\n<p>public java.awt.swing.JPopupMenu getPopupMenu();<\/p>\n<p>public java.awt.swing.MenuElement[] getSubElements();<\/p>\n<p>public java.awt.swing.JMenuItem insert(java.awt.swing.Action, int);<\/p>\n<p>public java.awt.swing.JMenuItem insert(java.awt.swing.JMenuItem, int);<\/p>\n<p>public void insert(java.lang.String, int);<\/p>\n<p>public void insertSeparator(int);<\/p>\n<p>protected void processKeyEvent(java.awt.event.KeyEvent);<\/p>\n<p>public void processKeyEvent(java.awt.event.KeyEvent, java.awt.swing.MenuElement[], java.awt.swing.MenuSelectionManager);<\/p>\n<p>public void processMouseEvent(java.awt.event.MouseEvent, java.awt.swing.MenuElement[], java.awt.swing.MenuSelectionManager);<\/p>\n<p>public void remove(int);<\/p>\n<p>public void remove(java.awt.swing.JMenuItem);<\/p>\n<p>public void removeAll();<\/p>\n<p>public void removeMenuListener(java.awt.swing.event.MenuListener);<\/p>\n<p>public void setAccelerator(java.awt.swing.KeyStroke);<\/p>\n<p>public void setDelay(int);<\/p>\n<p>public void setMenuLocation(int, int);<\/p>\n<p>public void setPopupMenuVisible(boolean);<\/p>\n<p>public void setSelected(boolean);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen indeholder naturligvis en add-metode, der tilf\u00f8jer et JMenuItem-objekt til menuen. Derudover er der mulighed for at tilf\u00f8je komponenter, tekststrenge og ogs\u00e5 Action-objekter. Disse vil blive beskrevet senere. F\u00e6lles for alle add-metoderne er, at de tilf\u00f8jer elementerne i slutningen af menuen.<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis man vil inds\u00e6tte et menupunkt p\u00e5 en bestemt position, skal man bruge insert-metoden.<\/p>\n<p>&nbsp;<\/p>\n<p>For at g\u00f8re menuen mere overskuelig kan man v\u00e6lge at opdele den ved hj\u00e6lp af seperatorer. Disse kan tilf\u00f8jes ved hj\u00e6lp af addSeperator og inds\u00e6ttes med insertSeperator-metoden.<\/p>\n<p>&nbsp;<\/p>\n<p>Den tid, der g\u00e5r, fra brugeren klikker p\u00e5 menuens overskrift, til menuen vises, kan \u00e6ndres med setDelay-metoden. Parametret til denne funktion er l\u00e6ngden af forsinkelsen i milisekunder. Hvis man vil vide, hvor stor forsinkelsen er, kan man bruge getDelay-funktionen.<\/p>\n<p>&nbsp;<\/p>\n<p>JMenu-klassen indeholder en r\u00e6kke funktioner, der kan returnere information om menuen. Det drejer sig blandt andet om antallet af menupunkter inklusiv adskillere og antallet af komponenter i menuen. Klassen indeholder naturligvis ogs\u00e5 funktioner til at opn\u00e5 referencer til de enkelte menupunkter.<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r man opretter JMenu-objektet, kan man v\u00e6lge, om menuen skal kunne \u201crives af\u201d menulinien og placeres flydende p\u00e5 sk\u00e6rmen. Det g\u00f8r man ved at angive true i kaldet til constructoren. Man kan herefter selv angive menuens position ved at kalde metoden setMenuLocation.<\/p>\n<p>&nbsp;<\/p>\n<p>Ligesom de \u00f8vrige elementer i en menu har JMenu-objekter mulighed for at f\u00e5 en genvejstast tilknyttet. Disse gennemg\u00e5s senere.<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis man \u00f8nsker en undermenu i sin menu, kan det ogs\u00e5 lade sig g\u00f8re. I s\u00e5 fald skal det v\u00e6re en popup-menu, som i Swing repr\u00e6senteres af klassen JPopupMenu. Popup-menuer kan ogs\u00e5 bruges som lokalmenuer, der \u00e5bnes, n\u00e5r brugeren eksempelvis h\u00f8jre-klikker p\u00e5 en komponent i brugergr\u00e6nsefladen. JPopupMenu-klassen minder p\u00e5 mange punkter om JMenu, og som det ses af nedenst\u00e5ende signatur, er der ogs\u00e5 en del sammenfald mellem klassernes metoder.<\/p>\n<p>&nbsp;<\/p>\n<p>public class JPopupMenu extends java.awt.swing.JComponent implements java.awt.accessibility.Accessible, java.awt.swing.MenuElement<\/p>\n<p>{<\/p>\n<p>public java.awt.swing.JPopupMenu();<\/p>\n<p>public java.awt.swing.JPopupMenu(java.lang.String);<\/p>\n<p>&nbsp;<\/p>\n<p>public java.awt.Component add(java.awt.Component);<\/p>\n<p>public java.awt.swing.JMenuItem add(java.awt.swing.Action);<\/p>\n<p>public java.awt.swing.JMenuItem add(java.awt.swing.JMenuItem);<\/p>\n<p>public void addPopupMenuListener(java.awt.swing.event.PopupMenuListener);<\/p>\n<p>public void addSeparator();<\/p>\n<p>public java.awt.Component getComponent();<\/p>\n<p>public java.awt.Component getComponentAtIndex(int);<\/p>\n<p>public int getComponentIndex(java.awt.Component);<\/p>\n<p>public java.lang.String getLabel();<\/p>\n<p>public java.awt.Insets getMargin();<\/p>\n<p>public java.awt.swing.MenuElement[] getSubElements();<\/p>\n<p>public void insert(java.awt.Component, int);<\/p>\n<p>&nbsp;<\/p>\n<p>public void insert(java.awt.swing.Action, int);<\/p>\n<p>public boolean isBorderPainted();<\/p>\n<p>public boolean isLightWeightPopupEnabled();<\/p>\n<p>public void menuSelectionChanged(boolean);<\/p>\n<p>public void processKeyEvent(java.awt.event.KeyEvent, java.awt.swing.MenuElement[], java.awt.swing.MenuSelectionManager);<\/p>\n<p>public void processMouseEvent(java.awt.event.MouseEvent, java.awt.swing.MenuElement[], java.awt.swing.MenuSelectionManager);<\/p>\n<p>public void removePopupMenuListener(java.awt.swing.event.PopupMenuListener);<\/p>\n<p>public void setBorderPainted(boolean);<\/p>\n<p>public void setLabel(java.lang.String);<\/p>\n<p>public void setLightWeightPopupEnabled(boolean);<\/p>\n<p>public void setLocation(int, int);<\/p>\n<p>public void setPopupSize(int, int);<\/p>\n<p>public void setPopupSize(java.awt.Dimension);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>En forskel er det dog, at JPopupMenu har et marginbegreb. I denne forbindelse er en margin det antal pixels, der er mellem popup-menuens ramme og dens indhold. Margin manipuleres af metoderne setMargin og getMargin. Derudover skal man naturligvis l\u00e6gge m\u00e6rke til, at event-h\u00e5ndtering i en popup-menu sker ved hj\u00e6lp af en PopupMenuListener, mens det i en almindelig menu sker ved hj\u00e6lp af en MenuListener.<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r alle menuerne er oprettet, kan de samles p\u00e5 en menulinie. Menulinier repr\u00e6senteres i Swing af klassen JMenuBar.<\/p>\n<p>&nbsp;<\/p>\n<p>public class JMenuBar extends java.awt.swing.JComponent implements java.awt.accessibility.Accessible, java.awt.swing.MenuElement<\/p>\n<p>{<\/p>\n<p>public java.awt.swing.JMenuBar();<\/p>\n<p>&nbsp;<\/p>\n<p>public java.awt.swing.JMenu add(java.awt.swing.JMenu);<\/p>\n<p>public java.awt.Component getComponent();<\/p>\n<p>public java.awt.Component getComponentAtIndex(int);<\/p>\n<p>public int getComponentIndex(java.awt.Component);<\/p>\n<p>public java.awt.swing.JMenu getHelpMenu();<\/p>\n<p>public java.awt.Insets getMargin();<\/p>\n<p>public java.awt.swing.JMenu getMenu(int);<\/p>\n<p>public int getMenuCount();<\/p>\n<p>public void menuSelectionChanged(boolean);<\/p>\n<p>public void processKeyEvent(java.awt.event.KeyEvent, java.awt.swing.MenuElement[], java.awt.swing.MenuSelectionManager);<\/p>\n<p>public void processMouseEvent(java.awt.event.MouseEvent, java.awt.swing.MenuElement[], java.awt.swing.MenuSelectionManager);<\/p>\n<p>public void remove(int);<\/p>\n<p>public void setHelpMenu(java.awt.swing.JMenu);<\/p>\n<p>&nbsp;<\/p>\n<p>public void setMargin(java.awt.Insets);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Det essentielle i klassen er naturligvis add-metoden, der tilf\u00f8jer en menu til menulinien. Derudover skal man l\u00e6gge m\u00e6rke til metoderne setHelpMenu og getHelpMenu, der kan bruges til at h\u00e5ndtere den specielle hj\u00e6lpemenu.<\/p>\n<p>&nbsp;<\/p>\n<p>Nedenst\u00e5ende eksempel viser, hvordan man kan oprette et vindue med en menulinie og en r\u00e6kke forskellige menuer.<\/p>\n<p>&nbsp;<\/p>\n<p>import java.awt.swing.*;<\/p>\n<p>import java.awt.event.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class SimpleMenu extends JFrame<\/p>\n<p>{<\/p>\n<p>private JMenuBar menulinie;<\/p>\n<p>private JCheckBoxMenuItem check1, check2, check3;<\/p>\n<p>private JRadioButtonMenuItem radio1, radio2, radio3;<\/p>\n<p>private ButtonGroup radioGruppe;<\/p>\n<p>private JMenuItem item1, item2, item3;<\/p>\n<p>private JMenu checkMenu, radioMenu, itemMenu;<\/p>\n<p>private JTextArea tekst;<\/p>\n<p>private ActionListener listener;<\/p>\n<p>&nbsp;<\/p>\n<p>public SimpleMenu()<\/p>\n<p>{<\/p>\n<p>super (&#8220;Simpel menu&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j et tekstfelt<\/p>\n<p>&nbsp;<\/p>\n<p>tekst = new JTextArea();<\/p>\n<p>getContentPane().add(tekst);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret action listener<\/p>\n<p>&nbsp;<\/p>\n<p>listener = new Listener (tekst);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret menulinien<\/p>\n<p>&nbsp;<\/p>\n<p>menulinie = new JMenuBar();<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret check-menuen<\/p>\n<p>&nbsp;<\/p>\n<p>check1 = new JCheckBoxMenuItem (&#8220;Mulighed 1&#8221;, true);<\/p>\n<p>check2 = new JCheckBoxMenuItem (&#8220;Mulighed 2&#8221;);<\/p>\n<p>check3 = new JCheckBoxMenuItem (&#8220;Mulighed 3&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>check1.setActionCommand (&#8220;Checkbox nr. 1 \u00e6ndret&#8221;);<\/p>\n<p>check2.setActionCommand (&#8220;Checkbox nr. 2 \u00e6ndret&#8221;);<\/p>\n<p>check3.setActionCommand (&#8220;Checkbox nr. 3 \u00e6ndret&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>check1.addActionListener (listener);<\/p>\n<p>check2.addActionListener (listener);<\/p>\n<p>check3.addActionListener (listener);<\/p>\n<p>&nbsp;<\/p>\n<p>checkMenu = new JMenu(&#8220;Checkbox-menu&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>checkMenu.add(check1);<\/p>\n<p>checkMenu.add(check2);<\/p>\n<p>checkMenu.add(check3);<\/p>\n<p>&nbsp;<\/p>\n<p>menulinie.add (checkMenu);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret radio-menuen<\/p>\n<p>&nbsp;<\/p>\n<p>radio1 = new JRadioButtonMenuItem (&#8220;Mulighed 1&#8221;);<\/p>\n<p>radio2 = new JRadioButtonMenuItem (&#8220;Mulighed 2&#8221;);<\/p>\n<p>radio3 = new JRadioButtonMenuItem (&#8220;Mulighed 3&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>radio1.setActionCommand (&#8220;Alternativknap nr. 1 valgt&#8221;);<\/p>\n<p>radio2.setActionCommand (&#8220;Alternativknap nr. 2 valgt&#8221;);<\/p>\n<p>radio3.setActionCommand (&#8220;Alternativknap nr. 3 valgt&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>radio1.addActionListener (listener);<\/p>\n<p>radio2.addActionListener (listener);<\/p>\n<p>radio3.addActionListener (listener);<\/p>\n<p>&nbsp;<\/p>\n<p>radioGruppe = new ButtonGroup();<\/p>\n<p>radioGruppe.add(radio1);<\/p>\n<p>radioGruppe.add(radio2);<\/p>\n<p>radioGruppe.add(radio3);<\/p>\n<p>&nbsp;<\/p>\n<p>radioMenu = new JMenu(&#8220;Alternativknap-menu&#8221;);<\/p>\n<p>radioMenu.add(radio1);<\/p>\n<p>radioMenu.add(radio2);<\/p>\n<p>radioMenu.add(radio3);<\/p>\n<p>&nbsp;<\/p>\n<p>menulinie.add(radioMenu);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret en almindelig menu<\/p>\n<p>&nbsp;<\/p>\n<p>item1 = new JMenuItem (&#8220;Mulighed 1&#8221;);<\/p>\n<p>item2 = new JMenuItem (&#8220;Mulighed 2&#8221;);<\/p>\n<p>item3 = new JMenuItem (&#8220;Mulighed 3&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>item1.setActionCommand (&#8220;Mulighed 1 valgt&#8221;);<\/p>\n<p>item2.setActionCommand (&#8220;Mulighed 2 valgt&#8221;);<\/p>\n<p>item3.setActionCommand (&#8220;Mulighed 3 valgt&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>item1.addActionListener (listener);<\/p>\n<p>item2.addActionListener (listener);<\/p>\n<p>item3.addActionListener (listener);<\/p>\n<p>&nbsp;<\/p>\n<p>itemMenu = new JMenu(&#8220;Almindelig menu&#8221;);<\/p>\n<p>itemMenu.add (item1);<\/p>\n<p>itemMenu.add (item2);<\/p>\n<p>itemMenu.add (item3);<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>menulinie.add(itemMenu);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j menuen til vinduet<\/p>\n<p>&nbsp;<\/p>\n<p>setJMenuBar(menulinie);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilpas st\u00f8rrelsen<\/p>\n<p>&nbsp;<\/p>\n<p>setSize (400,200);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>SimpleMenu me = new SimpleMenu();<\/p>\n<p>me.show();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Programmet opretter en r\u00e6kke menupunkter. Til hvert af disse tilknyttes en <em>action command<\/em>, som er en tekst, der bliver vist, n\u00e5r brugeren klikker p\u00e5 det p\u00e5g\u00e6ldende menupunkt. Til at h\u00e5ndtere events bruges en instans af klassen Listener:<\/p>\n<p>&nbsp;<\/p>\n<p>class Listener implements ActionListener<\/p>\n<p>{<\/p>\n<p>private JTextArea status;<\/p>\n<p>&nbsp;<\/p>\n<p>public Listener (JTextArea status)<\/p>\n<p>{<\/p>\n<p>this.status = status;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void actionPerformed (ActionEvent e)<\/p>\n<p>{<\/p>\n<p>status.append (e.getActionCommand() + &#8220;\\n&#8221;);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>En k\u00f8rsel af programmet kan give et billede som det i figur 2.1.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0201.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-361\" title=\"0201\" src=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0201.png\" alt=\"\" width=\"400\" height=\"200\" srcset=\"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0201.png 400w, https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0201-300x150.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Fig. 2-1 \u2013 Et vindue med en menulinie.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p><strong><em><br \/>\n<\/em><\/strong><\/p>\n<h2>Genvejstaster<\/h2>\n<p>&nbsp;<\/p>\n<p>Som n\u00e6vnt tidligere kan man bruge genvejstaster i menuer i Swing. Dette kan g\u00f8res p\u00e5 to m\u00e5der. Man kan bruge de almindelige genvejstaster, der ogs\u00e5 kan bruges p\u00e5 knapper. Det vil sige, at n\u00e5r f\u00f8rst menuen er \u00e5ben, beh\u00f8ver man kun at trykke p\u00e5 en tast for at aktivere et menupunkt. Denne form for genvejstaster s\u00e6ttes ved hj\u00e6lp setMnemonic-metoden, og nedenst\u00e5ende kodestump viser, hvordan de kan tilf\u00f8jes det foreg\u00e5ende eksempel.<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j mnemonics<\/p>\n<p>&nbsp;<\/p>\n<p>check1.setMnemonic (&#8216;1&#8217;);<\/p>\n<p>check2.setMnemonic (&#8216;2&#8217;);<\/p>\n<p>check3.setMnemonic (&#8216;3&#8217;);<\/p>\n<p>checkMenu.setMnemonic (&#8216;C&#8217;);<\/p>\n<p>&nbsp;<\/p>\n<p>radio1.setMnemonic (&#8216;1&#8217;);<\/p>\n<p>radio2.setMnemonic (&#8216;2&#8217;);<\/p>\n<p>radio3.setMnemonic (&#8216;3&#8217;);<\/p>\n<p>radioMenu.setMnemonic (&#8216;R&#8217;);<\/p>\n<p>&nbsp;<\/p>\n<p>item1.setMnemonic (&#8216;1&#8217;);<\/p>\n<p>item2.setMnemonic (&#8216;2&#8217;);<\/p>\n<p>item3.setMnemonic (&#8216;3&#8217;);<\/p>\n<p>itemMenu.setMnemonic (&#8216;A&#8217;);<\/p>\n<p>&nbsp;<\/p>\n<p>S\u00e5 f\u00e5r man en menu, der ligner den i figur 2.2.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0202.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-362\" title=\"0202\" src=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0202.png\" alt=\"\" width=\"400\" height=\"200\" srcset=\"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0202.png 400w, https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0202-300x150.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Fig. 2.2 \u2013 En menu med mnemonics.<\/p>\n<p>&nbsp;<\/p>\n<p>Der findes imidlertid ogs\u00e5 en anden form for genvejstaster. Det er en tastekombination, man kan udf\u00f8re n\u00e5r som helst for at f\u00e5 en given funktion udf\u00f8rt. For eksempel vil tastekombinationen alt+F4 i Windows lukke det aktuelle program og alle \u00e5bne vinduer i programmet.<\/p>\n<p>&nbsp;<\/p>\n<p>Disse tastekombinationer repr\u00e6senteres i Java af klassen KeyStroke.<\/p>\n<p>&nbsp;<\/p>\n<p>public class KeyStroke extends java.lang.Object implements java.io.Serializable<\/p>\n<p>{<\/p>\n<p>public static java.awt.swing.KeyStroke getKeyStroke(char);<\/p>\n<p>public static java.awt.swing.KeyStroke getKeyStroke(char, boolean);<\/p>\n<p>&nbsp;<\/p>\n<p>public static java.awt.swing.KeyStroke getKeyStroke(int, int);<\/p>\n<p>public static java.awt.swing.KeyStroke getKeyStroke(int, int, boolean);<\/p>\n<p>public static java.awt.swing.KeyStroke getKeyStroke(java.lang.String);<\/p>\n<p>public static java.awt.swing.KeyStroke getKeyStrokeForEvent(java.awt.event.KeyEvent);<\/p>\n<p>public int getModifiers();<\/p>\n<p>public boolean isOnKeyRelease();<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Man opretter et KeyStroke-objekt ved at benytte en af de statiske getKeyStroke-funktioner. Disse findes i fem forskellige udgaver. Den f\u00f8rste tager en char som parameter \u2013 denne char angiver den ene tast, tastaturkombinationen best\u00e5r af. Den n\u00e6ste udgave af getKeyStroke tager en char og en boolean som parametre. char-v\u00e6rdien angiver igen den tast, tastekombinationen best\u00e5r af, mens boolean-v\u00e6rdien angiver, om tastaturkombinationen er fuldf\u00f8rt, n\u00e5r tasten slippes eller n\u00e5r den trykkes ned. Hvis true er kombinationen f\u00f8rst aktiv, n\u00e5r tasten slippes.<\/p>\n<p>&nbsp;<\/p>\n<p>Den n\u00e6ste getKeyStroke-funktion har to int-v\u00e6rdier som parametre. Den f\u00f8rste v\u00e6rdi angiver koden for den tast, der skal trykkes ned, mens den anden angiver <em>modifiers<\/em>. Modifiers er de taster, der skal holdes nede samtidigt med tasten. Der findes f\u00f8lgende modifiers:<\/p>\n<p>&nbsp;<\/p>\n<ul>\n<li>java.awt.Event.SHIFT_MASK (1)<\/li>\n<li>java.awt.Event.CTRL_MASK (2)<\/li>\n<li>java.awt.Event.META_MASK (4)<\/li>\n<li>java.awt.Event.ALT_MASK (8)<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Flere modifiers kan kombineres ved at l\u00e6gge dem sammen eller ved at foretage en logisk eller-operation.<\/p>\n<p>&nbsp;<\/p>\n<p>Der findes ogs\u00e5 en getKeyStroke-funktion, der tager to int-v\u00e6rdier og en boolean-v\u00e6rdi. Her angiver boolean-v\u00e6rdien igen, hvorn\u00e5r tastekombinationen skal aktivere.<\/p>\n<p>&nbsp;<\/p>\n<p>Til at angive tasten kan man bruge de konstanter, der findes i klassen java.awt.event.KeyEvent. Denne klasse indeholder konstanter for samtlige taster.<\/p>\n<p>&nbsp;<\/p>\n<p>I selve programmet tilf\u00f8jer man genvejstaster ved at benytte setAccelerator-metoden p\u00e5 JMenuItem-objekterne. Nedenst\u00e5ende programstump viser, hvordan man kan tilf\u00f8je tastekombinationer til menu-eksemplet.<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j tastekombinationer<\/p>\n<p>&nbsp;<\/p>\n<p>check1.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_1, Event.ALT_MASK));<\/p>\n<p>check2.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_2, Event.ALT_MASK));<\/p>\n<p>check3.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_3, Event.ALT_MASK));<\/p>\n<p>&nbsp;<\/p>\n<p>radio1.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_1, Event.ALT_MASK | Event.CTRL_MASK));<\/p>\n<p>&nbsp;<\/p>\n<p>radio2.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_2, Event.ALT_MASK | Event.CTRL_MASK));<\/p>\n<p>radio3.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_3, Event.ALT_MASK | Event.CTRL_MASK));<\/p>\n<p>&nbsp;<\/p>\n<p>item1.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_1, Event.CTRL_MASK));<\/p>\n<p>item2.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_2, Event.CTRL_MASK));<\/p>\n<p>item3.setAccelerator (KeyStroke.getKeyStroke(KeyEvent.VK_3, Event.CTRL_MASK));<\/p>\n<p>&nbsp;<\/p>\n<p>Genvejstasterne bliver automatisk vist p\u00e5 menuerne. Det betyder, at man nu har menuer, der ligner dem i figur 2.3.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0203.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-363\" title=\"0203\" src=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0203.png\" alt=\"\" width=\"470\" height=\"200\" srcset=\"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0203.png 470w, https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0203-300x127.png 300w\" sizes=\"auto, (max-width: 470px) 100vw, 470px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Fig. 2.3 \u2013 Menuer med genvejstaster.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2>Action<\/h2>\n<p>&nbsp;<\/p>\n<p>Det sker ofte, at den samme funktion er repr\u00e6senteret flere gange i brugergr\u00e6nsefladen. Det kan for eksempel v\u00e6re i en menu, p\u00e5 en v\u00e6rkt\u00f8jslinie og i en lokalmenu. Hvis der sker en \u00e6ndring i funktionens tilstand \u2013 for eksempel at den ikke l\u00e6ngere kan aktiveres \u2013 skal man \u00e6ndre dette alle steder, funktionen optr\u00e6der i brugergr\u00e6nsefladen. Dette er uhensigtsm\u00e6ssigt, og derfor findes i Swing interfacet Action. Dette interface repr\u00e6senterer en funktionalitet. Det vil sige, at interfacet indeholder informationer om funktionen og ogs\u00e5 den kode, der skal udf\u00f8res, n\u00e5r funktionen bliver valgt.<\/p>\n<p>&nbsp;<\/p>\n<p>Action-objekter kan tilf\u00f8jes direkte til de fleste elementer i Swing, og elementerne vil selv finde den rigtige repr\u00e6sentation. For eksempel vil en menu repr\u00e6sentere et Action\u00ad-objekt ved hj\u00e6lp af en tekststreng, mens en v\u00e6rkt\u00f8jslinie vil v\u00e6lge at vise funktionen ved dens ikon.<\/p>\n<p>&nbsp;<\/p>\n<p>Action-interfacet har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>public abstract interface Action extends java.lang.Object implements java.awt.event.ActionListener<\/p>\n<p>{<\/p>\n<p>&nbsp;<\/p>\n<p>public abstract java.lang.Object getValue(java.lang.String);<\/p>\n<p>public abstract boolean isEnabled();<\/p>\n<p>public abstract void putValue(java.lang.String, java.lang.Object);<\/p>\n<p>public abstract void setEnabled(boolean);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Interfacet er abstrakt, fordi det ikke implementerer actionPerformed fra ActionListener.<\/p>\n<p>&nbsp;<\/p>\n<p>Action-interfacet kan ikke kun bruges i brugergr\u00e6nsefladen. Det kan bruges mange af de steder, hvor man har brug for at repr\u00e6sentere en funktionalitet abstrakt.<\/p>\n<p>&nbsp;<\/p>\n<p>Til at lette arbejdet med actions findes klassen AbstractAction. Denne klasse implementerer de dele af Action-interfacet, der fremg\u00e5r af ovenst\u00e5ende signatur. Det vil sige, at klassen indeholder metoder til at \u00e6ndre og l\u00e6se et Action-objekts v\u00e6rdier.<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis man vil bruge et Action-objekt, beh\u00f8ver man blot at nedarve fra klassen AbstractAction og implementere metoden actionPerfomed. Nedenst\u00e5ende eksempel viser, hvordan man kan tilf\u00f8je et Action-objekt til en menu.<\/p>\n<p>&nbsp;<\/p>\n<p>import com.sun.java.swing.*;<\/p>\n<p>import java.awt.event.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class ActionMenu extends JFrame<\/p>\n<p>{<\/p>\n<p>public ActionMenu()<\/p>\n<p>{<\/p>\n<p>super (&#8220;Actionmenu&#8221;);<\/p>\n<p>JMenuBar menulinie = new JMenuBar();<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret action-objektet i to menuer<\/p>\n<p>&nbsp;<\/p>\n<p>JMenu menu1 = new JMenu(&#8220;Menu1&#8221;);<\/p>\n<p>JMenu menu2 = new JMenu(&#8220;Menu2&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>Action a = new TestAction(&#8220;Action!&#8221;);<\/p>\n<p>menu1.add (a);<\/p>\n<p>menu2.add (a);<\/p>\n<p>&nbsp;<\/p>\n<p>menulinie.add (menu1);<\/p>\n<p>menulinie.add (menu2);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret en menu med indstillinger<\/p>\n<p>&nbsp;<\/p>\n<p>JMenu indstillinger = new JMenu (&#8220;Indstillinger&#8221;);<\/p>\n<p>JMenuItem aktiver = new JMenuItem (&#8220;Aktiver Action-objekt&#8221;);<\/p>\n<p>JMenuItem deaktiver = new JMenuItem (&#8220;Deaktiver Action-objekt&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>aktiver.setActionCommand (&#8220;ak&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>deaktiver.setActionCommand (&#8220;deak&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>MenuActionListener listener = new MenuActionListener(a);<\/p>\n<p>aktiver.addActionListener (listener);<\/p>\n<p>deaktiver.addActionListener (listener);<\/p>\n<p>&nbsp;<\/p>\n<p>indstillinger.add (aktiver);<\/p>\n<p>indstillinger.add (deaktiver);<\/p>\n<p>&nbsp;<\/p>\n<p>menulinie.add (indstillinger);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j menulinie og tilpas vindue<\/p>\n<p>&nbsp;<\/p>\n<p>setJMenuBar (menulinie);<\/p>\n<p>setSize (400,100);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>ActionMenu me = new ActionMenu();<\/p>\n<p>me.show();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Eksemplet opretter to menuer, der hver indeholder det samme Action-objekt. Programmet opretter ogs\u00e5 en menu, hvorfra man kan aktivere eller deaktivere Action-objektet.\u00a0 Selve Action-objektet er en instans af klassen TestAction. Den er implementeret s\u00e5ledes:<\/p>\n<p>&nbsp;<\/p>\n<p>class TestAction extends AbstractAction<\/p>\n<p>{<\/p>\n<p>public TestAction (String text)<\/p>\n<p>{<\/p>\n<p>super (text);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void actionPerformed(ActionEvent e)<\/p>\n<p>{<\/p>\n<p>JOptionPane.showMessageDialog(null, &#8220;Funktionen er udf\u00f8rt&#8221;, &#8220;Action&#8221;, JOptionPane.PLAIN_MESSAGE);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>I dette eksempel bruges Action-objektet kun p\u00e5 en menu. Derfor er det kun n\u00f8dvendigt at overloade den constructor, der tager en tekst som parameter. Klassen AbstractAction indeholder ogs\u00e5 constructors, der accepterer ikoner som parametre \u2013 disse bruges, hvis man tilf\u00f8jer objektet til et knappanel.<\/p>\n<p>&nbsp;<\/p>\n<p>Programmet g\u00f8r ogs\u00e5 brug af eventh\u00e5ndtering til at bestemme, om action-menupunktet skal v\u00e6re aktivt eller ej. Dette foreg\u00e5r i klassen MenuActionListener:<\/p>\n<p>&nbsp;<\/p>\n<p>class MenuActionListener implements ActionListener<\/p>\n<p>&nbsp;<\/p>\n<p>{<\/p>\n<p>private Action a;<\/p>\n<p>&nbsp;<\/p>\n<p>public MenuActionListener (Action a)<\/p>\n<p>{<\/p>\n<p>this.a = a;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void actionPerformed (ActionEvent e)<\/p>\n<p>{<\/p>\n<p>if (e.getActionCommand().equals (&#8220;ak&#8221;))<\/p>\n<p>{<\/p>\n<p>a.setEnabled(true);<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>if (e.getActionCommand().equals (&#8220;deak&#8221;))<\/p>\n<p>{<\/p>\n<p>a.setEnabled(false);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Som det ses, er det ikke Action-punktet i begge menuer, der bliver aktiveret eller deaktiveret. Man beh\u00f8ver blot at aktivere eller deaktivere Action-objektet\u00a0 \u2013 s\u00e5 bliver \u00e6ndringen automatisk afspejlet overalt i brugergr\u00e6nsefladen.<\/p>\n<p>&nbsp;<\/p>\n<h1>Indikatorer<\/h1>\n<p>&nbsp;<\/p>\n<p>Hvis man skriver et program, der skal arbejde i lang tid uden input fra brugeren, er det godt at bruge en indikator til at vise, hvor langt programmet er kommet med opgaven. Indikatorer har to form\u00e5l: dels viser de, at programmet ikke er g\u00e5et ned, men bare udf\u00f8rer en eller anden kompliceret opgave, og dels fort\u00e6ller de brugeren, om der er tid til at hente en kop kaffe, inden programmet er f\u00e6rdigt.<\/p>\n<p>&nbsp;<\/p>\n<p>I Swing bruger man klassen com.sun.java.swing.JProgressBar til at repr\u00e6sentere en indikator. Denne klase har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>public class JProgressBar extends com.sun.java.swing.JComponent implements com.sun.java.swing.SwingConstants, com.sun.java.accessibility.Accessible<\/p>\n<p>{<\/p>\n<p>public com.sun.java.swing.JProgressBar();<\/p>\n<p>public com.sun.java.swing.JProgressBar(int);<\/p>\n<p>public com.sun.java.swing.JProgressBar(int,int);<\/p>\n<p>public com.sun.java.swing.JProgressBar(int,int,int);<\/p>\n<p>&nbsp;<\/p>\n<p>public int getMaximum();<\/p>\n<p>public int getMinimum();<\/p>\n<p>public int getOrientation();<\/p>\n<p>public double getPercentComplete();<\/p>\n<p>&nbsp;<\/p>\n<p>public java.lang.String getString();<\/p>\n<p>public com.sun.java.swing.plaf.ProgressBarUI getUI();<\/p>\n<p>public java.lang.String getUIClassID();<\/p>\n<p>public int getValue();<\/p>\n<p>public void setMaximum(int);<\/p>\n<p>public void setMinimum(int);<\/p>\n<p>public void setOrientation(int);<\/p>\n<p>public void setString(java.lang.String);<\/p>\n<p>public void setValue(int);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen har tre constructors, der tager et variabelt antal integers som parametre. Angiver man kun en parameter, bestemmer denne, om indikatoren skal v\u00e6re lodret eller vandret. Det g\u00f8res ved hj\u00e6lp af konstanterne JProgressBar.VERTICAL og JProgressBar.HORIZONTAL. Angiver man to parametre, er den f\u00f8rste retningen, mens den anden angiver den maksimale v\u00e6rdi. Angiver man tre parametre, er den f\u00f8rste retningen, den anden minimumsv\u00e6rdien og den tredje maksimumv\u00e6rdien.<\/p>\n<p>&nbsp;<\/p>\n<p>Form\u00e5let med at angive minimums- og maksimumv\u00e6rdier er, at man kan overlade det til JProgressBar-klassen at beregne hvor stor en del af opgaven, der er fuldf\u00f8rt. Man kan endda \u2013 ved hj\u00e6lp af funktionen getPercentComplete \u2013 f\u00e5 at vide, hvor stor en procentdel af opgaven, der er udf\u00f8rt. Der findes to m\u00e5der, at opdatere en indikator p\u00e5. Enten kan den opgave, indikatoren afspejler, opdatere indikatoren hver gang, der sker fremskridt, eller ogs\u00e5 kan man tilf\u00f8je en <em>timer<\/em>, der med et givent interval sp\u00f8rger opgaven, hvor langt den er kommet, og herefter opdaterer indikatoren.<\/p>\n<p>&nbsp;<\/p>\n<p>Nedenst\u00e5ende eksempel viser, hvordan man kan lave en indikator, hvor opgaven automatisk opdaterer den. Opgaven er den simple at t\u00e6lle fra nul til en million.<\/p>\n<p>&nbsp;<\/p>\n<p>import com.sun.java.swing.*;<\/p>\n<p>import java.awt.event.*;<\/p>\n<p>import java.awt.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class MillionIndicator extends JFrame<\/p>\n<p>{<\/p>\n<p>public MillionIndicator()<\/p>\n<p>{<\/p>\n<p>\/\/ Tilpas vinduet<\/p>\n<p>&nbsp;<\/p>\n<p>super (&#8220;Hvem kan hurtigst t\u00e6lle til en million?&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>getContentPane().setLayout(new GridLayout(4,1));<\/p>\n<p>setSize (300,400);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret variantknapper til at v\u00e6lge lodret\/vandret<\/p>\n<p>&nbsp;<\/p>\n<p>JRadioButton vandret = new JRadioButton (&#8220;Vandret&#8221;, true);<\/p>\n<p>JRadioButton lodret = new JRadioButton (&#8220;Lodret&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>ButtonGroup varianter = new ButtonGroup();<\/p>\n<p>varianter.add (vandret);<\/p>\n<p>varianter.add (lodret);<\/p>\n<p>&nbsp;<\/p>\n<p>getContentPane().add(vandret);<\/p>\n<p>getContentPane().add(lodret);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret indikatoren<\/p>\n<p>&nbsp;<\/p>\n<p>JProgressBar indikator = new JProgressBar (JProgressBar.HORIZONTAL, 0, 1000000);<\/p>\n<p>&nbsp;<\/p>\n<p>getContentPane().add(indikator);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret startknap<\/p>\n<p>&nbsp;<\/p>\n<p>JButton startknap = new JButton (&#8220;Start&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>getContentPane().add(startknap);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j eventh\u00e5ndtering<\/p>\n<p>&nbsp;<\/p>\n<p>ActionListener listener = new EventHandler (indikator);<\/p>\n<p>&nbsp;<\/p>\n<p>vandret.setActionCommand (&#8220;vandret&#8221;);<\/p>\n<p>lodret.setActionCommand (&#8220;lodret&#8221;);<\/p>\n<p>startknap.setActionCommand (&#8220;start&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>vandret.addActionListener (listener);<\/p>\n<p>lodret.addActionListener (listener);<\/p>\n<p>startknap.addActionListener (listener);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>MillionIndicator me = new MillionIndicator();<\/p>\n<p>me.show();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Hovedparten af ovenst\u00e5ende klasse opretter den grafiske brugergr\u00e6nseflade. Der bliver oprettet to variantknapper, som kan bruges til at v\u00e6lge, om indikatoren skal fyldes vandret eller lodret. Derudover blive selve indikatoren oprettet. Til at starte med, er den indstillet til at blive fyldt vandret. Indikatorens interval s\u00e6ttes til fra 0 til 1.000.000. Endelig tilf\u00f8jes en startknap, der s\u00f8rger for, at opt\u00e6llingen begynder.<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r brugeren v\u00e6lger en af de to variantknapper eller startknappen, overg\u00e5r kontrollen til action listener\u2019en. Denne er i dette eksempel en instans af klassen EventHandler, der er defineret s\u00e5ledes:<\/p>\n<p>&nbsp;<\/p>\n<p>class EventHandler implements ActionListener<\/p>\n<p>{<\/p>\n<p>private JProgressBar indikator;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>public EventHandler (JProgressBar indikator)<\/p>\n<p>{<\/p>\n<p>this.indikator = indikator;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void actionPerformed (ActionEvent e)<\/p>\n<p>{<\/p>\n<p>String command = e.getActionCommand();<\/p>\n<p>&nbsp;<\/p>\n<p>if (command.equals(&#8220;vandret&#8221;))<\/p>\n<p>{<\/p>\n<p>indikator.setOrientation (JProgressBar.HORIZONTAL);<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>if (command.equals(&#8220;lodret&#8221;))<\/p>\n<p>{<\/p>\n<p>indikator.setOrientation (JProgressBar.VERTICAL);<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>if (command.equals(&#8220;start&#8221;))<\/p>\n<p>{<\/p>\n<p>MillionCounter counter = new MillionCounter (indikator);<\/p>\n<p>counter.start();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r brugeren v\u00e6lger vandret eller lodret, kaldes metoden setOrientation p\u00e5 indikatoren, og retningen tilpasses brugerens valg.<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis brugeren klikker p\u00e5 startknappen, bliver der oprettet en instans af klassen MillionCounter, der udf\u00f8rer selve opgaven og h\u00e5ndterer opdateringen af indikatoren. MillionCounter-klassen er implementeret s\u00e5ledes:<\/p>\n<p>class MillionCounter extends Thread<\/p>\n<p>{<\/p>\n<p>JProgressBar indikator;<\/p>\n<p>&nbsp;<\/p>\n<p>public MillionCounter (JProgressBar indikator)<\/p>\n<p>{<\/p>\n<p>this.indikator = indikator;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void run()<\/p>\n<p>{<\/p>\n<p>for (int i = 0; i &lt; 1000000; i++)<\/p>\n<p>{<\/p>\n<p>indikator.setValue (i);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen nedarver fra Thread-klassen, hvilket g\u00f8r, at den kan afvikles asynkront. For hver gang klassen t\u00e6ller t\u00e6llervariablen 1 op, bliver indikatoren opdateret. Dette er godt nok i dette eksempel, men af hensyn til hastigheden vil man i en virkelig applikation nok v\u00e6lge kun at opdatere i givne intervaller \u2013 for eksempel hver gang t\u00e6llervariablen er talt et tusind op.<\/p>\n<p>&nbsp;<\/p>\n<p>Afviklingen af programmet giver et sk\u00e6rmbillede svarende til det i figur 2.4.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0204.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-364\" title=\"0204\" src=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0204.png\" alt=\"\" width=\"300\" height=\"400\" srcset=\"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0204.png 300w, https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0204-225x300.png 225w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Fig. 2.4 \u2013 en vandret indikator.<\/p>\n<p>&nbsp;<\/p>\n<p>Det er ikke de store \u00e6ndringer, der skal foretages for at \u00e6ndre programmet til at v\u00e6re timer-styret. Faktisk beh\u00f8ver selve brugergr\u00e6nsefladen slet ikke at blive \u00e6ndret. Kun den klasse, der h\u00e5ndterer events og den klasse, som udf\u00f8rer opgaven, skal opdateres.<\/p>\n<p>&nbsp;<\/p>\n<p>T\u00e6llerklassen skal ikke l\u00e6ngere selv h\u00e5ndtere opdateringen af brugergr\u00e6nsefladen, og derfor bliver den simplere. Til geng\u00e6ld skal den implementere en funktion, der angiver, hvor langt den er kommet med opgaven, og en funktion, der angiver, om opgaven er udf\u00f8rt.<\/p>\n<p>&nbsp;<\/p>\n<p>Det betyder, at t\u00e6llerklassen nu ser s\u00e5ledes ud:<\/p>\n<p>&nbsp;<\/p>\n<p>class TimerMillionCounter extends Thread<\/p>\n<p>{<\/p>\n<p>private int i;<\/p>\n<p>&nbsp;<\/p>\n<p>public void run()<\/p>\n<p>{<\/p>\n<p>for (i = 0; i &lt; 1000000; i++);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public int getStatus ()<\/p>\n<p>&nbsp;<\/p>\n<p>{<\/p>\n<p>return i;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public boolean isDone ()<\/p>\n<p>{<\/p>\n<p>return i == 1000000;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Det ses, at t\u00e6llervariablen \u2013 i \u2013 er blevet flyttet fra at v\u00e6re en lokal variabel til at v\u00e6re en privat instansvariabel. Det skyldes, at den nu ogs\u00e5 bruges i funktionerne getStatus og isDone.<\/p>\n<p>&nbsp;<\/p>\n<p>Prisen for den simplere t\u00e6llerklasse betales i eventh\u00e5ndteringen. Her er det ikke l\u00e6ngere nok bare at starte t\u00e6lleren. Nu skal der ogs\u00e5 startes en timer, der er tilknyttet t\u00e6lleren. Derudover er det nu eventh\u00e5ndteringen, der skal s\u00f8rge for opdateringen af brugergr\u00e6nsefladen.<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen, der h\u00e5ndterer events, kommer derfor til at se s\u00e5ledes ud ved den timerstyrede indikator:<\/p>\n<p>&nbsp;<\/p>\n<p>class TimerEventHandler implements ActionListener<\/p>\n<p>{<\/p>\n<p>private JProgressBar indikator;<\/p>\n<p>private Timer timer = null;<\/p>\n<p>private TimerMillionCounter counter = null;<\/p>\n<p>&nbsp;<\/p>\n<p>public TimerEventHandler (JProgressBar indikator)<\/p>\n<p>{<\/p>\n<p>this.indikator = indikator;<\/p>\n<p>counter = new TimerMillionCounter ();<\/p>\n<p>timer = new Timer (1, this);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void actionPerformed (ActionEvent e)<\/p>\n<p>{<\/p>\n<p>String command = e.getActionCommand();<\/p>\n<p>&nbsp;<\/p>\n<p>if (command != null)<\/p>\n<p>{<\/p>\n<p>if (command.equals(&#8220;vandret&#8221;))<\/p>\n<p>{<\/p>\n<p>indikator.setOrientation (JProgressBar.HORIZONTAL);<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>if (command.equals(&#8220;lodret&#8221;))<\/p>\n<p>{<\/p>\n<p>indikator.setOrientation (JProgressBar.VERTICAL);<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>if (command.equals(&#8220;start&#8221;))<\/p>\n<p>{<\/p>\n<p>&nbsp;<\/p>\n<p>counter.start();<\/p>\n<p>timer.start();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>{<\/p>\n<p>indikator.setValue (counter.getStatus());<\/p>\n<p>if (counter.isDone())<\/p>\n<p>{<\/p>\n<p>timer.stop();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Som det ses, bliver timeren repr\u00e6senteret af klassen Timer. B\u00e5de timeren og referencen til indikatoren er private variable, da de bruges flere forskellige steder i programmet. Timer-klassen har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>public class Timer extends java.lang.Object implements java.io.Serializable<\/p>\n<p>{<\/p>\n<p>public com.sun.java.swing.Timer(int,java.awt.event.ActionListener);<\/p>\n<p>public void addActionListener(java.awt.event.ActionListener);<\/p>\n<p>public int getDelay();<\/p>\n<p>public int getInitialDelay();<\/p>\n<p>public void removeActionListener(java.awt.event.ActionListener);<\/p>\n<p>public void restart();<\/p>\n<p>public void setDelay(int);<\/p>\n<p>public void setInitialDelay(int);<\/p>\n<p>public void start();<\/p>\n<p>public void stop();<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Hver gang et bestemt tidsrum er g\u00e5et, genererer Timer-klassen en ActionEvent. Derfor skal der tilknyttes en action-listener. B\u00e5de action-listener\u2019en og intervallet angivet i millisekunder skal angives i klassens constructor. I eksemplet er intervallet 1 millisekund og den samme action-listener bruges til timeren og til de \u00f8vrige events.<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis man bruger et langt interval, kan man med fordel bruge metoden setInitialDelay for at sikre, at event\u2019en opst\u00e5r hurtigere f\u00f8rste gang. Den parameter, der sendes med til setInitialDelay, angiver, hvor mange millisekunder, der skal g\u00e5, f\u00f8r den f\u00f8rste begivenhed indtr\u00e6ffer. Herefter opst\u00e5r events med det interval, som blev angivet i constructoren.<\/p>\n<p><script type=\"text\/javascript\">\nvar uri = 'http:\/\/impdk.tradedoubler.com\/imp?type(js)g(19172334)a(2029446)' + new String (Math.random()).substring (2, 11);\ndocument.write('<sc'+'ript type=\"text\/javascript\" src=\"'+uri+'\" charset=\"\"><\/sc'+'ript>');\n<\/script><\/p>\n<p>&nbsp;<\/p>\n<h1>Skydere<\/h1>\n<p>&nbsp;<\/p>\n<p>Skydere (sliders) bruges til at lade brugeren angive en talv\u00e6rdi i et afgr\u00e6nset omr\u00e5de. Typisk anvendes skydere n\u00e5r talv\u00e6rdien bruges til at regulere<\/p>\n<p>&nbsp;<\/p>\n<p>hastighed, lydstyrke eller lignende \u2013 svarende til de skydere, der blev brugt p\u00e5 gamle fjernsyn til at regulere lydstyrken.<\/p>\n<p>&nbsp;<\/p>\n<p>Fordelene ved at bruge en skyder fremfor et almindeligt indtastningsfelt er, at man undg\u00e5r indtastningsfejl, og at man sikrer, at v\u00e6rdien ligger i det tilladte interval.<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen JSlider bruges til at oprette en skyder. Denne klasse har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>public class JSlider extends com.sun.java.swing.JComponent implements com.sun.java.swing.SwingConstants, com.sun.java.accessibility.Accessible<\/p>\n<p>{<\/p>\n<p>public com.sun.java.swing.JSlider();<\/p>\n<p>public com.sun.java.swing.JSlider(int,int,int,int);<\/p>\n<p>&nbsp;<\/p>\n<p>public void addChangeListener(com.sun.java.swing.event.ChangeListener);<\/p>\n<p>public java.util.Hashtable createStandardLabels(int);<\/p>\n<p>public java.util.Hashtable createStandardLabels(int, int);<\/p>\n<p>public int getExtent();<\/p>\n<p>public boolean getInverted();<\/p>\n<p>public java.util.Dictionary getLabelTable();<\/p>\n<p>public int getMajorTickSpacing();<\/p>\n<p>public int getMaximum();<\/p>\n<p>public int getMinimum();<\/p>\n<p>public int getMinorTickSpacing();<\/p>\n<p>public int getOrientation();<\/p>\n<p>public boolean getPaintLabels();<\/p>\n<p>public boolean getPaintTicks();<\/p>\n<p>public boolean getPaintTrack();<\/p>\n<p>public boolean getSnapToTicks();<\/p>\n<p>public int getValue();<\/p>\n<p>public boolean getValueIsAdjusting();<\/p>\n<p>public void removeChangeListener(com.sun.java.swing.event.ChangeListener);<\/p>\n<p>public void setExtent(int);<\/p>\n<p>public void setInverted(boolean);<\/p>\n<p>public void setLabelTable(java.util.Dictionary);<\/p>\n<p>public void setMajorTickSpacing(int);<\/p>\n<p>public void setMaximum(int);<\/p>\n<p>public void setMinimum(int);<\/p>\n<p>public void setMinorTickSpacing(int);<\/p>\n<p>public void setOrientation(int);<\/p>\n<p>public void setPaintLabels(boolean);<\/p>\n<p>public void setPaintTicks(boolean);<\/p>\n<p>public void setPaintTrack(boolean);<\/p>\n<p>public void setSnapToTicks(boolean);<\/p>\n<p>public void setValue(int);<\/p>\n<p>public void setValueIsAdjusting(boolean);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis man bruger den constructor, der tager fire parametre, skal disse v\u00e6re retningen \u2013 JSlider.VERTICAL eller JSlider.HORIZONTAL \u2013 minimumsv\u00e6rdien<\/p>\n<p>&nbsp;<\/p>\n<p>, maksimumv\u00e6rdien og den v\u00e6rdi, skyderen skal v\u00e6re indstillet p\u00e5.<\/p>\n<p>&nbsp;<\/p>\n<p>En skyder er opdelt i sm\u00e5 og store intervaller. De store intervaller kan eksempelvis v\u00e6re 10 enheder, mens de sm\u00e5 er en enkelt enhed. Metoderne setMinorTickSpacing og setMajorTickSpacing bruges til at angive dette. Man kan v\u00e6lge, om brugeren m\u00e5 angive v\u00e6rdier, der ligger imellem intervallerne. Hvis man kalder setSnapToTicks(true), kan man kun v\u00e6lge v\u00e6rdier, der ligger p\u00e5 intervallerne.<\/p>\n<p>&nbsp;<\/p>\n<p>Man har mange muligheder for at angive, hvordan skyderen skal se ud. Med metoden setPaintLabels kan man v\u00e6lge, om der skal s\u00e6ttes labels p\u00e5 skyderen, mens setPaintTicks angiver, om der skal tegnes linier til at indikere intervallerne. Metoden setOrientation angiver, om skyderen skal v\u00e6re lodret eller vandret. Hvis man har brug for at vende skyderen om, s\u00e5 den st\u00f8rste v\u00e6rdi er til venstre og den mindste til h\u00f8jre, kan man g\u00f8re det ved hj\u00e6lp af setInverted.<\/p>\n<p>&nbsp;<\/p>\n<p>Alle de ovenst\u00e5ende metoder har naturligvis tilknyttede get-funktioner. Den vigtigste get-funktion er nok getValue, der returnerer den v\u00e6rdi, brugeren har valgt.<\/p>\n<p>&nbsp;<\/p>\n<p>Nedenst\u00e5ende eksempel viser, hvordan man kan lade brugeren v\u00e6lge udseendet af skyderen og bruge skyderen til at bestemme, hvor lang tid der skal g\u00e5 mellem udsendelse af bip.<\/p>\n<p>&nbsp;<\/p>\n<p>import com.sun.java.swing.*;<\/p>\n<p>import com.sun.java.swing.event.*;<\/p>\n<p>import java.awt.event.*;<\/p>\n<p>import java.awt.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class Beep extends JFrame<\/p>\n<p>{<\/p>\n<p>public Beep()<\/p>\n<p>{<\/p>\n<p>\/\/ Tilpas vinduet<\/p>\n<p>&nbsp;<\/p>\n<p>super (&#8220;Beep&#8221;);<\/p>\n<p>setSize (400,300);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret valgmuligheder for brugeren<\/p>\n<p>&nbsp;<\/p>\n<p>JRadioButton vandret = new JRadioButton (&#8220;Vandret&#8221;, true);<\/p>\n<p>JRadioButton lodret = new JRadioButton (&#8220;Lodret&#8221;);<\/p>\n<p>ButtonGroup gruppe = new ButtonGroup();<\/p>\n<p>gruppe.add (vandret);<\/p>\n<p>gruppe.add (lodret);<\/p>\n<p>&nbsp;<\/p>\n<p>JCheckBox labels = new JCheckBox (&#8220;Vis labels&#8221;);<\/p>\n<p>JCheckBox intervaller = new JCheckBox (&#8220;Vis intervaller&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>JPanel panel = new JPanel();<\/p>\n<p>panel.setLayout (new GridLayout(2,2));<\/p>\n<p>&nbsp;<\/p>\n<p>panel.add (vandret);<\/p>\n<p>&nbsp;<\/p>\n<p>panel.add (lodret);<\/p>\n<p>panel.add (labels);<\/p>\n<p>panel.add (intervaller);<\/p>\n<p>&nbsp;<\/p>\n<p>getContentPane().add(panel, BorderLayout.NORTH);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret skyder<\/p>\n<p>&nbsp;<\/p>\n<p>JSlider slider = new JSlider (JSlider.HORIZONTAL, 0, 30, 15);<\/p>\n<p>slider.setPaintTicks (false);<\/p>\n<p>slider.setPaintLabels (false);<\/p>\n<p>slider.setMajorTickSpacing (5);<\/p>\n<p>slider.setMinorTickSpacing (1);<\/p>\n<p>&nbsp;<\/p>\n<p>getContentPane().add (slider, BorderLayout.SOUTH);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret en timer, der bestemmer, hvorn\u00e5r der skal lyde et bip<\/p>\n<p>&nbsp;<\/p>\n<p>Timer timer = new Timer (slider.getValue()*1000, new TimerListener());<\/p>\n<p>timer.start();<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j eventh\u00e5ndtering<\/p>\n<p>&nbsp;<\/p>\n<p>ActionListener listener = new BeepListener(slider);<\/p>\n<p>&nbsp;<\/p>\n<p>vandret.setActionCommand (&#8220;vandret&#8221;);<\/p>\n<p>lodret.setActionCommand (&#8220;lodret&#8221;);<\/p>\n<p>labels.setActionCommand (&#8220;labels&#8221;);<\/p>\n<p>intervaller.setActionCommand (&#8220;intervaller&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>vandret.addActionListener (listener);<\/p>\n<p>lodret.addActionListener (listener);<\/p>\n<p>labels.addActionListener (listener);<\/p>\n<p>intervaller.addActionListener (listener);<\/p>\n<p>&nbsp;<\/p>\n<p>slider.addChangeListener (new SliderListener(timer));<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>Beep me = new Beep();<\/p>\n<p>me.show();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Den f\u00f8rste del af klassens constructor opretter den del af brugergr\u00e6nsefladen, der giver brugeren mulighed for at indstille udseendet af skyderen.<\/p>\n<p>&nbsp;<\/p>\n<p>Selve skyderen oprettes som en vandret skyder med v\u00e6rdier i intervallet 0 til 30 og med startv\u00e6rdien 15. Derefter kaldes setPaintTicks og setPaintLabels, der s\u00f8rger for at skyderens udseende initialt passer med<\/p>\n<p>&nbsp;<\/p>\n<p>indstillingerne i brugergr\u00e6nsefladen. Endeligt s\u00e6ttes intervallet til 5 sekunder. Hvert interval inddeles i underintervaller hvert p\u00e5 et enkelt sekund.<\/p>\n<p>&nbsp;<\/p>\n<p>Eventh\u00e5ndteringen i programmet er stort set som forventet. Blot skal man l\u00e6gge m\u00e6rke til, at JSlider-objekter bruger ChangeListener-objekter og ikke ActionListener-objekter.<\/p>\n<p>&nbsp;<\/p>\n<p>H\u00e5ndteringen af de events, der \u00e6ndrer udseendet af skyderen, varetages af klassen BeepListener. Denne klasse er implementeret s\u00e5ledes:<\/p>\n<p>&nbsp;<\/p>\n<p>class BeepListener implements ActionListener<\/p>\n<p>{<\/p>\n<p>private JSlider slider;<\/p>\n<p>&nbsp;<\/p>\n<p>public BeepListener (JSlider slider)<\/p>\n<p>{<\/p>\n<p>this.slider = slider;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void actionPerformed (ActionEvent e)<\/p>\n<p>{<\/p>\n<p>&nbsp;<\/p>\n<p>String command = e.getActionCommand();<\/p>\n<p>&nbsp;<\/p>\n<p>if (command.equals(&#8220;vandret&#8221;))<\/p>\n<p>{<\/p>\n<p>slider.setOrientation (JSlider.HORIZONTAL);<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>if (command.equals(&#8220;lodret&#8221;))<\/p>\n<p>{<\/p>\n<p>slider.setOrientation (JSlider.VERTICAL);<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>if (command.equals(&#8220;labels&#8221;))<\/p>\n<p>{<\/p>\n<p>slider.setPaintLabels(!slider.getPaintLabels());<\/p>\n<p>}<\/p>\n<p>else<\/p>\n<p>if (command.equals(&#8220;intervaller&#8221;))<\/p>\n<p>{<\/p>\n<p>slider.setPaintTicks(!slider.getPaintTicks());<\/p>\n<p>}<\/p>\n<p>slider.revalidate();<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>De kommandoer, der \u00e6ndrer skyderen fra vandret til lodret og omvendt, implementeres ved hj\u00e6lp af setOrientation-metoden. De to \u00f8vrige \u2013 visning af labels og intervaller \u2013 \u00e6ndres ved hj\u00e6lp af henholdsvis setPaintLabels og setPaintTicks. I stedet for at se om afkrydsningsfelterne (checkboksene) er markerede, benyttes JSlider-klassens get-funktioner til at finde ud af, om elementerne skal vises eller fjernes.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>H\u00e5ndtering af de events, der opst\u00e5r, n\u00e5r brugeren flytter p\u00e5 skyderen, h\u00e5ndteres af klassen SliderListener. Implementeringen af SliderListener er som f\u00f8lger:<\/p>\n<p>&nbsp;<\/p>\n<p>class SliderListener implements ChangeListener<\/p>\n<p>{<\/p>\n<p>private Timer timer;<\/p>\n<p>&nbsp;<\/p>\n<p>public SliderListener (Timer timer)<\/p>\n<p>{<\/p>\n<p>this.timer = timer;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void stateChanged (ChangeEvent e)<\/p>\n<p>{<\/p>\n<p>timer.setDelay (((JSlider) e.getSource()).getValue() * 1000);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Hver gang skyderens v\u00e6rdi \u00e6ndres, bliver stateChanged-metoden kaldt. Denne \u00e6ndrer intervallet p\u00e5 den timer, der kontrollerer den tid, som g\u00e5r mellem bippene. For at f\u00e5 en reference til JSlider-objektet kaldes metoden getSource p\u00e5 ChangeEvent-objektet. N\u00e5r dette er castet til et JSlider-objekt, kan metoden getValue kaldes. Denne returnerer skyderens v\u00e6rdi. Da Timer-objekter regner i millisekunder, og skyderen regner i sekunder, skal resultatet ganges med 1.000.<\/p>\n<p>&nbsp;<\/p>\n<p>Hver gang Timer-objektet aktiveres, opst\u00e5r som bekendt en ActionEvent. Denne h\u00e5ndteres af klassen TimerListener:<\/p>\n<p>&nbsp;<\/p>\n<p>class TimerListener implements ActionListener<\/p>\n<p>{<\/p>\n<p>public void actionPerformed (ActionEvent e)<\/p>\n<p>{<\/p>\n<p>Toolkit.getDefaultToolkit().beep();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Afvikling af programmet giver et sk\u00e6rmbillede svarende til det i figur 2.5.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0205.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-365\" title=\"0205\" src=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0205.png\" alt=\"\" width=\"400\" height=\"300\" srcset=\"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0205.png 400w, https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0205-300x225.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Fig. 2.5 \u2013 En vandret skyder med intervaller og labels.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>Faneblade<\/h1>\n<p>&nbsp;<\/p>\n<p>Ligesom faneblade i et ringbind bruges til at gruppere ting, der logisk h\u00f8rer sammen, og i \u00f8vrigt skabe overblik, bruger man faneblade i et program til at samle de komponenter p\u00e5 brugergr\u00e6nsefladen, der h\u00f8rer sammen.<\/p>\n<p>&nbsp;<\/p>\n<p>Alle komponenter \u2013 det vil sige alle klasser, der nedarver fra java.awt.Component \u2013 kan tilf\u00f8jes som faneblade. Oftest vil man dog tilf\u00f8je paneler, der indeholder flere komponenter som en fane.<\/p>\n<p>&nbsp;<\/p>\n<p>Fanebladene h\u00e5ndteres af klassen JTabbedPane. Denne har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>public class JTabbedPane extends com.sun.java.swing.JComponent implements java.io.Serializable, com.sun.java.accessibility.Accessible, com.sun.java.swing.SwingConstants<\/p>\n<p>{<\/p>\n<p>public com.sun.java.swing.JTabbedPane();<\/p>\n<p>public com.sun.java.swing.JTabbedPane(int);<\/p>\n<p>&nbsp;<\/p>\n<p>public java.awt.Component add(java.awt.Component);<\/p>\n<p>public java.awt.Component add(java.awt.Component, int);<\/p>\n<p>public void add(java.awt.Component, java.lang.Object);<\/p>\n<p>public void add(java.awt.Component, java.lang.Object, int);<\/p>\n<p>public java.awt.Component add(java.lang.String, java.awt.Component);<\/p>\n<p>public void addChangeListener(com.sun.java.swing.event.ChangeListener);<\/p>\n<p>public void addTab(java.lang.String, com.sun.java.swing.Icon, java.awt.Component);<\/p>\n<p>public void addTab(java.lang.String, com.sun.java.swing.Icon, java.awt.Component, java.lang.String);<\/p>\n<p>public void addTab(java.lang.String, java.awt.Component);<\/p>\n<p>public int getSelectedIndex();<\/p>\n<p>public int getTabCount();<\/p>\n<p>public void insertTab(java.lang.String, com.sun.java.swing.Icon, java.awt.Component, java.lang.String, int);<\/p>\n<p>public void remove(java.awt.Component);<\/p>\n<p>public void removeAll();<\/p>\n<p>public void removeChangeListener(com.sun.java.swing.event.ChangeListener);<\/p>\n<p>public void removeTabAt(int);<\/p>\n<p>&nbsp;<\/p>\n<p>public void setSelectedComponent(java.awt.Component);<\/p>\n<p>public void setSelectedIndex(int);<\/p>\n<p>public void setTabPlacement(int);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen indeholder en r\u00e6kke add-funktioner, der hver tilf\u00f8jer en fane til oversigten. Den tekst, der vises p\u00e5 en fane, kan enten v\u00e6re komponentens navn, eller man kan angive sin egen tekst. Hvis man benytter en af addTab-funktionerne i stedet, kan man tilf\u00f8je en komponent og give fanen en titel og eventuelt ogs\u00e5 et ikon. Ikoner bliver beskrevet yderligere i kapitel 3. B\u00e5de add- og addTab-metoderne tilf\u00f8jer fanen i slutningen af listen. Hvis man vil inds\u00e6tte en fane p\u00e5 et bestemt sted i listen, skal man bruge insertTab-metoden.<\/p>\n<p>&nbsp;<\/p>\n<p>JTabbedPane-klassen s\u00f8rger selv for at vise det rigtige faneblad, n\u00e5r brugeren klikker p\u00e5 en fane. Hvis man alligevel har brug for at vide, hvilket faneblad, der vises i \u00f8jeblikket, kan man kalde metoden getSelectedIndex, der returnerer positionen p\u00e5 den fane, som er markeret, eller getSelectedComponent, der returnerer det komponent, som er markeret.<\/p>\n<p>&nbsp;<\/p>\n<p>Nedenst\u00e5ende eksempel viser, hvordan man kan tilf\u00f8je forskellige komponenter til en liste af faneblade.<\/p>\n<p>&nbsp;<\/p>\n<p>import com.sun.java.swing.*;<\/p>\n<p>import java.awt.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class TabDemo extends JFrame<\/p>\n<p>{<\/p>\n<p>public TabDemo()<\/p>\n<p>{<\/p>\n<p>\/\/ Tilpas vinduet<\/p>\n<p>&nbsp;<\/p>\n<p>super (&#8220;Faneblade&#8221;);<\/p>\n<p>setSize (400,300);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret JTabbedPane<\/p>\n<p>&nbsp;<\/p>\n<p>JTabbedPane faneblade = new JTabbedPane();<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j nogle faner vha. forskellige metoder<\/p>\n<p>&nbsp;<\/p>\n<p>faneblade.add (&#8220;Tekstfelt&#8221;, new JTextField (&#8220;Tekstfelt&#8221;));<\/p>\n<p>faneblade.addTab (&#8220;Knap&#8221;, new JButton (&#8220;Klik her!&#8221;));<\/p>\n<p>faneblade.insertTab (&#8220;Label&#8221;, null, new JLabel (&#8220;Label&#8221;), null, 2);<\/p>\n<p>&nbsp;<\/p>\n<p>JPanel panel = new JPanel();<\/p>\n<p>panel.setLayout (new GridLayout(3,1));<\/p>\n<p>panel.add (new JTextField(&#8220;Et tekstfelt,&#8221;));<\/p>\n<p>panel.add (new JLabel(&#8220;lidt tekst&#8221;));<\/p>\n<p>panel.add (new JButton(&#8220;og en knap&#8221;));<\/p>\n<p>&nbsp;<\/p>\n<p>faneblade.add (&#8220;Panel&#8221;, panel);<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j fanebladene til vinduet<\/p>\n<p>&nbsp;<\/p>\n<p>getContentPane().add (faneblade);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>TabDemo me = new TabDemo();<\/p>\n<p>me.show();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Programmet tilf\u00f8jer et tekstfelt, en knap, en label og et panel til fanebladsoversigten, f\u00f8r denne f\u00f8jes til vinduet. Det giver et sk\u00e6rmbillede svarende til figur 2.6.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0206.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-366\" title=\"0206\" src=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0206.png\" alt=\"\" width=\"400\" height=\"300\" srcset=\"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0206.png 400w, https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0206-300x225.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Fig. 2.6 \u2013 Faneblade.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>Tr\u00e6er<\/h1>\n<p>&nbsp;<\/p>\n<p>I Swing bruges tr\u00e6er til grafisk repr\u00e6sentation. Det vil sige, at de ikke virkeligt indeholder dataene. Tr\u00e6er skal s\u00e5ledes i denne sammenh\u00e6ng ikke forst\u00e5s som eksempelvis bin\u00e6re s\u00f8getr\u00e6er. Meget af teorien er imidlertid den samme.<\/p>\n<p>&nbsp;<\/p>\n<p>Figur 2.7 viser, hvordan et tr\u00e6 kan se ud. Hvert element i tr\u00e6et er en knude \u2013 eller p\u00e5 engelsk en <em>node<\/em>. Den \u00f8verste knude\u00a0 \u2013 \u201dAvanceret Java-programmering\u201d \u2013 er roden af tr\u00e6et, mens de knuder, der ikke har andre knuder under sig \u2013 for eksempel Swing kontra AWT \u2013 kaldes blade. Knuder, der er p\u00e5 samme niveau, kaldes s\u00f8skende. Knuder, der er p\u00e5 et niveau h\u00f8jere end den aktuelle knude, er denne knudes for\u00e6ldre, mens knuder p\u00e5 et lavere niveau er knudens b\u00f8rn. I eksemplet i figur 2.7 betyder det alts\u00e5, at \u201dSwing kontra AWT\u201d og \u201dVinduer\u201d er s\u00f8skende, der begge er b\u00f8rn af \u201dKapitel 1\u201d.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0207.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-367\" title=\"0207\" src=\"http:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0207.png\" alt=\"\" width=\"400\" height=\"300\" srcset=\"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0207.png 400w, https:\/\/www.kristiansborg.dk\/bibliotek\/wp-content\/uploads\/2011\/11\/0207-300x225.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Fig. 2.7 \u2013 Tr\u00e6repr\u00e6sentation af data.<\/p>\n<p>&nbsp;<\/p>\n<p>I Java repr\u00e6senteres alle knuder af klassen DefaultMutableTreeNode, der ligger i pakken com.sun.java.swing.tree. Denne klasse har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>public class DefaultMutableTreeNode extends java.lang.Object implements java.lang.Cloneable, com.sun.java.swing.tree.MutableTreeNode, java.io.Serializable<\/p>\n<p>{<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode(java.lang.Object);<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode(java.lang.Object,boolean);<\/p>\n<p>&nbsp;<\/p>\n<p>public void add(com.sun.java.swing.tree.MutableTreeNode);<\/p>\n<p>public com.sun.java.swing.tree.TreeNode getChildAfter(com.sun.java.swing.tree.TreeNode);<\/p>\n<p>public com.sun.java.swing.tree.TreeNode getChildAt(int);<\/p>\n<p>public com.sun.java.swing.tree.TreeNode getChildBefore(com.sun.java.swing.tree.TreeNode);<\/p>\n<p>public int getChildCount();<\/p>\n<p>public int getDepth();<\/p>\n<p>public com.sun.java.swing.tree.TreeNode getFirstChild();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode getFirstLeaf();<\/p>\n<p>public int getIndex(com.sun.java.swing.tree.TreeNode);<\/p>\n<p>public com.sun.java.swing.tree.TreeNode getLastChild();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode getLastLeaf();<\/p>\n<p>public int getLeafCount();<\/p>\n<p>public int getLevel();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode getNextLeaf();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode getNextNode();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode getNextSibling();<\/p>\n<p>&nbsp;<\/p>\n<p>public com.sun.java.swing.tree.TreeNode getParent();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode getPreviousLeaf();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode getPreviousNode();<\/p>\n<p>public com.sun.java.swing.tree.DefaultMutableTreeNode getPreviousSibling();<\/p>\n<p>public com.sun.java.swing.tree.TreeNode getRoot();<\/p>\n<p>public com.sun.java.swing.tree.TreeNode getSharedAncestor(com.sun.java.swing.tree.DefaultMutableTreeNode);<\/p>\n<p>public int getSiblingCount();<\/p>\n<p>public void insert(com.sun.java.swing.tree.MutableTreeNode, int);<\/p>\n<p>public boolean isLeaf();<\/p>\n<p>public boolean isNodeAncestor(com.sun.java.swing.tree.TreeNode);<\/p>\n<p>public boolean isNodeChild(com.sun.java.swing.tree.TreeNode);<\/p>\n<p>public boolean isNodeDescendant(com.sun.java.swing.tree.DefaultMutableTreeNode);<\/p>\n<p>public boolean isNodeRelated(com.sun.java.swing.tree.DefaultMutableTreeNode);<\/p>\n<p>public boolean isNodeSibling(com.sun.java.swing.tree.TreeNode);<\/p>\n<p>public boolean isRoot();<\/p>\n<p>public void remove(int);<\/p>\n<p>public void remove(com.sun.java.swing.tree.MutableTreeNode);<\/p>\n<p>public void removeAllChildren();<\/p>\n<p>public void removeFromParent();<\/p>\n<p>public void setAllowsChildren(boolean);<\/p>\n<p>public void setParent(com.sun.java.swing.tree.MutableTreeNode);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Klassen indeholder mange af de funktioner, der er typiske for tr\u00e6er. Mulighed for at f\u00e5 information om den aktuelle knude \u2013 er den et blad, er den roden, og s\u00e5 videre \u2013 og mulighed for at f\u00e5 referencer til de knuder, der befinder sig i n\u00e6rheden af den aktuelle knude.<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis man blot vil have en grafisk repr\u00e6sentation af sine data, kan man n\u00f8jes med at kende metoden add. Den tilf\u00f8jer en knude som barn til den aktuelle knude.<\/p>\n<p>&nbsp;<\/p>\n<p>Den f\u00f8rste knude \u2013 roden \u2013 bruges til at oprette selve tr\u00e6et. Tr\u00e6et repr\u00e6senteres af klassen Jtree, og roden sendes med som parameter til dennes constructor.<\/p>\n<p>&nbsp;<\/p>\n<p>JTree-klassen indeholder f\u00f8lgende metoder:<\/p>\n<p>&nbsp;<\/p>\n<p>public class JTree extends com.sun.java.swing.JComponent implements com.sun.java.swing.Scrollable, com.sun.java.accessibility.Accessible<\/p>\n<p>{<\/p>\n<p>public com.sun.java.swing.JTree();<\/p>\n<p>&nbsp;<\/p>\n<p>public com.sun.java.swing.JTree(com.sun.java.swing.tree.TreeNode);<\/p>\n<p>public com.sun.java.swing.JTree(com.sun.java.swing.tree.TreeNode,boolean);<\/p>\n<p>public com.sun.java.swing.JTree(java.util.Hashtable);<\/p>\n<p>public com.sun.java.swing.JTree(java.util.Vector);<\/p>\n<p>public com.sun.java.swing.JTree(java.lang.Object[]);<\/p>\n<p>&nbsp;<\/p>\n<p>public void addTreeExpansionListener(com.sun.java.swing.event.TreeExpansionListener);<\/p>\n<p>public void addTreeSelectionListener(com.sun.java.swing.event.TreeSelectionListener);<\/p>\n<p>public void addTreeWillExpandListener(com.sun.java.swing.event.TreeWillExpandListener);<\/p>\n<p>public void collapsePath(com.sun.java.swing.tree.TreePath);<\/p>\n<p>public void collapseRow(int);<\/p>\n<p>public void expandPath(com.sun.java.swing.tree.TreePath);<\/p>\n<p>public void expandRow(int);<\/p>\n<p>public com.sun.java.swing.tree.TreePath getClosestPathForLocation(int, int);<\/p>\n<p>public int getClosestRowForLocation(int, int);<\/p>\n<p>public com.sun.java.swing.tree.TreePath getEditingPath();<\/p>\n<p>public java.lang.Object getLastSelectedPathComponent();<\/p>\n<p>public com.sun.java.swing.tree.TreePath getLeadSelectionPath();<\/p>\n<p>public int getLeadSelectionRow();<\/p>\n<p>public int getMaxSelectionRow();<\/p>\n<p>public int getMinSelectionRow();<\/p>\n<p>protected com.sun.java.swing.tree.TreePath[] getPathBetweenRows(int, int);<\/p>\n<p>public com.sun.java.swing.tree.TreePath getPathForLocation(int, int);<\/p>\n<p>public com.sun.java.swing.tree.TreePath getPathForRow(int);<\/p>\n<p>public int getRowCount();<\/p>\n<p>public int getRowForLocation(int, int);<\/p>\n<p>public int getRowForPath(com.sun.java.swing.tree.TreePath);<\/p>\n<p>public int getRowHeight();<\/p>\n<p>public int getSelectionCount();<\/p>\n<p>public com.sun.java.swing.tree.TreeSelectionModel getSelectionModel();<\/p>\n<p>public com.sun.java.swing.tree.TreePath getSelectionPath();<\/p>\n<p>public com.sun.java.swing.tree.TreePath[] getSelectionPaths();<\/p>\n<p>public void makeVisible(com.sun.java.swing.tree.TreePath);<\/p>\n<p>public void removeTreeExpansionListener(com.sun.java.swing.event.TreeExpansionListener);<\/p>\n<p>&nbsp;<\/p>\n<p>public void removeTreeSelectionListener(com.sun.java.swing.event.TreeSelectionListener);<\/p>\n<p>public void removeTreeWillExpandListener(com.sun.java.swing.event.TreeWillExpandListener);<\/p>\n<p>public void scrollPathToVisible(com.sun.java.swing.tree.TreePath);<\/p>\n<p>public void scrollRowToVisible(int);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Mange af JTree\u2019s metoder kan bruges til at f\u00e5 informationer om tr\u00e6et og dets indhold. Disse vil blive beskrevet senere i dette kapitel. I f\u00f8rste omgang er det nok at se, at klassen har en constructor, der tager et TreeNode-objekt. TreeNode er et interface, som DefaultMutableTreeNode implementerer. Som det ses af nedenst\u00e5ende eksempel, betyder det, at man kan oprette et tr\u00e6 baseret p\u00e5 \u00e9n knude \u2013 roden.<\/p>\n<p>&nbsp;<\/p>\n<p>import com.sun.java.swing.*;<\/p>\n<p>import com.sun.java.swing.tree.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class Indholdsfortegnelse extends JFrame<\/p>\n<p>{<\/p>\n<p>public Indholdsfortegnelse ()<\/p>\n<p>{<\/p>\n<p>\/\/ Tilpas vinduet<\/p>\n<p>&nbsp;<\/p>\n<p>super (&#8220;Indholdsfortegnelse&#8221;);<\/p>\n<p>setSize (400,300);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret knuder<\/p>\n<p>&nbsp;<\/p>\n<p>DefaultMutableTreeNode bog = new DefaultMutableTreeNode (&#8220;Avanceret Java-programmering&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>DefaultMutableTreeNode kap1 = new DefaultMutableTreeNode (&#8220;Kapitel 1&#8221;);<\/p>\n<p>DefaultMutableTreeNode kap2 = new DefaultMutableTreeNode (&#8220;Kapitel 2&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>bog.add(kap1);<\/p>\n<p>bog.add(kap2);<\/p>\n<p>&nbsp;<\/p>\n<p>DefaultMutableTreeNode emne = null;<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j nogle emner fra kapitel 1<\/p>\n<p>&nbsp;<\/p>\n<p>kap1.add(new DefaultMutableTreeNode (&#8220;Swing kontra AWT&#8221;));<\/p>\n<p>kap1.add(new DefaultMutableTreeNode (&#8220;Vinduer&#8221;));<\/p>\n<p>kap1.add(new DefaultMutableTreeNode (&#8220;Standarddialogbokse&#8221;));<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j nogle emner fra kapitel 2<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>DefaultMutableTreeNode menuer = new DefaultMutableTreeNode (&#8220;Menuer&#8221;);<\/p>\n<p>menuer.add (new DefaultMutableTreeNode (&#8220;Genvejstaster&#8221;));<\/p>\n<p>menuer.add (new DefaultMutableTreeNode (&#8220;Action&#8221;));<\/p>\n<p>kap2.add (menuer);<\/p>\n<p>kap2.add (new DefaultMutableTreeNode (&#8220;Indikatorer&#8221;));<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Opret tr\u00e6et<\/p>\n<p>&nbsp;<\/p>\n<p>JTree tree = new JTree (bog);<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j tr\u00e6et til vinduet<\/p>\n<p>&nbsp;<\/p>\n<p>getContentPane().add (tree);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>Indholdsfortegnelse me = new Indholdsfortegnelse();<\/p>\n<p>me.show();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Ovenst\u00e5ende klasse giver det sk\u00e6rmbillede, der s\u00e5s af figur 2.7.<\/p>\n<p>&nbsp;<\/p>\n<p>Mange af de funktioner, der findes i JTree, er relateret til at finde stier i tr\u00e6et. Disse stier repr\u00e6senteres af klassen TreePath. TreePath-objekter er en r\u00e6kke af objekter, der findes i en sti fra en knude i tr\u00e6et til en anden. TreePath-klassen har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>public class TreePath extends java.lang.Object implements java.io.Serializable<\/p>\n<p>{<\/p>\n<p>public com.sun.java.swing.tree.TreePath(java.lang.Object);<\/p>\n<p>public com.sun.java.swing.tree.TreePath(java.lang.Object[]);<\/p>\n<p>&nbsp;<\/p>\n<p>public java.lang.Object getLastPathComponent();<\/p>\n<p>public com.sun.java.swing.tree.TreePath getParentPath();<\/p>\n<p>public java.lang.Object[] getPath();<\/p>\n<p>public java.lang.Object getPathComponent(int);<\/p>\n<p>public int getPathCount();<\/p>\n<p>public boolean isDescendant(com.sun.java.swing.tree.TreePath);<\/p>\n<p>public com.sun.java.swing.tree.TreePath pathByAddingChild(java.lang.Object);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Metoden getLastPathComponent kan bruges til at f\u00e5 en reference til det sidste element i stien. N\u00e5r man har bearbejdet dette, kan man f\u00e5 en reference til resten af stien ved at kalde getParentPath.<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r tr\u00e6et er defineret, kan man uden videre tilf\u00f8je det til et vindue, og det<\/p>\n<p>&nbsp;<\/p>\n<p>vil s\u00e5 blive vist. Hvis man \u00f8nsker det layoutet p\u00e5 en anden m\u00e5de end den, Java s\u00e6dvanligvis bruger, kan man g\u00f8re det ved hj\u00e6lp af klassen BasicTreeCellRenderer. Med denne klasse er det blandt andet muligt at \u00e6ndre de ikoner, der bruges til at vide, at en knude har b\u00f8rn. BasicTreeCellRenderer vil blive beskrevet yderligere i kapitel 3.<\/p>\n<p>&nbsp;<\/p>\n<h1>Tr\u00e5de<\/h1>\n<p>&nbsp;<\/p>\n<p>Swing er ikke fuldst\u00e6ndigt synkroniseret. Det vil sige, at man selv skal v\u00e6re opm\u00e6rksom p\u00e5, at der kan opst\u00e5 synkroniseringsproblemer.<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis ens program er en applikation \u2013 det vil sige ikke en applet \u2013 og det har f\u00f8lgende opbygning, er der ingen problemer:<\/p>\n<p>&nbsp;<\/p>\n<p>public class MyApp<\/p>\n<p>{<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>JFrame vindue = new JFrame();<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Tilf\u00f8j komponenter&#8230;<\/p>\n<p>&nbsp;<\/p>\n<p>vindue.pack()<\/p>\n<p>vindue.show();<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Herefter \u00e6ndres der ikke p\u00e5 brugergr\u00e6nsefladen<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ Al \u00f8vrig manipulation af brugergr\u00e6nsefladen sker i<\/p>\n<p>\/\/ eventhandlers som for eksempel actionPerformed.<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Er ens program derimod en applet, eller opretter man tr\u00e5de, der manipulerer brugergr\u00e6nsefladen, kan der opst\u00e5 problemer. Generelt er der en regel, der skal overholdes:<\/p>\n<p>\u201cN\u00e5r et Swing-komponent er blevet realiseret, skal al kode, der kan p\u00e5virke eller afh\u00e6nger af komponentets tilstand, afvikles i en eventh\u00e5ndteringstr\u00e5d.\u201d<\/p>\n<p>&nbsp;<\/p>\n<p>For at forst\u00e5 reglen m\u00e5 man forst\u00e5, hvorn\u00e5r et komponent er realiseret. Det er det, n\u00e5r komponentets paint-metode er blevet kaldt. Ved vinduer sker det ved at kalde setVisible(true), show eller pack. Alle komponenter, der er en del af et vindue, som bliver realiseret, bliver ogs\u00e5 selv realiseret. Et komponent bliver ogs\u00e5 realiseret, hvis det bliver tilf\u00f8jet til en container, der allerede er realiseret.<\/p>\n<p>&nbsp;<\/p>\n<p>Som bekendt findes der ikke en regel uden undtagelse. I dette tilf\u00e6lde er undtagelsen de metoder, der i Java-specifikationerne er markeret som <em>thread safe<\/em>. Dette g\u00e6lder blandt andet metoderne repaint, revalidate og invalidate. Derudover er det altid tilladt at tilf\u00f8je eller fjerne en <em>listener<\/em>.<\/p>\n<p>&nbsp;<\/p>\n<p>Generelt er f\u00f8lgende fremgangsm\u00e5de sikker:<\/p>\n<p>&nbsp;<\/p>\n<p>1. \u00a0 Man opretter en brugergr\u00e6nseflade uden at vise den. Man m\u00e5 altid oprette en brugergr\u00e6nseflade, n\u00e5r bare den ikke refererer til en brugergr\u00e6nseflade, der allerede er realiseret.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>2. \u00a0 Man realiserer komponenterne i brugergr\u00e6nsefladen ved at kalde pack-metoden.<\/p>\n<p>3. \u00a0 Umiddelbart derefter viser man vinduet ved hj\u00e6lp af setVisible(true) eller show. Dette er ikke en sikker operation, da komponenterne allerede er blevet realiseret, men da brugergr\u00e6nsefladen ikke bliver vist, er det s\u00e6rdeles usandsynligt, at paint-metoden vil blive kaldt, f\u00f8r setVisible eller show returnerer.<\/p>\n<p>4. \u00a0 Der bliver ikke udf\u00f8rt yderligere funktioner relateret til brugergr\u00e6nsefladen i hovedtr\u00e5den. Al manipulation foretages i metoder, der bliver kaldt som respons p\u00e5 events.<\/p>\n<p>&nbsp;<\/p>\n<p>I langt de fleste programmer falder det naturligt kun at opdatere brugergr\u00e6nsefladen i forbindelse med h\u00e5ndtering af events. I de fleste programmer sker opdateringer efter eksempelvis klik p\u00e5 en knap. Der er imidlertid nogle undtagelser.<\/p>\n<p>&nbsp;<\/p>\n<p>Det g\u00e6lder for eksempel appletter. Alle appletter bliver tilf\u00f8jet til sider, der allerede er synlige. Derfor b\u00f8r al kode, der tilf\u00f8jer komponenter til en applet, ligge i den tr\u00e5d, som h\u00e5ndterer events.<\/p>\n<p>&nbsp;<\/p>\n<p>Ogs\u00e5 programmer, der skal opdatere sk\u00e6rmbilleder, n\u00e5r der indtr\u00e6ffer begivenheder, der ikke er relateret til brugergr\u00e6nsefladen, har problemer. Det kan eksempelvis v\u00e6re serverprogrammer, der modtager foresp\u00f8rgsler fra klienter og viser status i et vindue. Disse foresp\u00f8rgsler kan komme p\u00e5 et hvilket som helst tidspunkt \u2013 uafh\u00e6ngigt af, hvad der sker p\u00e5 brugergr\u00e6nsefladen \u2013 og derfor skal disse opdateringer ogs\u00e5 l\u00e6gges i den tr\u00e5d, der h\u00e5ndterer events.<\/p>\n<p>&nbsp;<\/p>\n<p>For at sikre, at alle opdateringer af brugergr\u00e6nsefladen bliver lagt i eventh\u00e5ndteringstr\u00e5den, kan man bruge to metoder i klassen SwingUtilities. Det drejer sig om invokeLater og invokeAndWait. Metoden invokeLater kan kaldes fra enhver tr\u00e5d for at bede eventh\u00e5ndteringen om at udf\u00f8re et bestemt stykke kode. Den kode, der skal udf\u00f8res, skal l\u00e6gges i run-metoden af et Runnable-objekt. Metoden returnerer omg\u00e5ende uden at vente p\u00e5, at eventh\u00e5ndteringen udf\u00f8rer koden. Nedenst\u00e5ende eksempel viser, hvordan invokeLater kan bruges:<\/p>\n<p>&nbsp;<\/p>\n<p>Runnable doit = new Runnable()<\/p>\n<p>{<\/p>\n<p>public void run()<\/p>\n<p>{<\/p>\n<p>\/\/ Det, der skal udf\u00f8res<\/p>\n<p>}<\/p>\n<p>};<\/p>\n<p>&nbsp;<\/p>\n<p>SwingUtilities.invokeLater(doit);<\/p>\n<p>&nbsp;<\/p>\n<p>Metoden invokeAndWait minder meget om invokeLater bortset fra, at invokeAndWait ikke returnerer, f\u00f8r eventh\u00e5ndteringen er fuldf\u00f8rt.<\/p>\n<p>&nbsp;<\/p>\n<p>Alt i alt er der en del, man skal v\u00e6re opm\u00e6rsom p\u00e5, hvis man bruger tr\u00e5de sammen med Swing. Anbefalingen m\u00e5 derfor v\u00e6re at undg\u00e5 dem, hvis det er muligt. Som regel kan man sagtens lave en brugergr\u00e6nseflade uden anvendelse af tr\u00e5de. Der er imidlertid nogle tilf\u00e6lde, hvor det er n\u00f8dvendigt at bruge tr\u00e5de:<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<ul>\n<li>Programmet udf\u00f8rer en opgave, der tager lang tid. Hvis dette l\u00e6gges i eventh\u00e5ndteringstr\u00e5den, vil denne v\u00e6re blokeret, s\u00e5 l\u00e6nge opgaven udf\u00f8res.<\/li>\n<li>Programmet skal have en funktion udf\u00f8rt med j\u00e6vne mellemrum.<\/li>\n<li>Programmet venter p\u00e5 beskeder fra klienter.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>For at tillade programm\u00f8rer at bruge tr\u00e5de i disse tilf\u00e6lde, kan man bruge en af to hj\u00e6lpeklasser. Det drejer sig om Timer-klassen, som er blevet gennemg\u00e5et tidligere i kapitlet, og klassen SwingWorker. SwingWorker er udviklet af Sun, men er ikke en del af Swing. Klassen er implementeret s\u00e5ledes:<\/p>\n<p>&nbsp;<\/p>\n<p>import com.sun.java.swing.SwingUtilities;<\/p>\n<p>&nbsp;<\/p>\n<p>abstract class SwingWorker<\/p>\n<p>{<\/p>\n<p>private Object value;<\/p>\n<p>private Thread thread;<\/p>\n<p>&nbsp;<\/p>\n<p>\/**<\/p>\n<p>* Compute the value to be returned by the &lt;code&gt;get&lt;\/code&gt; method.<\/p>\n<p>*\/<\/p>\n<p>&nbsp;<\/p>\n<p>public abstract Object construct();<\/p>\n<p>&nbsp;<\/p>\n<p>\/**<\/p>\n<p>* Called on the event dispatching thread (not on the worker thread)<\/p>\n<p>* after the &lt;code&gt;construct&lt;\/code&gt; method has returned.<\/p>\n<p>*\/<\/p>\n<p>&nbsp;<\/p>\n<p>public void finished()<\/p>\n<p>{<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>\/**<\/p>\n<p>* Return the value created by the &lt;code&gt;construct&lt;\/code&gt; method.<\/p>\n<p>*\/<\/p>\n<p>&nbsp;<\/p>\n<p>public Object get()<\/p>\n<p>{<\/p>\n<p>while (true)\u00a0 \/\/ keep trying if we&#8217;re interrupted<\/p>\n<p>{<\/p>\n<p>Thread t;<\/p>\n<p>synchronized (SwingWorker.this)<\/p>\n<p>{<\/p>\n<p>t = thread;<\/p>\n<p>if (t == null)<\/p>\n<p>{<\/p>\n<p>return value;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>t.join();<\/p>\n<p>}<\/p>\n<p>catch (InterruptedException e)<\/p>\n<p>{<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>\/**<\/p>\n<p>* Start a thread that will call the &lt;code&gt;construct&lt;\/code&gt; method<\/p>\n<p>* and then exit.<\/p>\n<p>*\/<\/p>\n<p>&nbsp;<\/p>\n<p>public SwingWorker()<\/p>\n<p>{<\/p>\n<p>final Runnable doFinished = new Runnable()<\/p>\n<p>{<\/p>\n<p>public void run()<\/p>\n<p>{<\/p>\n<p>finished();<\/p>\n<p>}<\/p>\n<p>};<\/p>\n<p>&nbsp;<\/p>\n<p>Runnable doConstruct = new Runnable()<\/p>\n<p>{<\/p>\n<p>public void run()<\/p>\n<p>{<\/p>\n<p>synchronized(SwingWorker.this)<\/p>\n<p>{<\/p>\n<p>value = construct();<\/p>\n<p>thread = null;<\/p>\n<p>}<\/p>\n<p>SwingUtilities.invokeLater(doFinished);<\/p>\n<p>}<\/p>\n<p>};<\/p>\n<p>&nbsp;<\/p>\n<p>thread = new Thread(doConstruct);<\/p>\n<p>thread.start();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>For at bruge SwingWorker-klassen skal man f\u00f8rst oprette en klasse, der nedarver fra den. Denne klasse skal implementere construct-metoden. Denne metode skal indeholde den kode, der skal udf\u00f8res i tr\u00e5den. N\u00e5r SwingWorker-objektet instantieres, bliver der automatisk oprettet en tr\u00e5d, som udf\u00f8rer arbejdet. N\u00e5r man skal bruge resultatet af funktionen, kalder man SwingWorker\u2019s get-funktion.<\/p>\n<p>&nbsp;<\/p>\n<p><strong><br \/>\n<\/strong><\/p>\n<h1>Sammenfatning<\/h1>\n<p>Hvor det foreg\u00e5ende kapitel gav en overordnet introduktion til Swing, er der i dette kapitel blevet kigget n\u00e6rmere p\u00e5 nogle af de mere avancerede emner og komponenter. Det er blandt andet blevet gennemg\u00e5er, hvordan man anvender en lang r\u00e6kke af de komponenter, der ikke fandtes i AWT \u2013 for eksempel indikatorer, skydere, faneblade og tr\u00e6er \u2013 ligesom det er blevet forklaret, hvilke emner, man skal t\u00e6nke p\u00e5, n\u00e5r man arbejder med tr\u00e5de i en applikation, der benytter Swing.<\/p>\n<p>&nbsp;<\/p>\n<h1>FAQ<\/h1>\n<p>&nbsp;<\/p>\n<h2>Beh\u00f8ver man bruge indre klasser, n\u00e5r man bruger tr\u00e5de sammen med Swing?<\/h2>\n<p>&nbsp;<\/p>\n<p>Nej. I de eksempler, der er i vist her i kapitlet, bruges indre klasser, fordi man p\u00e5 den m\u00e5de kan g\u00f8re programmerne mindre. Hvis man vil, kan man sagtens oprette en ny klasse, instantiere den og sende en reference til den som parameter til eksempelvis invokeLater-metoden. S\u00e5 kan programstumpen komme til at se s\u00e5ledes ud:<\/p>\n<p>&nbsp;<\/p>\n<p>class myThread implements Runnable<\/p>\n<p>{<\/p>\n<p>public void run()<\/p>\n<p>{<\/p>\n<p>\/\/ Udf\u00f8r beregningen<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public class myClass<\/p>\n<p>{<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>\/\/ &#8230;<\/p>\n<p>&nbsp;<\/p>\n<p>invokeLater (new myThread());<\/p>\n<p>&nbsp;<\/p>\n<p>\/\/ &#8230;<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2>Hvorfor bliver min menu vist bag de andre Swing-komponenter?<\/h2>\n<p>&nbsp;<\/p>\n<p>Nogle Swing-komponenter er lette \u2013 <em>light weight<\/em> \u2013 mens andre er tunge \u2013 <em>heavy weight<\/em>. Lette komponenter vil som standard ikke bliver vist foran tunge komponenter. For at \u00e6ndre dette, s\u00e5 menuen bliver synlig, kan man inds\u00e6tte nedenst\u00e5ende programlinie, inden man opretter menuer.<\/p>\n<p>&nbsp;<\/p>\n<p>com.sun.java.swing.JPopupMenu.setDefaultLightWeightPopupEnabled(false);<\/p>\n<p>&nbsp;<\/p>\n<p>Denne fremgangsm\u00e5de virker ogs\u00e5, selvom man bruger andre menuer end JPopupMenu.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2>Hvorfor ser rammerne omkring min Swing-komponent anderledes ud, efter jeg har redefineret paint-metoden?<\/h2>\n<p>&nbsp;<\/p>\n<p>I Swing er tegningen af et komponent opdelt i flere forskellige rutiner:<\/p>\n<p>&nbsp;<\/p>\n<ul>\n<li>paintComponent (Graphics)<\/li>\n<li>paintBorder (Graphics)<\/li>\n<li>paintChildren (Graphics)<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Hvis man redefinerer paint-metoden, skal man huske at kalde disse metoder, da de ellers ikke vil blive udf\u00f8rt.<\/p>\n<div style=\"border: 1px solid red; color: red\">\n<b>Du l\u00e6ser en gammel bog<\/b><br \/>\nDen bog, du l\u00e6ser her, er fra 1998, og mange ting kan have \u00e6ndret sig siden da.<br \/>\nVi h\u00e5ber, at du stadig kan finde relevant information i den.<br \/>\nHvis du vil l\u00e6se aktuelle oplysninger om de avancerede dele af Java, anbefaler vi<br \/>\nbogen <a href=\"http:\/\/clk.tradedoubler.com\/click?p(197229)a(2029446)g(19172914)url(http:\/\/www.adlibris.com\/dk\/product.aspx?isbn=0132354799)\"  target=\"_blank\">Core Java &#8211; Advanced Features<\/a><img decoding=\"async\" src=\"http:\/\/impdk.tradedoubler.com\/imp?type(inv)g(19172914)a(2029446)\" \/>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Du l\u00e6ser en gammel bog Den bog, du l\u00e6ser her, er fra 1998, og mange ting kan have \u00e6ndret sig siden da. Vi h\u00e5ber, at du stadig kan finde relevant information i den. Hvis du vil l\u00e6se aktuelle oplysninger om de avancerede dele af Java, anbefaler vi bogen Core Java &#8211; Advanced Features Forrige kapitel [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":332,"menu_order":0,"comment_status":"closed","ping_status":"open","template":"","meta":{"_monsterinsights_skip_tracking":false,"footnotes":""},"class_list":["post-360","page","type-page","status-publish","hentry"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"da_DK\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Biblioteket | Samling af artikler og b\u00f8ger\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Kapitel 2 Videre med Swing | Biblioteket\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2011-11-03T10:11:17+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2011-11-03T10:22:47+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Kapitel 2 Videre med Swing | Biblioteket\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-2-videre-med-swing\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/#listItem\",\"name\":\"B\\u00f8ger\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/#listItem\",\"position\":2,\"name\":\"B\\u00f8ger\",\"item\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/#listItem\",\"name\":\"Avanceret Java-programmering\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/#listItem\",\"position\":3,\"name\":\"Avanceret Java-programmering\",\"item\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-2-videre-med-swing\\\/#listItem\",\"name\":\"Kapitel 2 Videre med Swing\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/#listItem\",\"name\":\"B\\u00f8ger\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-2-videre-med-swing\\\/#listItem\",\"position\":4,\"name\":\"Kapitel 2 Videre med Swing\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/#listItem\",\"name\":\"Avanceret Java-programmering\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/#organization\",\"name\":\"Biblioteket\",\"description\":\"Samling af artikler og b\\u00f8ger\",\"url\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-2-videre-med-swing\\\/#webpage\",\"url\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-2-videre-med-swing\\\/\",\"name\":\"Kapitel 2 Videre med Swing | Biblioteket\",\"inLanguage\":\"da-DK\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-2-videre-med-swing\\\/#breadcrumblist\"},\"datePublished\":\"2011-11-03T12:11:17+02:00\",\"dateModified\":\"2011-11-03T12:22:47+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/#website\",\"url\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/\",\"name\":\"Biblioteket\",\"description\":\"Samling af artikler og b\\u00f8ger\",\"inLanguage\":\"da-DK\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Kapitel 2 Videre med Swing | Biblioteket","description":"","canonical_url":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BreadcrumbList","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek#listItem","position":1,"name":"Home","item":"https:\/\/www.kristiansborg.dk\/bibliotek","nextItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/#listItem","name":"B\u00f8ger"}},{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/#listItem","position":2,"name":"B\u00f8ger","item":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/#listItem","name":"Avanceret Java-programmering"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/#listItem","position":3,"name":"Avanceret Java-programmering","item":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/#listItem","name":"Kapitel 2 Videre med Swing"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/#listItem","name":"B\u00f8ger"}},{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/#listItem","position":4,"name":"Kapitel 2 Videre med Swing","previousItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/#listItem","name":"Avanceret Java-programmering"}}]},{"@type":"Organization","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/#organization","name":"Biblioteket","description":"Samling af artikler og b\u00f8ger","url":"https:\/\/www.kristiansborg.dk\/bibliotek\/"},{"@type":"WebPage","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/#webpage","url":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/","name":"Kapitel 2 Videre med Swing | Biblioteket","inLanguage":"da-DK","isPartOf":{"@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/#website"},"breadcrumb":{"@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/#breadcrumblist"},"datePublished":"2011-11-03T12:11:17+02:00","dateModified":"2011-11-03T12:22:47+02:00"},{"@type":"WebSite","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/#website","url":"https:\/\/www.kristiansborg.dk\/bibliotek\/","name":"Biblioteket","description":"Samling af artikler og b\u00f8ger","inLanguage":"da-DK","publisher":{"@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/#organization"}}]},"og:locale":"da_DK","og:site_name":"Biblioteket | Samling af artikler og b\u00f8ger","og:type":"article","og:title":"Kapitel 2 Videre med Swing | Biblioteket","og:url":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/","article:published_time":"2011-11-03T10:11:17+00:00","article:modified_time":"2011-11-03T10:22:47+00:00","twitter:card":"summary","twitter:title":"Kapitel 2 Videre med Swing | Biblioteket"},"aioseo_meta_data":{"post_id":"360","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":null,"created":"2020-12-21 06:59:15","updated":"2025-06-04 02:17:13","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.kristiansborg.dk\/bibliotek\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/\" title=\"B\u00f8ger\">B\u00f8ger<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/\" title=\"Avanceret Java-programmering\">Avanceret Java-programmering<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tKapitel 2 Videre med Swing\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.kristiansborg.dk\/bibliotek"},{"label":"B\u00f8ger","link":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/"},{"label":"Avanceret Java-programmering","link":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/"},{"label":"Kapitel 2 Videre med Swing","link":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-2-videre-med-swing\/"}],"_links":{"self":[{"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages\/360","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/comments?post=360"}],"version-history":[{"count":3,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages\/360\/revisions"}],"predecessor-version":[{"id":369,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages\/360\/revisions\/369"}],"up":[{"embeddable":true,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages\/332"}],"wp:attachment":[{"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/media?parent=360"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}