?? s02.htm
字號(hào):
panel.add(new Button("AWT Button ..."));</p> <p> Container contentPane = getContentPane();<br> JScrollPane scrollPane = new JScrollPane(panel);</p> <p> scrollPane.setPreferredSize(new Dimension(125,50));<br> contentPane.setLayout(new FlowLayout(FlowLayout.LEFT));<br> contentPane.add(scrollPane);<br> }<br> }</p> <p> 圖2-9所示的小應(yīng)用程序把一個(gè)Swing按鈕和一個(gè)AWT按鈕添加到一個(gè)面板中,這個(gè)面板是要滾動(dòng)的組件。這個(gè)小應(yīng)用程序?yàn)闈L動(dòng)窗格設(shè)置了首選大小,并把滾動(dòng)窗格添加到其內(nèi)容窗格中。<br> 圖2-9所示的組件效果是我們不想要的。遺憾的是,與彈出式菜單不同,JScrollPane沒(méi)有能實(shí)例化為重量組件的選項(xiàng)。但是,幸運(yùn)的是,AWT的ScrollPane組件是一個(gè)重量滾動(dòng)窗格,它和Swing的JScrollPane幾乎完全相同。<br> <a href="s02_t10.htm" target="_blank">圖2-10</a>示出了與圖2-9相同的小應(yīng)用程序,但圖2-10中的小應(yīng)用程序用重量AWT的ScrollPane替代了Swing的輕量JScrollPane。由于AWT滾動(dòng)窗格是重量的,所以它們滾動(dòng)輕量組件和重量組件都沒(méi)有問(wèn)題。<br> 例2-11列出了圖2-10示的小應(yīng)用程序的代碼</p> <p align="center"><b>例2-11 使用AWT的ScrollPane來(lái)滾動(dòng)重量組件</b></p> <p> import javax.swing.*;<br> import java.awt.*;<br> import java.awt.event.*;</p> <p> public class Test extends JApplet {<br> public void init() {<br> JPanel panel = new JPanel();</p> <p> panel.add(new JButton("Swing Button ..."));<br> panel.add(new Button("AWT Button ..."));</p> <p> Container contentPane = getContentPane();<br> SizedScrollPane scrollPane = new SizedScrollPane();</p> <p> scrollPane.add(panel);</p> <p> contentPane.setLayout(new FlowLayout(FlowLayout.LEFT));<br> contentPane.add(scrollPane);<br> }<br> }<br> class SizedScrollPane extends ScrollPane {<br> public Dimension getPreferredSize() {<br> return new Dimension(125,50);<br> }<br> }<br> 注意:在例2-11列出的小應(yīng)用程序中實(shí)現(xiàn)了java.awt.ScrollPane的一個(gè)擴(kuò)展,以便把滾動(dòng)窗格的大小設(shè)置為首選尺寸。有關(guān)Swing組件與AWT組件在設(shè)置首選尺寸方面的差別的更多信息,請(qǐng)參見(jiàn)4.2.2節(jié)“最小尺寸、最大尺寸和首選尺寸。” </p> <p> <b><a name="2.3.4"></a>2.3.4 內(nèi)部窗體</b></p> <p> Swing的內(nèi)部窗體是包含在桌面窗格中的窗體(參見(jiàn)第15章“內(nèi)部窗體和桌面窗格”),Swing的內(nèi)部窗體是輕量組件,如果把重量組件添加到一個(gè)內(nèi)部窗體,則這個(gè)窗體很可能會(huì)遇到到麻煩。<br> <a href="s02_t11.htm" target="_blank">圖2-11</a>所示的小應(yīng)用程序包含兩個(gè)JInternalFrame實(shí)例。它們都包含一個(gè)重量AWT畫(huà)布。如果一個(gè)內(nèi)部窗體與另一個(gè)內(nèi)部窗體重疊,則下面的內(nèi)部窗體的重量畫(huà)布將會(huì)使上面的內(nèi)部窗體的一部分變模糊,因?yàn)橹亓慨?huà)布的層序比輕量?jī)?nèi)部窗體的層序高。<br> 例2-12 列出了圖2-11所示的小應(yīng)用程序的代碼</p> <p align="center"> <b>例2-12把重量組件添加到Swing內(nèi)部窗體中</b></p> <p> import java.awt.*;<br> import java.awt.event.*;<br> import javax.swing.*;</p> <p> public class InternalFrameTest extends JApplet {<br> JDesktopPane dtp = new JDesktopPane();</p> <p> public void init() {<br> JPanel controlPanel = new ControlPanel(dtp);<br> Container contentPane = getContentPane();<br> JPanel centerPanel = new JPanel();</p> <p> contentPane.setLayout(new BorderLayout());<br> contentPane.add(controlPanel, BorderLayout.NORTH);<br> contentPane.add(dtp, BorderLayout.CENTER);<br> }<br> }<br> class ControlPanel extends JPanel {<br> private static int cnt=0;</p> <p> public ControlPanel(final JDesktopPane dtp) {<br> JButton b = new JButton("make frame");</p> <p> add(b);</p> <p> b.addActionListener(new ActionListener() {<br> public void actionPerformed(ActionEvent event) {<br> JInternalFrame jif = new JInternalFrame();<br> Container contentPane = jif.getContentPane();</p> <p> jif.setLocation(10,50);<br> jif.setTitle("Internal Frame" + cnt++);<br> jif.setResizable(true);<br> jif.setMaximizable(true);<br> jif.setClosable(true);<br> jif.setVisible(true);<br> jif.setIconifiable(true);</p> <p> contentPane.setLayout(new FlowLayout());<br> contentPane.add(new ColoredCanvas(), "Center");<br> jif.pack();</p> <p> dtp.add(jif, 2); // add at layer 2<br> }<br> });<br> }<br> }<br> class ColoredCanvas extends Canvas {<br> public void paint(Graphics g) {<br> Dimension sz = getSize();<br> g.setColor(Color.blue);<br> g.fillRect(0,0,sz.width,sz.height);<br> }<br> public Dimension getPreferredSize() {<br> return new Dimension(200,200);<br> }<br> }</p> <p><b>Swing提示</b><br> <b>混合使用AWT組件和Swing組件的原則</b><br> 一般不提倡把Swing輕量組件與AWT重量組件混合使用。大多數(shù)情況下,這不會(huì)是一個(gè)問(wèn)題,因?yàn)镾wing對(duì)所有AWT組件都提供了替代的輕量組件。對(duì)已有的、使用AWT組件的小應(yīng)用程序或應(yīng)用程序,最好的方法是用Swing的相應(yīng)組件來(lái)替代AWT組件。如果不能替代,則必須遵守如下原則:<br> 1)如果輕量組件必須在重量組件之上顯示,則不要在一個(gè)容器中混合使用輕量組件和重量組件。<br> 2)如果彈出式菜單與重量組件重疊,則必須強(qiáng)迫彈出式菜單成為重量組件<br> 3)如果把重量組件添加到一個(gè)JScrollPane實(shí)例中,而應(yīng)該把重量組件添加到一個(gè)java.awt.ScrollPane實(shí)例中。<br> 4)不要把重量組件添加到Swing內(nèi)部窗體中。 </p> <p> <b><a name="2.4"></a>2.4 Swing和線程</b></p> <p> 大多數(shù)情況下,Swing是線程不安全的,即只能從單線程來(lái)訪問(wèn)Swing組件。首先,我們要討論為什么Swing是線程不安全的,然后介紹在Swing開(kāi)發(fā)過(guò)程中單線程設(shè)計(jì)所帶來(lái)的結(jié)果。<br> 讓我們面對(duì)這個(gè)事實(shí),甚至在java中,開(kāi)發(fā)多線程的應(yīng)用程序也是不容易的。設(shè)計(jì)一個(gè)線程安全的工具包就更不是一個(gè)簡(jiǎn)單的事情。例如,確定如何同步對(duì)類(lèi)的訪問(wèn)就是一個(gè)復(fù)雜的任務(wù)(注:參見(jiàn)Lea,Doug,“java中的并發(fā)編程”,Addison-Wesley,1997。)。同樣,擴(kuò)展線程安全的類(lèi)需要較高的技術(shù),對(duì)非線程編程高手的開(kāi)發(fā)人員(大多數(shù)開(kāi)發(fā)人員都屬此范圍)是充滿危險(xiǎn)的。Swing是線程不安全的一個(gè)主要原因是為了簡(jiǎn)化擴(kuò)展組件的任務(wù)。<br> Swing是線程不安全的另一個(gè)原因是由于獲取和釋放鎖定及恢復(fù)狀態(tài)所帶來(lái)的開(kāi)銷(xiāo)。使用線程安全GUI工具包的所有應(yīng)用程序(無(wú)論它們是否是多線程的)都必須付出同樣的性能代價(jià)。<br> 線程的使用增加了調(diào)試、測(cè)試、維護(hù)和擴(kuò)展的困難度。例如,測(cè)試和維護(hù)等通常已經(jīng)很艱苦的工作對(duì)于大多數(shù)多線程應(yīng)用程序就更困難了,有時(shí)甚至是不可能的。<br> 有些Swing組件方法確實(shí)支持多線程訪問(wèn)。例如,JComponent的repaint、revalidate和invalidate等方法都對(duì)放在事件派發(fā)線程上的請(qǐng)求進(jìn)行排隊(duì)。因此,可從任何線程中調(diào)用這些方法。另外,可以從多個(gè)線程把監(jiān)聽(tīng)器添加到事件監(jiān)聽(tīng)器列表(參見(jiàn)6.2節(jié)“事件監(jiān)聽(tīng)器列表”)中或從列表中刪掉。最后,有些組件方法是同步的。例如,JCheckBoxMenuItem.setState()是同步的,因此,可以從多線程中調(diào)用它。 </p> <p> <b><a name="2.4.1"></a>2.4.1 Swing單線程設(shè)計(jì)的結(jié)果</b></p> <p> Swing單線程設(shè)計(jì)的主要結(jié)果是:大多數(shù)情況下,只能從事件派發(fā)線程中訪問(wèn)將要在屏幕上繪制的Swing組件。<br> 事件派發(fā)線程是調(diào)用paint和update等回調(diào)方法的線程,而且,它還是事件監(jiān)聽(tīng)器接口中定義的事件處理方法。例如,ActionListener和PropertyListener接口的實(shí)現(xiàn)使它們的actionPerformed方法和propertyChange方法在事件派發(fā)線程中調(diào)用。 <br> 技術(shù)上說(shuō),在Swing組件的對(duì)等組件創(chuàng)建之前(指可在屏幕上繪制之前)(注:對(duì)等組件是用addNotify方法創(chuàng)建的),它們可以從多個(gè)線程中訪問(wèn)。例如,可以有一個(gè)小應(yīng)用程序的init方法中構(gòu)造和操縱組件,只要在操縱它們之前,還沒(méi)有使它們成為可見(jiàn)的。</p> <p> <b><a name="2.4.2"></a>2.4.2 SwingUtilties類(lèi)的invokeLater和invokeAndWait方法</b></p> <p> 由于AWT和Swing都是事件驅(qū)動(dòng)工具包,所以在回調(diào)方法中更新可見(jiàn)的GUI就是很自然的事。例如,如果在一個(gè)按鈕激活,項(xiàng)目列表需要更新時(shí),則通常在與該按鈕相關(guān)聯(lián)的事件監(jiān)聽(tīng)器的actionPerformed方法中來(lái)實(shí)現(xiàn)該列表的更新。<br> 然而,有時(shí)可能需要從事件派發(fā)線程以外的線程中更新Swing組件。例如,如果上述項(xiàng)目列表中包含了很多來(lái)自數(shù)據(jù)庫(kù)或Internet的數(shù)據(jù),則可能在按鈕激活后還要等一段時(shí)間才能看到更新的列表。如果信息的獲取是在actionPerformed中實(shí)現(xiàn)的,則按鈕仍保持按下的狀態(tài),直到對(duì)actionPerformed的調(diào)用返回,不僅按鈕的彈起需要一段時(shí)間,而且一般來(lái)說(shuō),耗時(shí)較長(zhǎng)的操作也不應(yīng)當(dāng)在事件方法中的執(zhí)行,因?yàn)樵谑录幚矸椒ǚ祷刂埃渌氖录荒芘砂l(fā)。<br> 有時(shí),在獨(dú)立的線程上執(zhí)行耗時(shí)的操作可能更好,這將允許立即更新用戶界面和釋放事件派發(fā)線程去派發(fā)其他的事件,幸運(yùn)的是,Swing提供了兩種機(jī)制,它們都支持這種想法。<br> SwingUtilities類(lèi)提供了兩個(gè)方法:invokdLater和invokdAndWait,它們都使事件派發(fā)線程上的可運(yùn)行對(duì)象排隊(duì)。當(dāng)可運(yùn)行對(duì)象排在事件派隊(duì)列的隊(duì)首時(shí),就調(diào)用基run方法。其效果是允許事件派發(fā)線程調(diào)用另一個(gè)線程中的任意一個(gè)代碼塊。<br> 1.SwingUtilities invokeLater<br> 在介紹invokeLater和invokeAndWait方法之前,我們首先來(lái)看一個(gè)小應(yīng)用程序,由于是從事件派發(fā)線程以外的線程中更新Swing組件,所以該小應(yīng)用程序運(yùn)行不正常。<a href="s02_t12.htm" target="_blank">圖2-12</a>所示的小應(yīng)用程序有一個(gè)按鈕和一個(gè)進(jìn)度條。當(dāng)激活按鈕后,就開(kāi)始模仿獲取信息的長(zhǎng)操作。當(dāng)獲取了信息(即一個(gè)integer值)后,就用該信息來(lái)更新小應(yīng)用程序的進(jìn)度條。<br> 圖2-12左圖顯示的是這個(gè)小應(yīng)用程序的初始狀態(tài)。圖2-12右圖顯示的則是當(dāng)激活start按鈕后,這個(gè)小應(yīng)用程序的樣子,此時(shí),已獲取了信息,也更新了進(jìn)度條。<br> 小應(yīng)用程序把一個(gè)動(dòng)作監(jiān)聽(tīng)器添加到該按鈕中,該監(jiān)聽(tīng)器創(chuàng)建一個(gè)新線程,這個(gè)線程不斷收到信息并更新進(jìn)度條。每隔半秒獲取一次信息,而且這個(gè)線程會(huì)獲得一個(gè)對(duì)這個(gè)小應(yīng)用程序進(jìn)度條的引用。</p> <p> public class Test extends JApplet {<br> ...<br> public void init() {<br> ...<br> startButton.addActionListener(new ActionListener() {<br> public void actionPerformed(ActionEvent e) {<br> GetInfoThread t = new GetInfoThread(Test.this);<br> t.start();</p> <p> // this is ok, because actionPerformed<br> // is called on the event dispatch thread<br> startButton.setEnabled(false);<br> }<br>
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -