📄 s03.htm
字号:
</p> <p> <b><a name="3.2.2"></a>3.2.2 静态认识</b></p> <p> 轻量Swing组件的实现方式尽量与组成它们的MVC结构的对象的实现方式相类似。例如:Swing按钮由JButton类、ButtonUI类及其他对象组成。其他轻量Swing组件与此相同,以相似的类名实现相似的功能,如:JLabel与LabelUI、JCheckBox与CheckBoxUI、JTree与TreeUI等等。<br> 图3-2示出了组成Swing滑杆的类的类图。由于轻量Swing组件的MVC实现的一致性,所以图3-2对总体了解Swing MVC的根本思想是有所帮助的。<br> 与大多数轻量Swing组件一样,JSlider维护对其模型的一个引用。Swing模型由接口定义滑杆的模型实现BoundedRangeModel接口。有边界范围的模型跟踪最小值、最大值和当前值(注:有关滑杆和滑杆模型的更多信息,请参见11.2节“JSlider”)<br> Swing提供缺省的模型实现,在组件模型没有被显式地指定时,则使用这个缺省模型。例如,如果一个滑杆的模型没有被显式地指定(通常都是这种情况),则用DefaultBoundeRangeModel的实例来实现滑杆。<br> 所有的Swing轻量组件扩展JComponent类,该类维护一个对组件UI的引用。ComponentUI类是javax.swing.plaf包中的一个抽象类,javax.swing.plaf包定义UI代表的基本功能。<br> BasicSliderUI类在javax.swing.plaf.basic包中并且封装了基本的按钮功能。标准Swing界面样式的滑杆UI类扩展BasicSliderUI类并定制了缺省功能。 <br> BasicSliderUI类实现六个内部类监听器,其中的五个监听JSlider组件;这个滑杆模型中还包含BasicSliderUI.ChangeHandler。 </p> <p> <b><a name="3.2.3"></a>3.2.3 动态认识</b></p> <p> 上一节提供了组成轻量Swing组件的对象之间关系的静态视图。本节提供一个组件的组成部分之间的相互关系的动态视图。<br> 图3-3示出了一个图表,与图3-1的目的相似,它说明了Swing实现的MVC的信息3-3中示的组件代表一个轻量Swing组件类,如JButton、JLabel、JSlider等等。<br> 因为一个UI代表的监听器几乎总是作为内部类来实现的,所以,图3-3中的监听器包含在UI代表中。<br> 图3-3中的模型代表组件的模型。例如,一个按钮的模型是ButtioModel接口的一个实现。<br> “Swing MVC”中曾作过介绍,组件为其模型提供传递方法,不用直接访问一个组件的模型就能操纵模型值。因此,图3-3示出了组件变化它们的模型。<br> 我们还知道,JButton、JLabel和JSlider等组件监听它们的模型,以便把模型事件传送给组件自己的监听器。因此,图3-3描述这了种模型,所以,图3-3示了被组件 模型更新的监听器。通常,监听器通过有选择地从模型获得信息并变化组件或UI代表来响应事件。<br> 案例<br> 图3-3图解说明了组成轻量Swing组件的对象相互通信的一般情况。本节介绍两个具体的例子以进一步阐明轻量Swing的组件通信。<br> 图3-4示出了一个滑杆属性被程序修改时所发生的事件序列。JSlider的paintTicks属性是滑杆的UI代表的一个属性,不是一个模型属性。<br> Swing滑杆维护一个boolean属性,该属性决定滑杆是否绘制勾号(tick)标记。图3-4示出了JSlider.setPaintTicks()被调用时发生的事件序列。<br> 在设置了paintTicks属性后,JSlider.setPaintTicks()调用firePropertyChange(),firePropertyChange()则向滑杆的属性变化监听器报告属性变化事件。滑杆属性变化监听器之一是BasicSliderUI.PropertyChangeHandler的一个实例,它对这个事件的反映是强迫滑杆的UI代表更新滑杆。<br> 在把变化情况通知给监听器之后,JSlider.setPaintTicks()使滑杆重新生效并重画滑杆。<br> 除了图3-5是为图3-4所示的事件序列定制的以外,图3-5与图3-3类似。当JSlider.setPaintTicks()被激活后,这个滑杆更新UI代表的一个监听器。这个监听器便修改这个UI代表,这个UI代表获得这个组件本身的信息。<br> 图3-6示出在滑杆的轨道上按下鼠标后发生的事件序列。鼠标按下事件派发给滑杆,滑杆响应该事件,把一个事件发送给它的监听器。它的监听器之一是BasicSliderUI的TrackListener的一个实例。<br> 这个监听器通过调用BasicSliderUI.scrollDueToClickInTrack()来操纵滑杆的UI代表。这个UI代表然后调用JSlider.setValue()来设置滑杆的值。JSlider.setValue()方法是DefaultBounderRangeModel.setValue()的参数传递方法。参见3.2.1节“Swing组件”中对模型属性的组件传递方法的讨论。<br> 当这个模型值设置后,模型激发一个状态已变化事件,该事件由一个BasicSliderUI.ChangeHandler实例处理。变化监听器通过算滑杆的滑块位置,然后重画滑杆来处理这个事件。<br> 图3-7示出了图3-6中说明的事件序列的组件通信。滑杆激发一个由轨道监听器处理的事件。该监听器修改UI代表(它更新滑杆),然后滑杆又更新模型。模型激发一个状态变化事件,这个事件由一个变化监听器通过重画组件来处理。 </p> <p><b><a name="3.2.4"></a>3.2.4 模型</b></p> <p> 大多数轻量Swing组件都有这样一个模型,这个模型维护状态信息,并在信息变化时激发事件(有些组件(例如,JSeparater)没有模型),一个按钮的模型跟踪按钮的助记键及按钮是否待按下、按下或选取。按钮模型在它们的模型改变时将激发变化事件,当模型的选取状态变化时将激发项事件。<br> Swing提示<br> 谁在监听?<br> Swing组件由许多对象组成,例如,一个滑杆至少由九个对象组成,参见图3-2“滑杆组件类”。通过记住谁在监听谁有助于跟踪对象在做什么和了解对象之间是怎么交互的。<br> 组件监听其模型,其主要目的是把事件传送给已向组件登记过的监听器。组件还为模型属性提供传递方法。传送模型事件和提供模型属性的传递方法减少了直接访问组件模型的需要。<br> UI代表监听器主要监听组件,有时也直接监听组件的模型。UI代表对组件和模型变化的响应通常是更新它们的外观,这通常要访问组件或模型,以便获得有关变化的更多信息。 <br> 模型是当作JavaBeans的关联属性来实现的。如果修改一个属性导致激发一个属性变化事件,则这个属性就是关联的,并且这个属性的访问方法遵循如下名字的约定:<br> public void setModel(<<i>ModelInterface</i>>model)<br> public <<i>ModelIntervace</i>>getModel()<br> < ModelInterface >表示定义模型类型的接口名。<br> 1.模型事件<br> 模型具有激发大量事件的潜能。例如,当一个滑杆的滑块拖动时,该滑杆的模型激发一个连续不断的事件流,指示这个滑杆的值正在改变。因此,从性能方面来看,模型为每个激发的事件创建一个事件不总是实现的。<br> 为了大大减少由一个模型创建的事件对象的数量,模型激发一个由javax.swing.event.ChangeEvent类定义的特殊事件类型。变化事件与大多数其他事件不同,因为它们仅包含事件源这一种信息。这样,每个模型可以对所有的变化通知重复使用一个变化事件。<br> 激发变化事件称为轻量通知,因为很少的信息与事件有关。激发其他的事件(例如,由按钮激发的动作事件)称为状态通知,因为该事件除包含事件之外还包含许多状态信息。<br> 轻量通知用于要经常修改的模型属性。监听轻量通知的监听器(指实现ChangeListener接口的监听器)询问从变化事件获得的事件源,以了解与变化有关的更多信息。<br> 对很少变化的模型属性则使用状态通知。例如,从一个列表模型中删除一个元素产生一个状态通知,该通知包括删除行的索引值。<br> 2.Swing模型<br> 表3-1列出了Swing模型接口以及与模型有关的组件。该表还指出了模型是否提供了轻量或状态通知以及Swing是否提供了模型接口的一个抽象实现。</p> <p> <b>表3-1 Swing模型</b><br> <font face="Fixedsys, 宋体"> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<br> 模型接口 使用者 通知<sup>②</sup> 抽象类 <br> ─────────────────────────────────<br> BoundedRangeModel JProgressBar、JSlider LW<br> ButtonModel JButton、JCheckBox、 LW/ST<br> JCheckBoxMenuItem、<br> JMenu、JMenuItem、<br> JRadioButton、<br> JRadioButtonMenuItem<br> JToggleButton<br> ComboBoxModel JComboBox ST<br> Document1<sup>①</sup> JEditorPane、JPasswordField、<br> JTextArea ST<br> JTextField、JTextPane<br> ListModel JList ST<br> ListSelectionModel JList、JTable ST<br> SingleSelectionModel JMenuBar、JPopupMenu、<br> JTabbedPane LW<br> TableModel JTable ST<br> TableColumnModel JTable ST<br> TreeModel JTree ST<br> TreeSelectionModel JTree ST<br> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<br> ①Document接口在javax.swing.text包中<br> ②LW=轻量通知 ST=状态通知</font></p> <p> 所有的Swing模型都由javax.swing包中的接口定义,并且所有的Swing模型都有一个缺省的实现。例如,ButtonModel接口由DefaultButtonModel类来实现,ListModel由DefaultListModel来实现,如此类推。在没有为组件显式地设置模型时,就使用缺省实现。<br> 有些模型如ListModel、TableModel和文本包中的Document接口,提供了抽象实现供开发人员去扩展。抽象的模型实现至少为监听器和事件激发方法提供了登记方法,这使得它们具有子类化的价值。提供抽象实现的模型是更复杂的Swing模型。<br> 3.一个模型的多个说明<br> MVC体系结构的优点之一是可以把多个视图附加在单个模型上。而且,因为模型向它们的视图广播变化,所以模型的所有视图很容易保持同步。<br> <a href="s03_t08.htm" target="_blank">图3-8</a>所示的小应用程序含有两个组件:一个滑杆和一个滚动窗格,它们共享DefaultBoundedRangeModel的一个实例。滑杆使用这个模型来定位其滑块的位置,滚动空格则使用这个模型来设置要显示的图形的比例。<br> 本例中的滑杆和滚动窗格(ImageView实例)共享一个DefaultBoundedRangeModel实例。通过调整滑杆来改变模型值会产生一个模型变化通知。滚动窗格响应这个模型通知,它根据这个模型值来调整它所显示的图像的比例。<br> 本例的小应用程序创建模型、滑杆和滚动窗格。把这个模型传送给JSlider和ImageView的构造方法,并且还把一个变化监听器添加到模型中。 <br> public class Test extends JApplet{<br> DefaultBoundedRangeModel model=<br> new DefaultBoundedRangeModel(100,0,0,100);<br> JSlider slider = new JSlider(model);<br> JLabel readOut=new JLabel("100%");<br> //ImageIcon image = new ImageIcon("shortcake.jpg");原文<br> ImageIcon image = new ImageIcon(this.getClass().getResource("shortcake.jpg")); <br> ImageView imageView = new ImageView(image,model);<br> public void init(){<br> Container contentPane =getContentPane();<br> JPanel panel = new JPanel();<br> panel.add(new JLabel(" Set Image Size:"));<br> panel.add(slider);<br> panel.add(readOut);<br> contentPane.add(panel,BorderLayout.NORTH);<br> contentPane.add(imageView,BorderLayout.CENTER);<br> model.addChangeListener(new ReadOutSynchronizer());<br> }<br> ...<br> 这个小应用程序的变化监听器对模型变化作出反应,它更新显示图像比例的readOut标签。此后还调用了这个标签的revalidate方法,以便这个标签重新布局和重画。有关revalidate方法的更多信息,请参见4.3.5节“Validate、Invalidate和Revalidate方法”。<br> ...<br> class ReadoutSynchronizer implements ChangeListener{<br> public void stateChanged(ChangeEvent e){<br> String s= Integer.toString(model.getValue());<br> readOut.setText(s + "%");<br> readOut.revalidate();<br> }<br> }} </p> <p> 接下来定义一个扩展JScrollPane的ImageView类,这个ImageView类必须以一个图像图标和一个有边界范围的模型为参数来构造。有关图标的更多信息,请参见第5章“边框、图标和动作”。有关滚动窗格的更多信息,则参见13.2节“JScrollPane”。<br> ImageView构造方法把一个变化监听器添加到模型中。这个变化监听器作为ImageVew的一个内部类来实现,它根据模型值创建一个与原图像成比例的实例。接着,把与原图像成比例的实例显示在滚动窗格上。<br> class ImageView extends JScrollPane {<br> ... <br> public ImageView(ImageIcon icon, BoundedRangeModel model) {<br> ...<br> <b>model.addChangeListener(new ModelListener());</b></p> <p> ...<br> }<br> class <b>ModelListener implements ChangeListener</b> {<br> public void stateChanged(ChangeEvent e) {<br> <b>BoundedRangeModel model</b> = <br> <b>(BoundedRangeModel)e.getSource()</b>;</p> <p> if( <b>! model.getValueIsAdjusting()</b>) {<br> int min = <b>model.getMinimum()</b>, <br> max = <b>model.getMaximum()</b>, <br> span = max - min,<br> value = <b>model.getValue()</b>;</p> <p> double multiplier = (double)value / (double)span;</p> <p> multiplier = multiplier == 0.0 ? <br> 0.01 : multiplier;<br> <br> Image scaled = originalImage.getScaledInstance(<br> (int)(originalSize.width * multiplier),<br> (int)(originalSize.height * multiplier),<br> Image.SCALE_FAST);</p> <p> icon.setImage(scaled);<br> ...<br> }<br> }<br> }<br> }<br> 例3-1列出了图3-8所示的小应用程序的完整代码。</p> <p align="center"><b>例3-1 一个带多个视图的模型</b></p> <hr> <p> import javax.swing.*;<br> import javax.swing.event.*;<br> import java.awt.*;<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -