上一篇博客介绍了NetBeans Java代码编辑器,这一篇将介绍用NetBeans 开发基于Swing Application Framework (JSR 296) 的程序,Swing一直以来是饱受争议的GUI库,特别是SWT与Swing的争论,分别体现在了Eclipse与NetBeans的争论上。
在此,我无心讨论SWT与Swing孰优孰劣,你要是真的对这个感兴趣,可以到williamchen的博客里的Swing专栏看看,该专栏博主已经对SWT和Swing分析得非常透彻:
http://blog.sina.com.cn/swingjava
Swing在1998年末作为Java 2的一部分发布了1.0,但是当时发布的版本不管是性能还是外观都足以令人失望。虽然当时Swing小组的工程师使用了最前沿的技术,如设计模式,但是时的Swing小组已经被性能、本地化外观一致性问题以及许多错误深深陷住了,这些问题几乎把整个项目拖垮。
幸亏,Swing小组坚持了下来,要不然今天我们就不会看到像NetBeans这样的IDE了。自从JDK1.5后,Java的性能已经有了巨大的改观,现在1.6下,Swing的性能已经完全不是问题。
那么Swing的优势有那些呢?
l 优秀的设计,正如刚才所说的,Swing从九十年代中期开始设计就用了当时最前沿的开发方法学。可以说Swing在当今各种GUI库中,设计是非常优雅的。
l 轻量级
l 官方的存在JRE中,发布程序不需要像SWT那样还要捆绑自己的GUI库
l 真正的平台独立,Swing组件由Java 2D绘制
l 轻易的更改面观模式
l 强大的IDE支持,现在用NetBeans做界面,几乎不用你敲一行代码。
l Swing现在又多了一个杀手级框架Swing Application Framework (JSR 296)
下面开始我的第二篇Java博客
NetBeans 6.0 提高生产力之Swing Application Framework (JSR 296)
Swing Application Framework简介
Swing Application Framework(以下简称SAF)致力于简化Swing应用程序的开发,框架定义了对大多数桌面应用程序的共有的基础设施:
l 应用程序生命周期管理,特别是GUI的启动和关闭。
l 对资源管理和载入的支持,这些资源是字符串,格式化的消息,图像,颜色,字体以及桌面应用程序共有的其他类型的资源。
l 对动作定义,管理和绑定的支持,其中包含了异步运行的动作(在后台运行)。
l 持久化会话状态:支持自动地,有选择地保存应用程序一次运行到下次运行的GUI状态。比如顶层的窗口几何位置。
用NetBeans开发SAF
SAF已经定义了大多数桌面应用程序的基础设施,我们可以更方便的开发Swing程序了,但是手动编码开发GUI还是让很多人抓狂。能不能提供一个像VB一样的环境,让程序员更加专注于功能的实现而不是界面的实现?答案是肯定的,NetBeans 6.0就为开发SAF提供了这样一个环境,甚至比VB还要做得更好。
那么到底NetBeans6.0为SAF提供了什么样的优势呢?
l 生成基础框架,不必每次新创建一个程序的时候都从头开始
l 唾手可得的国际化支持,在Netbeans里对SAF程序国际化,简单得不能再简单
l 拥有Swing世界里的明星级UI设计工具模块Matisse提供的免费大餐
l 用NetBeans开发SAF,意味着你还可以使用Beans Binding(JSR 295),数据绑定将问题变得更简单
开始之前
用NetBeans 6.0开发基于SAF,其实不用明白SAF,主要遵循一定的开发原则也可以开发出稳定高效的SAF程序来
但是为了你更深入的理解SAF,强烈建议看下面的文章
l Using the Swing Application Framework (JSR 296)
http://java.sun.com/developer/technicalArticles/javase/swingappfr/
l Swing应用程序框架(Swing Application Framework)API绪论(JSR-296)之一
http://www.javaeye.com/topic/81326
Swing应用程序框架(Swing Application Framework)API绪论(JSR-296)之二
http://www.javaeye.com/topic/81327
l 如果你有更多的时间,那么请看下面网址有关Swing的部分
http://blog.sina.com.cn/swingjava
开发环境准备
JDK1.6或以上版本
NetBeans6.0或以上版本
提示:Netbeans 网站上提供有六种捆绑包下载
l Web & Java EE
l Mobility
l Java SE
l Ruby
l C/C++
l All
开发Swing Application Framework只需要Java SE下载包就行了
这样能真正发挥NetBeans的性能!
创建项目
我们新建一个项目,从Java种类里选择 Java Desktop Application,如下图
点击下一步,接着输入项目名称、项目存放地址和程序的主类
在Choose Application Shell里面我们选择Basic Application,单击完成
提示:我们如果创建基于Swing的数据库程序,那么选择BataBase Application将提供极大的便利。但是在这里我只是介绍Basic Application,也就是现在我们用SAF开发普通的桌面程序。
生成的项目目录结构
l META-INF/services目录
该目录一般存在一个JAR包里面
META-INF/services目录涉及一个模式:服务提供者模式
引入该模式一般是为了程序松散耦合,而且是IoC(控制反转)的另一种实现方式
服务提供者模式一般有两个角色
² 服务 :一般为一个抽象类
² 服务提供者 :抽象类的实现
存在META-INF/services目录的JAR包一般为一个“服务提供者”
用NetBeans开发中小型程序不需要服务提供者概念,如果你要深入理解
可以Baidu或Google一下META-INF/services
l foo包
foo包是我们创建程序的主类foo.FooApp时,NetBeans自动为我们生成的,
我们来看看NetBeans为我们生成了什么基础框架类:
1、FooApp.java
public class FooApp extends SingleFrameApplication {
/** *//**
* 在启动的时候创建和显示程序的主框架
*/
@Override protected void startup() {
show(new FooView(this));
}
/** *//**
*这个方法是用相应的资源注入到特定的Window来初始化程序
*因为我们用NetBeansk开发,所以相应的资源是通过Gui生成器来完成
*也就是不用我们手动编写代码
*所以这个方法不是必须的
*/
@Override protected void configureWindow(java.awt.Window root) {
}
/** *//**
* 一个方便的静态获取器,用来获取FooApp类的实例
*/
public static FooApp getApplication() {
return Application.getInstance(FooApp.class);
}
/** *//**
* 程序的运行入口点
*/
public static void main(String[] args) {
launch(FooApp.class, args);
}
}
FooApp.java里面的代码是NetBean为我们生成的,开发简单的程序这里基本上不需要更改什么。
2、FooAboutBox.java
每个程序都应该有一个关于界面,Netbeans为我们生成了关于的模板
修改相应文字就可以了,当然也可以自己做个About界面
3、FooView.java
FooView.java是程序View部分的关键,由于生成的代码比较多
在解释代码之前,我先看看Design部分
NetBenas给我们生成的框架非常简单明了
需要注意的是图上标识的两个地方
标有1的地区为消息地区,我们的程序完成任务后,在这里提示消息
标有2的地区为任务进度条,程序执行任务时在这里现实任务进度,当然任务有确定模式和不确定模式,不确定模式的进度条持续地显示动画来表示正进行的操作。
接下来我们看看FooView.java的构造器的代码:
public FooView(SingleFrameApplication app) {
super(app);
initComponents();
ResourceMap resourceMap = getResourceMap();
int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");
messageTimer = new Timer(messageTimeout, new ActionListener() {
public void actionPerformed(ActionEvent e) {
statusMessageLabel.setText("");
}
});
messageTimer.setRepeats(false);
int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");
for (int i = 0; i < busyIcons.length; i++) {
busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");
}
busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {
public void actionPerformed(ActionEvent e) {
busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
}
});
idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
statusAnimationLabel.setIcon(idleIcon);
progressBar.setVisible(false);
// connecting action tasks to status bar via TaskMonitor
TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
taskMonitor.addPropertyChangeListener(
new java.beans.PropertyChangeListener() {
public void propertyChange(java.beans.PropertyChangeEvent evt) {
String propertyName = evt.getPropertyName();
if ("started".equals(propertyName)) {
if (!busyIconTimer.isRunning()) {
statusAnimationLabel.setIcon(busyIcons[0]);
busyIconIndex = 0;
busyIconTimer.start();
}
progressBar.setVisible(true);
progressBar.setIndeterminate(true);
} else if ("done".equals(propertyName)) {
busyIconTimer.stop();
statusAnimationLabel.setIcon(idleIcon);
progressBar.setVisible(false);
progressBar.setValue(0);
} else if ("message".equals(propertyName)) {
String text = (String)(evt.getNewValue());
statusMessageLabel.setText((text == null) ? "" : text);
messageTimer.restart();
} else if ("progress".equals(propertyName)) {
int value = (Integer)(evt.getNewValue());
progressBar.setVisible(true);
progressBar.setIndeterminate(false);
progressBar.setValue(value);
}
}
});
}
一大段代码,看起来是不是眼花缭乱啊,其实这么一大断代码是IDE给我们生成的用来初始发刚才所示的两个区域的消息区和进度条区域的。
也就是初始化消息区域和进度条区域,如果不想自定义消息现实和任务进度条的实现方式,上面一大段代码大可不必理会。
我们再来看一段代码:
@Action
public void showAboutBox() {
if (aboutBox == null) {
JFrame mainFrame = FooApp.getApplication().getMainFrame();
aboutBox = new FooAboutBox(mainFrame);
aboutBox.setLocationRelativeTo(mainFrame);
}
FooApp.getApplication().show(aboutBox);
}
看到方法名我们就知道,这个是用来实现关于界面的
我们运行一下程序,在点击菜单栏里面的 Help > About..
结果,关于界面跳了出来。
看到这里你也许会惊讶,因为按照传统的方法,相应的菜单应该有相应的ActionListener事情监听器才能发生事件啊!
别着急,你看showAboutBox()方法前不是有一个注解: @Action
这个注解涉及到SAF对动作的定义,我们后面将详细讲解
l foo.resources、foo.resources.busyicons包
foo.resources、foo.resources.busyicons包是用来存放程序的图像、文字等资源的地方
后面讲到程序的国际化的时候再作介绍。
忘掉ActionListener事件监听器,拥抱@Action
上面我们已经提到,没有相应的ActionListener事件监听器,点击菜单栏里面的 Help > About..也照样能触发事件,这是怎么回事?
原来是SAF框架对有@Action注释的方法进行了管理。
既然用原来的ActionListener已经处理得非常好,为什么SAF专家组的人还弄个@Action,这不是没事找事干吗?呵呵,下面我们通过几个例子来说明引入@Action的好处
l 制作环境相关的按钮
我们经常遇到这样的按钮或菜单,一般情况下它是不可用的,如上图画圈的三个按钮,要等一定的条件他们自动启用。
我举例用@Action做这样的按钮看看,不用担心,不必写多少代码,IDE帮我们完成了大部分的工作
我们要做的功能如下
点击选择CheckBox选项后,按钮自动开启,取消选中后按钮变灰色,不可用。
在右边的Palette属性面板中拖出我们需要的两个组件到程序主界面,并修改Text文字
右键点击按钮,选择 Set Action…
在跳出来对话框中,在Action 选择框中选中Create New Action
接下填写按钮的相关信息,这里我做一下简单的说明
Action’s Class :动作方法存放在那个类中
Action’s Method:动作方法的名称,比如上面提到的跳出关于对话框的方法showAboutBox()
Background Task选项:这个暂时不理它,后面将讲到
Attributes :1、Basic标签 设置按钮的显示文字、快捷键、按钮图像。
2、Advanced标签可要注意了,因为这个和我们现在要做的例子关系非常大,我们在Advanced标签里的Enabled Preproty 写上 hasSelect(名字可以自己写),如下面第二个图
单击OK,马上看到IDE为我们生成的代码
这样我们就可以通过setHasSelect()方法来设置按钮的可用性,setHasSelect(ture) 时按钮开启,setHasSelect(false)时按钮不可用。
接下来选择CheckBox组件,单击右键,再选择下图所示
出来代码中写上一行代码:
setHasSelect( ((JCheckBox)evt.getSource()).isSelected());
最后是这样子的
private void jCheckBox1ItemStateChanged(java.awt.event.ItemEvent evt) {
setHasSelect( ((JCheckBox)evt.getSource()).isSelected());
}
自此环境相关的按钮就做完了,我们可以运行代码试试。
从上面的例子中我们看到,做这个例子真正只写了一行代码:
setHasSelect( ((JCheckBox)evt.getSource()).isSelected());
其它都是通过IDE生成的,多亏IDE和SAF的Action我们才能如此高效的制作程序。
l 菜单和按钮关联
菜单和按钮经常表现为同一个行为,我们当然可以各自实现它,但是这样就产生了冗余
编写程序最总要的一点就是不要重复代码。在Design模式下右键单击相应的菜单,在Set Action…操作里面选择相应Action
这样菜单和按钮就关联起来了,该菜单和按钮行为相同,SAF如此简单的为我们完成了任务。
这又是Action解决了一大问题
l Background Task
刚才在制作环境相关的按钮时,我们忽略一个选项
现在我们来介绍这个非常重要的特性
以前人们不断抱怨Swing是如何慢,其实JRE1.5后,特别是1.6后Swing性能已经完全不是问题,问题是开发Swing的程序员没有处理好Swing的线程问题,比如本该在后台运行的任务却直接写在Swing中,造成Swing程序相应缓慢。
在以前你要非常熟练Swing和小心才能写出高质量的Swing程序来,但是现在你利用SAF提供的框架支持,处理后台任务变得非常容易。
我们 Set Action 的时候把Background Task选项选中,最后看一下IDE给我们生成了什么代码:
原来的代码:
@Action(enabledProperty = "hasSelect")
public Task actionMethodName() {
}
Background Task选项选中后为如下代码:
@Action(enabledProperty = "hasSelect")
public Task actionMethodName() {
return new ActionMethodNameTask(getApplication());
}
private class ActionMethodNameTask extends
org.jdesktop.application.Task<Object, Void> {
ActionMethodNameTask(org.jdesktop.application.Application app) {
// Runs on the EDT. Copy GUI state that
// doInBackground() depends on from parameters
// to ActionMethodNameTask fields, here.
super(app);
}
@Override protected Object doInBackground() {
// Your Task's code here. This method runs
// on a background thread, so don't reference
// the Swing GUI from here.
return null; // return your result
}
@Override protected void succeeded(Object result) {
// Runs on the EDT. Update the GUI based on
// the result computed by doInBackground().
}
}
可以看到,actionMethodName()方法里运行的代码将在一个新线程中运行
如果你要彻底明白上面的代码和EDT是什么概念,请浏览下面的博客地址
http://blog.sina.com.cn/swingjava中有关Swing部分。
如果您懒得浏览,那么请遵循下面的建议
1、在ActionMethodNameTask构造器中
代码运行在EDT(事件分派进程)中,在这里复制doInBackground() 方法中所需要的GUI 的状态或参数到ActionMethodNameTask的成员变量中。
2、doInBackground()方法中,不要存放处理GUI界面的代码,该方法返回的值将自动传到后面succeeded(Object Result)中
3、succeeded()运行在EDT中,任务成功的执行后才执行该方法,该方法主要收集doInBackground()方法运行后的结果以用来更新GUI程序。
我们做一下试验,为ActionMethodNameTask增加一些代码:
private class ActionMethodNameTask extends
org.jdesktop.application.Task<Object, Void> {
ActionMethodNameTask(org.jdesktop.application.Application app) {
super(app);
}
@Override protected Object doInBackground() {
for(int i=0;i<1000000;i++){
System.out.println(i);
}
return null;
}
@Override protected void succeeded(Object result) {
setMessage("运行成功");
}
}
运行程序后点击按钮
任务开始:
任务结束:
注意提示信息只显示一定时间,这个时间可以自己设定,一定时间后信息自动消失。
l 制作阻塞按钮
我经常要做具有阻塞功能的按钮,比如我们按一下按钮,就跳出如上对话框,程序在后台运行任务,但是我们不能进行其它操作,除非我们点击取消按钮。
制作这样的按钮,其实非常简单:
在Set Action...进行如下设置就可以了
Blocking Type 可以选择NONE,ACTION,COMPONENT,WINDOW,APPLICATION指定阻塞的范围空间,你可以自己尝试一下,效果是怎样的。
从上面几个小例子可以看到引入@Action巨大好处了吧。
国际化支持
正如开始所说的,SAF本身就很好的支持了国际化,再加上NetBeans辅助,国际化简直易如反掌。
我们知道一些企业框架比如Structs、JSF是通过后缀名为properties的文件保存相关资源的。
SAF也是,我们看看IDE为我们生成的properties文件
从上图可以看出,
FooApp.java
FooAboutBox.java
FooView.java
这三个类在foo.resources包分别对应有后缀名为properties的文件
FooApp. properties
FooAboutBox. properties
FooView. Properties
也就是说,类里面用到文字资源,图像资源集中放到了一起。这样国际化就相当简单了
这时也许你会说,一开始就写properties的文件,岂不是很麻烦?
别忘了,我们NetBeans的支持,做这个工作几乎不费你任何力气
在可视化编辑页面,我直接修改相应文字,IDE会自动为我们把相应的文字保存到对应的properties的文件中。
根本不用自己手动敲写下面的文字
jCheckBox1.text=选中按钮开启
尾声
关于用NetBeans开发Swing Application Framework程序其实还有很多我没有说清楚,有些也很难说清楚,关键还是要自己亲身试验一下,正如大牛们所说的“理论看不懂就去实践,实践不懂就去看理论”。
本文仅起到入门介绍的作用。开发需要的SAF JavaDoc文档可以下面所示查看:
为了你更好的理解,我用SAF做一个端口扫描程序,这个程序很不完善,仅做演示:
点击这里下载源代码:
PortScan.rar
(提示:用NetBeans6.X打开项目目录即可)
用SAF做的更完整的程序:
l mp3在线搜索工具
你可以到作者博客查看更详细信息:
http://www.blogjava.net/huliqing/archive/2008/03/26/188817.html
l 单词Mp3随身宝DIY
本人的英语成绩一直不是很好,特别是词汇量很低,很多单词不会发音。
所以我就有个想法,能不能把自己想要背记的单词做成MP3的形式,单词和单词的解释放到对应.lrc文件中,随意挑选单词,
制作成带同步歌词(LRC文件)的MP3文件,这样在MP3机上就能边听单词边同步看单词及其解释,单词先后次序、间隔多长,
可以相应自由地调整,让我们的MP3机变成英语学习机!我通过网络寻找这样的软件。
但是找了好久就是找不到合适的软件,只是有一个《我爱背单词》的软件有类似这样的一个功能,但是该软件是闭源收费软件,
免费版的语音是合成的,效果非常差。求人不如求自己,于是产生制作这个程序的想法。
这个程序原本就是想开源的,但是我拿去参加学校的电脑作品赛了,比赛的作品先前不能发布。所以暂时不发布程序和源代码,等比赛结束再上传程序和源代码。这个可能要一段时间,请大家耐心等待消息。
l 用NetBeans开发但是没有用SAF的YOYOPlayerMp3播放器
详细信息请看作者的博客:
http://www.blogjava.net/hadeslee/archive/2007/12/31/171678.html
通过上面几个程序可以体现出NetBeans的强大特性来(不仅仅是做桌面程序哦)!SAF框架加上NetBeans,我们可以专注程序的功能而不是程序的基础实施上,而且即使是初学者也可以编写出高性能的Swing程序来。我相信随着越来越多的人了解SAF和NetBeans,后面会不断涌现出新的Swing桌面程序来,让我们拭目以待吧!
由于水平有限,文中有些术语解释可能不准确,欢迎批评指正!(这也是我写博客的原因,通过他人认识自己的不足)
版权声明:
本文由令狐虫原创,欢迎转载,转载请保留博客地址:
http://www.blogjava.net/linghuchong/