📄 s02.htm
字号:
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所示的小应用程序把一个Swing按钮和一个AWT按钮添加到一个面板中,这个面板是要滚动的组件。这个小应用程序为滚动窗格设置了首选大小,并把滚动窗格添加到其内容窗格中。<br> 图2-9所示的组件效果是我们不想要的。遗憾的是,与弹出式菜单不同,JScrollPane没有能实例化为重量组件的选项。但是,幸运的是,AWT的ScrollPane组件是一个重量滚动窗格,它和Swing的JScrollPane几乎完全相同。<br> <a href="s02_t10.htm" target="_blank">图2-10</a>示出了与图2-9相同的小应用程序,但图2-10中的小应用程序用重量AWT的ScrollPane替代了Swing的轻量JScrollPane。由于AWT滚动窗格是重量的,所以它们滚动轻量组件和重量组件都没有问题。<br> 例2-11列出了图2-10示的小应用程序的代码</p> <p align="center"><b>例2-11 使用AWT的ScrollPane来滚动重量组件</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列出的小应用程序中实现了java.awt.ScrollPane的一个扩展,以便把滚动窗格的大小设置为首选尺寸。有关Swing组件与AWT组件在设置首选尺寸方面的差别的更多信息,请参见4.2.2节“最小尺寸、最大尺寸和首选尺寸。” </p> <p> <b><a name="2.3.4"></a>2.3.4 内部窗体</b></p> <p> Swing的内部窗体是包含在桌面窗格中的窗体(参见第15章“内部窗体和桌面窗格”),Swing的内部窗体是轻量组件,如果把重量组件添加到一个内部窗体,则这个窗体很可能会遇到到麻烦。<br> <a href="s02_t11.htm" target="_blank">图2-11</a>所示的小应用程序包含两个JInternalFrame实例。它们都包含一个重量AWT画布。如果一个内部窗体与另一个内部窗体重叠,则下面的内部窗体的重量画布将会使上面的内部窗体的一部分变模糊,因为重量画布的层序比轻量内部窗体的层序高。<br> 例2-12 列出了图2-11所示的小应用程序的代码</p> <p align="center"> <b>例2-12把重量组件添加到Swing内部窗体中</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重量组件混合使用。大多数情况下,这不会是一个问题,因为Swing对所有AWT组件都提供了替代的轻量组件。对已有的、使用AWT组件的小应用程序或应用程序,最好的方法是用Swing的相应组件来替代AWT组件。如果不能替代,则必须遵守如下原则:<br> 1)如果轻量组件必须在重量组件之上显示,则不要在一个容器中混合使用轻量组件和重量组件。<br> 2)如果弹出式菜单与重量组件重叠,则必须强迫弹出式菜单成为重量组件<br> 3)如果把重量组件添加到一个JScrollPane实例中,而应该把重量组件添加到一个java.awt.ScrollPane实例中。<br> 4)不要把重量组件添加到Swing内部窗体中。 </p> <p> <b><a name="2.4"></a>2.4 Swing和线程</b></p> <p> 大多数情况下,Swing是线程不安全的,即只能从单线程来访问Swing组件。首先,我们要讨论为什么Swing是线程不安全的,然后介绍在Swing开发过程中单线程设计所带来的结果。<br> 让我们面对这个事实,甚至在java中,开发多线程的应用程序也是不容易的。设计一个线程安全的工具包就更不是一个简单的事情。例如,确定如何同步对类的访问就是一个复杂的任务(注:参见Lea,Doug,“java中的并发编程”,Addison-Wesley,1997。)。同样,扩展线程安全的类需要较高的技术,对非线程编程高手的开发人员(大多数开发人员都属此范围)是充满危险的。Swing是线程不安全的一个主要原因是为了简化扩展组件的任务。<br> Swing是线程不安全的另一个原因是由于获取和释放锁定及恢复状态所带来的开销。使用线程安全GUI工具包的所有应用程序(无论它们是否是多线程的)都必须付出同样的性能代价。<br> 线程的使用增加了调试、测试、维护和扩展的困难度。例如,测试和维护等通常已经很艰苦的工作对于大多数多线程应用程序就更困难了,有时甚至是不可能的。<br> 有些Swing组件方法确实支持多线程访问。例如,JComponent的repaint、revalidate和invalidate等方法都对放在事件派发线程上的请求进行排队。因此,可从任何线程中调用这些方法。另外,可以从多个线程把监听器添加到事件监听器列表(参见6.2节“事件监听器列表”)中或从列表中删掉。最后,有些组件方法是同步的。例如,JCheckBoxMenuItem.setState()是同步的,因此,可以从多线程中调用它。 </p> <p> <b><a name="2.4.1"></a>2.4.1 Swing单线程设计的结果</b></p> <p> Swing单线程设计的主要结果是:大多数情况下,只能从事件派发线程中访问将要在屏幕上绘制的Swing组件。<br> 事件派发线程是调用paint和update等回调方法的线程,而且,它还是事件监听器接口中定义的事件处理方法。例如,ActionListener和PropertyListener接口的实现使它们的actionPerformed方法和propertyChange方法在事件派发线程中调用。 <br> 技术上说,在Swing组件的对等组件创建之前(指可在屏幕上绘制之前)(注:对等组件是用addNotify方法创建的),它们可以从多个线程中访问。例如,可以有一个小应用程序的init方法中构造和操纵组件,只要在操纵它们之前,还没有使它们成为可见的。</p> <p> <b><a name="2.4.2"></a>2.4.2 SwingUtilties类的invokeLater和invokeAndWait方法</b></p> <p> 由于AWT和Swing都是事件驱动工具包,所以在回调方法中更新可见的GUI就是很自然的事。例如,如果在一个按钮激活,项目列表需要更新时,则通常在与该按钮相关联的事件监听器的actionPerformed方法中来实现该列表的更新。<br> 然而,有时可能需要从事件派发线程以外的线程中更新Swing组件。例如,如果上述项目列表中包含了很多来自数据库或Internet的数据,则可能在按钮激活后还要等一段时间才能看到更新的列表。如果信息的获取是在actionPerformed中实现的,则按钮仍保持按下的状态,直到对actionPerformed的调用返回,不仅按钮的弹起需要一段时间,而且一般来说,耗时较长的操作也不应当在事件方法中的执行,因为在事件处理方法返回之前,其他的事件不能派发。<br> 有时,在独立的线程上执行耗时的操作可能更好,这将允许立即更新用户界面和释放事件派发线程去派发其他的事件,幸运的是,Swing提供了两种机制,它们都支持这种想法。<br> SwingUtilities类提供了两个方法:invokdLater和invokdAndWait,它们都使事件派发线程上的可运行对象排队。当可运行对象排在事件派队列的队首时,就调用基run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。<br> 1.SwingUtilities invokeLater<br> 在介绍invokeLater和invokeAndWait方法之前,我们首先来看一个小应用程序,由于是从事件派发线程以外的线程中更新Swing组件,所以该小应用程序运行不正常。<a href="s02_t12.htm" target="_blank">图2-12</a>所示的小应用程序有一个按钮和一个进度条。当激活按钮后,就开始模仿获取信息的长操作。当获取了信息(即一个integer值)后,就用该信息来更新小应用程序的进度条。<br> 图2-12左图显示的是这个小应用程序的初始状态。图2-12右图显示的则是当激活start按钮后,这个小应用程序的样子,此时,已获取了信息,也更新了进度条。<br> 小应用程序把一个动作监听器添加到该按钮中,该监听器创建一个新线程,这个线程不断收到信息并更新进度条。每隔半秒获取一次信息,而且这个线程会获得一个对这个小应用程序进度条的引用。</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>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -