2. 如果界面已经初始化, 典型情况如在构造器中,或者是更换界面的菜单项,那么除了
上述的调用来修改默认外观外,还需要更新所有组件的显示,一般是更新最顶层的,例如JFrame,JApplet,JDialog等。这样的代码片段如下: javax.swing.SwingUtilities.updateComponentTreeUI(this);
3. 最后,因为并非每个界面都能正常显示,所以务必要在发现应用新主题失败时,恢复到
系统默认的跨平台外观上,否则最后的界面将会完全无法显示,这也是一个大忌,估计用户遇到这种情况一次都会对你做的软件从心里面打个大红的X。
最后,汇总这些步骤,那么更改一个软件外观的完整代码,我们顺便把它放到前面提到的MatisseJApplet的例子中,来解决显示出现的问题,在例子代码中添加一个构造器: public MatisseJApplet() { try {
javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
javax.swing.SwingUtilities.updateComponentTreeUI(this);
System.out.println(javax.swing.UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
// TODO Auto-generated catch block e.printStackTrace(); try {
javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getCrossPlatformLookAndFeelClassName());
javax.swing.SwingUtilities.updateComponentTreeUI(this); } catch (Exception e1) {
// TODO Auto-generated catch block e1.printStackTrace(); } } }
此时再来运行MatisseJApplet,即可得到和图18.23相同的显示结果了。
18.3.4 开发UDP局域网聊天桌面应用
前面的例子,都是开发的Applet,需要放在浏览器中才能运行,然而实际项目中经常会用到的是独立运行的桌面应用。那么接下来,我们来开发一个简单的局域网UDP多播聊天的小应用,并提供换肤的功能。 在进行这个小项目的开发之前,有必要先介绍一点UDP多播编程方面的知识。大家可以打开Javadoc中文文档,查看类MulticastSocket的文档介绍,有比较详细的示例代码,如下所示(摘录,有改动):
多播数据报套接字类用于发送和接收 IP 多播包。MulticastSocket 是一种 (UDP)
41
刘长炯著
DatagramSocket,它具有加入 Internet 上其他多播主机的“组”的附加功能。
多播组通过 D 类 IP 地址和标准 UDP 端口号指定。D 类 IP 地址在 224.0.0.0 和 239.255.255.255 的范围内(包括两者)。地址 224.0.0.0 被保留,不应使用。
可以通过首先使用所需端口创建 MulticastSocket,然后调用 joinGroup(InetAddress groupAddr) 方法来加入多播组:
// 加入一个多播组并发出打招呼的信息 ...
String msg = \你好!\
InetAddress group = InetAddress.getByName(\ MulticastSocket s = new MulticastSocket(6789);//端口 s.joinGroup(group);
DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), group, 6789); s.send(hi);//发送
// 获取组的响应信息!
byte[] buf = new byte[1000];
DatagramPacket recv = new DatagramPacket(buf, buf.length); s.receive(recv);//接收 ... // OK, 聊天结束,离开组... s.leaveGroup(group);
将消息发送到多播组时,该主机和端口的所有预定接收者都将接收到消息(在数据包的生存时间范围内,请参阅下文)。套接字不必成为多播组的成员即可向其发送消息。
当套接字预定多播组/端口时,它将接收由该组/端口的其他主机发送的数据报,像该组和端口的所有其他成员一样。套接字通过 leaveGroup(InetAddress addr) 方法放弃组中的成员资格。多个 MulticastSocket 可以同时预定多播组和端口,并且都会接收到组数据报。
同时,不允许 applet 使用多播套接字。这主要是为了安全性着想。 那么,开发聊天的应用,一般来说至少额外需要一个线程,就是接收服务器发来的消息并进行显示,核心代码就如上文档中的代码示例中段所示。这个线程如下所示: class ReaderThread extends Thread {
public void run() { while(socket != null) { try { byte[] buf = new byte[1024]; DatagramPacket recv = new DatagramPacket(buf, buf.length); socket.receive(recv); textArea.append(new String(recv.getData())); }catch(Exception ex) { } } }
42
刘长炯著
} 有了这些基础知识,我们就可以考虑界面的设置了,原型的界面设计如图18.25所示,该设计图使用Microsoft Visio软件绘制。其中文件菜单下,包括:进入Ctrl + E,离开Ctrl +
;操作菜单下包括清D,分隔符,退出Ctrl+Q这四个菜单项(注意后面的是快捷键组合)
;帮空记录和更改界面(下面包含三个单选钮子菜单:Office2003;系统外观;默认外观)
助菜单下包括:帮助内容和关于。除了帮助菜单项和更改界面菜单项及其子菜单外,其它菜单项都有对应的图标,参见图18.26。
图18.25 聊天界面原型设计
界面的美化方面,首先使用了开源的OfficeLnFs(见参考资料链接),这个外观,同时提供了三个风格的界面:Office 2003,Office XP,Visual Studio 2005。下载后得到的类库文件名是OfficeLnFs_2.7.jar。下面是官方网站使用这个外观的示例代码:
try {
UIManager.setLookAndFeel(\);
//UIManager.setLookAndFeel(\.OfficeXPLookAndFeel\
//UIManager.setLookAndFeel(\eel\
} catch (Exception e) {
System.err.println(\went wrong!\); }
其次我们收集了一些图标,如图18.26所示。从上到下依次对应几个功能:清空历史,连接,断开,退出,窗体图标,以及发送。这些图标存放在src/icons/目录下。
43
刘长炯著
图18.26 界面用到的图标
现在预备知识已经介绍的差不多了,可以开始开发了。我们开发的过程是,先把界面设置好,最后加入事件和其它功能代码。新建一个Java项目,名为UDPChat,透视图当然还是切换到MyEclipse Swing/Matisse。随后启动新建 Matisse Form向导,并在图18.14的窗体列表中,选中JFrame,并将类名定为UDPChatFrame。然后把下载的文件OfficeLnFs_2.7.jar复制到项目的lib目录下,并加入到项目的Build Path中。随后切换到窗体设计器UDPChatFrame.form,点击Outline视图中的[JFrame],并在Properties视图中修改以下属性为目标值:defaultCloseOperation:DO_NOTHING;title:多点传送数据IP地址范围: 224.0.0.1 ~ 239.255.255.255。这两个属性分别设置了窗口的关报聊天程序 -
闭操作――关闭时不采取任何动作,以及窗口的标题。然后常见的窗口,都有一个图标,位于最左上角,例如MyEclipse的那个像8一样的图标,我们这个窗口也要设置,不过很遗憾只能通过代码设置,使用setIconImage(Image)方法来设置,如下粗体所示在构造器里加入下面的代码: /** Creates new form UDPChatFrame */ public UDPChatFrame() { initComponents();
setIconImage(new javax.swing.ImageIcon(getClass().getResource( \ }
ImageIcon这个类是专门用来加载图标的,读者可以查看中文JavaDoc文档来了解更多说明。例如要给一个按钮设置显示的图标,可以用这样的代码片段:jButton1.setIcon(new javax.swing.ImageIcon(getClass().getResource(\ 完成了窗口的显示设置后,开始搭建如图18.25所示除菜单外的主要界面。点击Matisse Palette视图中的SwingControls目录下的控件Label,Button,Text Field这几个组件,即可创建除中央文本显示区之外的界面。而由于Swing组件默认情况下不会滚动,因此我们要在SwingContainers下面,先选中一个Scroll Pane放入设计器正中,提供滚动功能,把它的大小条件成占用面积最大;然后再从SwingControls下选中控件Text Area,将它放置到刚才的滚动面板的中央。在这些组件中,发送按钮的属性的编辑有点麻烦,因为需要设置按钮显示的图标和助记符。设置icon的对话框如图18.27所示,关键点在于点击icon属性右侧的按钮来打开图标浏览对话框,接着选中单选钮Image Within Project,在Package右侧选中包含图片的包(既可以是源代码目录下的,也可以是JAR文件中的),然后从File右侧的下拉框中选中需要使用的图片,同时下侧会显示图片的预览,随后点击OK按钮关闭对话框即可完成图片的选择。图18.27中同时也展示了属性mnemonic(助记符)和text的取值设置,分别为S和发送(S)。随后需要修改各个组件的变量名,使之变得更可读一些。这样界面完成时的设计如图18.28所示。 随后我们来搭建菜单。搭建菜单的第一步,是点击Matisse Palette视图中的位于SwingMenus目录下的
44
MenuBar,将其放到界面的最顶部。此时可以看到默认带了两个
刘长炯著
菜单项:File和Edit,很显然我们要对此做修改,将其标签(text属性)分别修改为文件(F)和操作(O),助记符分别设置为F和O。随后点击Menu项,将其放置到菜单条的最右侧,然后修改标签和助记符,生成菜单帮助(H)。随后点击Menu Item,将其放置到菜单上,会自动添加为子菜单项,然后需要设置icon和text属性,而快捷键则是通过点击accelerator属性右侧的按钮来打开快捷键对话框,参考图18.29。在图中的Key Stroke右侧输入框,按下您希望的快捷键组合,可以是单个键,例如F1,也可以是多个键的组合(例如先按下Ctrl键,然后不松开键盘后再按下E键),最后点击OK按钮关闭对话框,即可完成快捷键的设置。要给菜单栏添加分隔符,点击图标Separator即可添加。使用类似的过程搭建其它菜单。有一个地方需要特别注意,那就是更改界面添加的时候要用菜单(Menu),不能用菜单项(MenuItem),其下属的三个子菜单项需要使用Menu Item / Radio Button来搭建。菜单创建时,不要忘了修改变量名为有意义的取值。
图18.27设置图标属性对话框
45
刘长炯著