图18.21在Properties栏中编辑属性
在Properties分类下,列出了当前组件可以触发的所有事件,例如动作(Action,即鼠标点击按钮后触发的事件),键盘,鼠标,组件大小改变等等事件。前面介绍修改组件显示文字的时候已经提到过双击可以生成组件默认的事件的处理代码,因此在这里如果我们要给按钮加入一个事件处理的代码,还是很简便的。另一种比较全面的事件生成方法就是在这个列表下,点击方法右侧的输入框,修改事件处理的方法名,然后按下回车键即可生成方法处理的代码,此时编辑器会自动切换到需要修改的事件处理代码处。此过程如图18.22(1)所示。
(1)
(2)
图18.22 定义事件处理方法
36
刘长炯著
另外一种方式,就是在设计器中的组件上点击右键,选择菜单中的Events中的子菜单所对应的事件类型和事件处理方法名,即可生成方法处理代码,如图18.22(2)所示。
注意:由于MyEclipse自身的Bug,导致此处的事件处理自定义按钮也不可用。 此时读者应该完成这一练习,并看到代码定位到下面这个方法中的TODO一行的末尾: private void jButtonCalcActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling }
而快速切换到此事件处理方法的步骤也很简单,最简单的步骤就是在设计器的按钮上双击;而另一方式为重复定义时的动作,在属性列表的Events一栏方法输入框中按下回车键。 那么这段方法中,ActionEvent对象即为传递过来的事件参数,包括了事件源,发生时间等众多信息,我们可以通过判断事件源是否和按钮的变量一致来判断是哪个按钮发生了这个事件(因为一个事件处理方法可以被多个按钮所共用)。例如: if(evt.getSource() == jButtonCalc) // 更多处理 因为在此处仅有一个按钮触发此事件处理方法,因此这个判断是不需要的。不过前面提到的秒表Applet则因为多个按钮公用一个事件处理方法,所以用到了判断,读者可以回头去读那部分的代码。 那么这事件到底是如何连接到按钮上的呢?查看private void initComponents() 方法中的组件初始化代码,可以看到有如下片段:
jButtonCalc.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) {
jButtonCalcActionPerformed(evt); } });
,原来是调用按钮的addActionListener(ActionListener)这个方法,然后传入了一个匿名的接
口ActionListener的实现类,这个实现类的方法代码又转而委托调用方法jButtonCalcActionPerformed(ActionEvent),这样才把jButtonCalcActionPerformed()这个方法和按钮的动作事件联系起来。那么关于这段时间处理的代码,自己写的话,还可以用一个内部类来实现:
class CalcButtonActionListener implements java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButtonCalcActionPerformed(evt); } }
同样,这个内部类,也把自己的处理方法委托到 然后将Applet的初始化方法init()改写为: public void init() { try {
java.awt.EventQueue.invokeAndWait(new Runnable() { public void run() { initComponents(); jButtonCalc.addActionListener(new CalcButtonActionListener());
37
刘长炯著
} }); } catch (Exception ex) {
ex.printStackTrace(); } }
这就是常说的两种事件处理方法:匿名内部类和非匿名内部类。OK,现在让我们进入正题,修改方法jButtonCalcActionPerformed()来添加计算乘积的功能代码,并显示计算结果。代码片段如下所示:
private void jButtonCalcActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling try {
double num1 = Double.parseDouble(jTextFieldNum1.getText()); double num2 = Double.parseDouble(jTextFieldNum2.getText());
javax.swing.JOptionPane.showMessageDialog(this, \计算结果是:\ + num1 * num2);
}catch(Exception ex) {
javax.swing.JOptionPane.showMessageDialog(this, \请输入有效的数字\, \错误\,
javax.swing.JOptionPane.ERROR_MESSAGE);
}
}
JOptionPane是javax.swing包中的一个专门用来显示选项面板(包括各种消息)的类,具体的用法读者可以参考JDK的中文文档,带有详尽的示例代码。在这个处理方法中,我们用Double类来解析文本框中显示的字符串,转换为双精度数字,之后弹出对话框显示计算结果。注意一定要在用户输入的信息不合法时,反馈出错的信息,这是图形界面的一个基本要求,虽然这里做的还不是很完备。最佳做法是指出到底是哪个文本框出错,以及出错的原因是什么。 至此,代码编写和功能基本上都已经开发完毕。在运行之前,我们可以预览程序最终显示的界面。点击设计区上侧的带有眼睛的按钮Preview Design即可看到界面的预览。如图18.23所示。需要注意的是,这个预览窗口:Design Preview仅仅显示外观,并不会将事件处理代码加进去,所以点击计算乘积按钮测试的话,不会得到任何反馈信息。 最后,我们来运行这个例子,选择菜单Run > Run As > 1 Java Applet,即可看到运行的结果,刚开始之时,Applet的窗口有点太小了,我们可以把它的大小拖动一下,显示界面如图18.24所示。 注意:细心的读者会发现这个运行界面和预览的很不一样,不用担心,稍后我们会介绍怎么回事,已经如何让它看起来和预览的界面一样。
38
刘长炯著
图18.23预览设计界面
图18.24 界面运行结果
18.3.3 调整生成代码和换肤
如上一节所述,虽然看上去内容和步骤有点多,然而熟悉之后,的确可以做到1分钟内完成一个原型开发的能力,这也是图形界面设计器以及以前的VB,Delphi等RAD(Rapid Application Development,快速应用开发)开发工具大大流行,因为它降低了程序员开发门槛,提高了开发的效率,让开发人员把注意力集中到业务逻辑上而不是图形界面的运行原理上。当然,由于Java本身的发展侧重点不同,这些Java版本的界面设计器还是不尽人意。读者先看看生成的代码的片段: //GEN-BEGIN:initComponents
// ...... jLabelHint.setText(\f0c\并\点\击\计\算\乘\积\按\钮\查\看 39 刘长炯著 \结\果\。\); ...... }//
//GEN-END:initComponents //GEN-BEGIN:variables
// Variables declaration - do not modify private javax.swing.JButton jButtonCalc; private javax.swing.JLabel jLabelHint;
private javax.swing.JTextField jTextFieldNum1; private javax.swing.JTextField jTextFieldNum2;
// End of variables declaration//GEN-END:variables
位于代码注释//GEN-BEGIN:和//GEN-END:中间的部分,就是界面设计器自动生成的代码,开发人员不能修改这些代码,因为每次设计器中的界面重新修改后,这些代码都会被自动重新生成,所以改了也是做无用功。如果需要加入自己的代码,建议放在调用initComponents()方法之后进行,通常对于Applet来说这个调用代码放在init()中,而普通窗体放在构造器中。然后我们再注意第二个重大的问题:中文的标签或者文字(Label或者Text)属性,在initComponents()方法中变成了jLabelHint.setText(\这是因为早期的Netbeans对中文的处理不好,而采用了Java中的Unicode转码方式来表示。虽然在最新的Netbeans 6.1中这个BUG已经修正了,但是显然MyEclipse的这个插件的更新速度没有那么快,很可能是基于Netbeans 5.5中的界面设计器代码移植的,因此这个历史遗留问题也给阅读代码带来了问题,汉字的标签只能通过在设计器中打开时才能看到是什么意思了。 最后就是换肤的问题,前面大家已经看到Applet显示的样子有点古怪,完全和操作系统的不一样。首先AWT中的组件都是简化版的,和操作系统提供的组件显示的外观类似,但是一般都不能带图标,也不能带助记符,所以现在一般很少用到。而Swing中的组件绝大部分都是自行使用画图方法产生的,例如Graphics对象和它的升级版:Graphics2D(这个图形对象封装了很多图形运算功能),因此,引入了一个很重要的概念就是Look and Feel:外观和感觉,常简称为L&F。也就是说,一个JButton按钮只向携带了状态信息和事件处理器,而如何绘制按钮是由一个单独的软件包LookAndFeel来完成的,类似于常见的Winamp等软件的皮肤(Skin)功能,而且还有很多公司提供了专业漂亮的皮肤软件包。默认情况下,Java带了一个纯软件的跨平台的外观,基本上就和图18.24上显示的差不多,这个外观,或者说皮肤,可以保证Java的界面在各个操作系统下都显示的一致。但是,为了最大限度的让用户习惯和操作系统一致的界面,Swing又提供了主流系统下的模拟皮肤包,例如Windows XP下可以仿真Windows经典界面和Windows XP主题界面,在Linux下提供GTK界面,在苹果Mac系统下提供Aqua界面,所需的只是加入一两句更改皮肤的代码即可。OK,就以上面的Applet为例,分成两种情况: 1. 整个界面尚未初始化时修改外观和感觉:
javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
UIManager类的setLookAndFeel(String 外观类名)方法,可以更改显示的外观,不过此方法调用时可能会抛出异常。而UIManager为了方便程序员,还提供了这个getSystemLookAndFeelClassName()方法,来返回当前操作系统的外观名字,例如Windows XP下返回值为:com.sun.java.swing.plaf.windows.WindowsLookAndFeel。
40
刘长炯著