Java实用经验总结.docx
- 文档编号:14127008
- 上传时间:2023-06-20
- 格式:DOCX
- 页数:21
- 大小:411.66KB
Java实用经验总结.docx
《Java实用经验总结.docx》由会员分享,可在线阅读,更多相关《Java实用经验总结.docx(21页珍藏版)》请在冰点文库上搜索。
Java实用经验总结
1改变Swing应用程序的默认字体/字号
经常使用Swing作为程序UI的人可能会注意到,Swing组件默认显示文字的字号为11。
这对于英文显示毫无问题,但是如果用这个字号显示中文的话,这么小的字号就会使程序变得很难看。
我当年在用IReport0.56的时候就发现他的菜单栏和弹出的Dialog里的字很难看,但是将字号调大之后就好多了。
虽然在最近版本的JDK里似乎修正了这个字体问题,但是如果你的程序必须使用以前版本的JDK的话,这个问题就需要处理一下,下面就是一个不错的解决方案:
FontvFont=newFont("Dialog",Font.PLAIN,13);
UIManager.put("ToolTip.font",vFont);
UIManager.put("Table.font",vFont);
UIManager.put("TableHeader.font",vFont);
UIManager.put("TextField.font",vFont);
UIManager.put("ComboBox.font",vFont);
UIManager.put("TextField.font",vFont);
UIManager.put("PasswordField.font",vFont);
UIManager.put("TextArea.font",vFont);
UIManager.put("TextPane.font",vFont);
UIManager.put("EditorPane.font",vFont);
UIManager.put("FormattedTextField.font",vFont);
UIManager.put("Button.font",vFont);
UIManager.put("CheckBox.font",vFont);
UIManager.put("RadioButton.font",vFont);
UIManager.put("ToggleButton.font",vFont);
UIManager.put("ProgressBar.font",vFont);
UIManager.put("DesktopIcon.font",vFont);
UIManager.put("TitledBorder.font",vFont);
UIManager.put("Label.font",vFont);
UIManager.put("List.font",vFont);
UIManager.put("TabbedPane.font",vFont);
UIManager.put("MenuBar.font",vFont);
UIManager.put("Menu.font",vFont);
UIManager.put("MenuItem.font",vFont);
UIManager.put("PopupMenu.font",vFont);
UIManager.put("CheckBoxMenuItem.font",vFont);
UIManager.put("RadioButtonMenuItem.font",vFont);
UIManager.put("Spinner.font",vFont);
UIManager.put("Tree.font",vFont);
UIManager.put("ToolBar.font",vFont);
UIManager.put("OptionPane.messageFont",vFont);
UIManager.put("OptionPane.buttonFont",vFont);
这段代码用在程序的开始部分,可以有效地将Swing组件的显示字体设置为我们在vFont所设定的内容。
2自定义JFrame的关闭事件
有的时候,当用户关闭应用程序窗口的时候,我们可能希望程序在结束之前保存一些必要的数据。
对于这种需求,我们有两种备选方案:
2.1获取程序关闭的“钩子”
Runtime.getRuntime().addShutdownHook(shutdownHook);
protectedThreadshutdownHook=newPlatformShutdownHook();
protectedclassPlatformShutdownHookextendsThread{
publicvoidrun()
{
//一些清理工作在这里进行……
}
}
通过这种方法,我们就可以在程序结束时获得通知,以便进行一些保存或清理的工作。
然而这种方法的缺点是,在程序收到结束通知的时候,所有的UI组件已经被销毁了,用户此时看到的是程序已经结束。
而事实上如果程序保存需要花很长的时间的话,用户是不能获取任何信息的,这是一个很糟糕的用户体验。
因为如果这时用户关机的话,程序就有可能丢失尚未保存的信息,而对于这一切,用户并不知情。
2.2处理JFrame关闭事件
为了在UI被销毁之前收到程序结束的消息,我们需要自行处理窗口关闭的事件。
注意在这里我们没有采用addActionListener(……)方法,因为这样做只能让我们在窗口关闭之后收到通知,这样就与上面的方法没什么区别了。
我们需要在JFrame的构造函数中设置:
//设定标志,让MainFrame自己接收窗口事件
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
然后再实现下面的函数:
protectedvoidprocessWindowEvent(finalWindowEventpEvent){
if(pEvent.getID()==WindowEvent.WINDOW_CLOSING){
/**防止用户多次点击“关闭”按钮造成重复保存**/
if(!
isClosing)isClosing=true;
elsereturn;
//处理JFrame关闭事件……
}else{
//忽略其他事件,交给JFrame处理
super.processWindowEvent(pEvent);
}
}
如此一来,我们就可以在窗口被关闭之前通知用户程序正在保存数据的信息,例如后面提到的InfiniteProgressPanel可以显示的内容。
3日期选择组件与JDialog的冲突问题
由于很多应用程序都需要用户输入日期,却又怕用户输入的日期格式错误,所以日期选择组件便应运而生。
虽然我们很需要它,但是网上绝大多数的组件都是需要给钱的。
在找到SwingX之前,我找到的唯一能够免费使用的日历组件就是一个名为DateChooser的JDialog:
看样子很不错,它支持中文,对于今天高亮显示,可以调整年分和月份……一切都非常符合要求。
但是这么好的组件却不能用在我的程序里,原因是在我的程序中,调用这个组件的组件也是一个JDialog,并且设置了setAlwaysOnTop(true)—即总在最前端显示。
由于DateChooser也设定了在最前端显示,这就导致了它和其父组件的显示冲突,最终结果是DateChooser不能正常显示。
对于这个问题,我最终使用SwingX的组件DatePicker来代替DateChooser完成选择日期的使命,惯于DatePicker的使用我将来会在“SwingX使用详解”中提到,这里就不再细说。
但是这个问题仍然值得我们注意,即如果一个窗口组件是设置了总在最前端显示的JDialog,那么就不要以这个JDialog为父组件来弹出其他JDialog,以避免冲突的发生。
5用JEditorPane显示HTML描述的文本
从JDK1.4开始,Swing的很多组件(如JLabel)都可以显示HTML语言写的文本。
这是一个巨大的进步,因为我们可以将所要显示的文字的配置信息如字体,字号,颜色,换行等信息直接以HTML写入到组件的setText()方法当中,不但免去了事后对这些信息进行繁杂配置的烦恼,而且还丰富和简化了所要显示文本的形式。
而JEditorPane则有所不同,它天生就是用来分析并显示格式化文本的,由一些Java写的开源Web浏览器甚至都采用改进后的JEditorPane作为Web页的显示器。
下图就是SwingSet2中的JEditorPane相关的例子。
我们可以看到JEditorPane可以显示大多数的HTML元素,包括图片,格式化文字,URL链接等。
然而通过JEditorPane显示HTML描述的文本有两种方式:
第一种是直接使用JEditorPane.setPage(StringhtmlTxt);来显示用html语言写成的文本。
但是这种方法的缺点是无法显示HTML文本中所描述的对外部资源(如图片,CSS等)的引用。
所以如果要显示更为丰富的信息,仅仅用第一种方法是不够的。
所以第二种方法就呼之欲出:
将用HTML语言描述的动态文本信息写到文件中,使之成为真正的HTML文件,再用JEditorPane.setPage(URL)或JEditorPane.setPage(StringhtmlFilePath),JEditorPane方法读入这个动态生成的内容文件就可以让JEditorPane自动为我们显示丰富的信息了。
StringvNewReportFileName="file:
///c:
/temp.html";
JEditorPanereportPane=newJEditorPane();
Filef=newFile(FileUtil.reportDir,vNewReportFileName);
FileWriterfw=newFileWriter(f,false);
fw.write("");
fw.write("
");…………
fw.write("");
//清理操作
fw.flush();
fw.close();
f=null;
reportPane.setPage(vNewReportFileName);
下图就是我的程序所显示的结果,从图中我们可以清楚地看到由CSS文件定义的表格的Title,这个Title是由一个蓝色的图片作为背景的。
让人遗憾的是用JEditorPane显示的表格的边框都很粗,虽然我已经将了表格的border设置为1,可是JEditorPane依然我行我素。
但是在IE下,表格的边框的表现就要好的多:
2. 横向打印JEditorPane的显示内容
要想打印JEditorPane的内容很简单,只需要调用JEditorPane.print()就可以了。
如果想要调整页眉页脚,边距,打印方向这些细节问题,就需要麻烦一点的步骤。
老实说,用JavaAPI来进行打印工作实在是一件很繁琐的工作,而且一个控制不好就会连累整个输出效果。
真怀念使用JasperReport的日子啊。
不过幸好客户只需要我将程序的打印方向变成“横向打印”,这似乎不太难。
我的解决方法如下:
其中的detailInfoTxt就是一个JEditorPane,具体方法是将PrinterJob中的缺省PageFormat的打印方向属性替换成我们想要的打印方向,然后再利用PrinterJob.setPrintable()进行设置就好了。
值得注意的是JEditorPane本身并未实现Printable接口,所以我们必须通过JEditorPane.getPrintable()方法来获得一个与某个JEditorPane相对应的该接口的实例。
6用InfiniteProgressPanel实现GlassPane
俗话说重要人物都最后出场,作为Swing篇的完结部分,我为大家隆重推荐一个GlassPane的实现—InfiniteProgressPanel,它的效果如图所示:
怎么样,很酷吧?
这是在程序进行更新的时候能够给用户以提示,可以屏蔽用户操作而且十分美观的特殊进度条。
它源于一个超级Java大牛的手笔,此君的《SwingHacker》在去年如带给我的震撼到现在还挥之不去。
从那以后,谁再敢说Java不能做出好看的用户界面之前都需要自己好好掂量一下自己是否有这么说的资格。
这本书让我真正认识到,只有想不到没有做不到。
都是一样用Swing,为啥人家就能玩出花样呢?
差距!
其实现原理很简单,说白了就是用Java2D画圈!
至于源码各位可以到网上自己搜。
他的使用十分简单:
InfiniteProgressPanelglassPane=newInfiniteProgressPanel();
frame.setGlassPane(glassPane)
在需要它显示的时候,就这样做:
ThreadmyThread=newThread(newRunnable(){
publicvoidrun(){
InfiniteProgressPanelgl=thisRef.glassPane;
gl.start();
gl.setText("正在保存数据请稍候....");
try{
//这里放要做的事情……
gl.setText("保存完毕,欢迎使用!
");
Thread.sleep(1000);
}catch(InterruptedExceptionex){
}finally{
gl.stop();
}
}
});
myThread.start();
这里有几个问题需要注意:
1.必须要将InfiniteProgressPanel的显示放到一个线程里,相信大家都知道原因,我不用多说。
2.在InfiniteProgressPanel结束之前的Thread.sleep(1000);是必要的,如果时间设得太短或不设将会导致InfiniteProgressPanel死掉。
至于原因我没有时间深究,各位有兴趣可以自行察看其源码,如果你能找到原因高诉我,我会非常感激。
3.在有些时候会出现圆圈“四处乱窜”的现象,不过不太常见。
1.日期部分
对于像日期、时间和钱这样的对象来说,不同的国家、地区都有不同的显示格式。
即便是同一地区,也可能存在差异。
但是在不考虑国家化,时间格式相对固定的情形下,对于时间的处理还是相对比较简单的。
在我最近所作的一个小程序里面,遇到了一些与日期有关的且不考虑国际化和复杂格式的问题。
例如如何求两个日期所差的天数,所差的月数;将日期类转化为规定格式的字符串,将规定格式的日期字符串转成相应的日期类等等。
下面我就以源码的形式逐一介绍上面提到的问题,需要注意的是这些代码都源于我做的一个名为DateUtil的类,其中独立的变量都是其中的成员变量,函数都是其成员函数:
1.1.成员变量简介:
要想对日期进行格式化首先就需要一个DateFormat类的实例,如果没有特殊需求的话,SimpleDateFormat类就是一个不错的选择,它可以将日期类格式化为在其构造函数中指定的日期格式。
例如,如果我们想要将日期格式化为类似于2007-07-25这样的格式,我们就可以如下定义:
/**以yyyy-MM-dd的形式显示格式 **/
SimpleDateFormatsDateFormat=newSimpleDateFormat("yyyy-MM-dd");
SimpleDateFormatsFullFormat=newSimpleDateFormat("yyyy-MM-ddHH:
mm:
ss");
这里需要注意的是指定的日期格式一定要是”yyyy-MM-dd”,而不能是”YYYY-MM-DD”,否则的就不能正常显示。
对于这个问题我没有深究,如果有对这个问题有研究的朋友欢迎留言。
下面的两个成员变量分别是日期分隔符字符串和字符串分隔器,专门用来解析字符串格式的日期。
/**
*程序中主要的日期分隔符为"-"和"/",且日期序列为“年/月/日”型,其内容缺一不可
*例如:
09/02/02或2009-02-02
**/
publicstaticfinalStringDATE_SEPARATOR="-/";
/**作日期分析之用*/
staticStringTokenizersToken;
1.2.取得两个日期之间所差天数的方法
鉴于java.util.Date类的绝大多数方法都不建议使用了,所以我也就不能够利用Date里面方便的getYear(),getMonth(),getDay()方法来计算日期差了—现在的JRE都可以自动升级,谁知道哪天SUN突然把Date这些API去掉了,那我就欲哭无泪了。
不过话又说回来就算是能够使用这些方法,我们似乎也不太好算,因为每个月的日期数都不一样,如果单纯用两个日期的年月日信息来计算日期差还真是有些麻烦。
基于以上两点原因,我在我的程序里采用了GregorianCalendar做为日期计算的主力军。
这个类有一些很实用的方法,如get(int),这个方法可以获得当前日期类的各项日期指标。
比如我们有一个日期类名为gcDate,要获取它所在的年,月,日,至需要这么做:
gcDate.get(Calendar.YEAR),gcDate.get(Calendar.MONTH)以及gcDate.get(Calendar.DATE)。
不过由于使用这种方法获得的日期和月份都是日期类所指定的年份的,所以如果我们知道两个日期在同一年份的话才能使用gcDate1.get(Calendar.DATE)-gcDate2.get(Calendar.DATE)来获得日期差,否则就不能这么做。
所以如果想要获得不在同一年份的日期差的话就需要用到另一个有用的方法:
GregorianCalendar.add(int,int),这个方法可以让我们在日期类指定的日期指标(如年,月,日等)上加上一个数字,这个数字可以是正数也可以为负数。
其中第一个参数指定所要增加的指标,第二个参数指定增加的数量。
例如我们调用gcDate.add(Calendar.DATE,1)的话,如果gcDate原来代表的时间为2007-07-24,那么调用之后就变成2007-07-25了。
于是我们就可以这样计算日期差:
让日期比较小的那个日期用add函数逐渐“逼近”那个较大的日期,直到两个日期完全相等为止。
计数器中包含的数量即为两个日期的差值。
下面我给出了多个的计算日期差的方法,主要包含两个版本,一个版本参数为格式化字符串,另一个版本参数为GregorianCalendar。
功能包括计算“今天与未来的某一天之间的日期差”和“给定两个日期的日期差”。
主要的计算集中在最后一个daysBetween函数上,该函数接受两个GregorianCalendar类作为参数,并可以计算出两个日期之间的日期差,如果用户给出的较大的日期和较小的日期顺序颠倒的话,该函数会返回一个负数值。
/**
*返回未来的某一天和今天所差的日期数
*注意,这里要clone一个新的日期以免对原始日期类造成的修改。
*而在daysBetween(GregorianCalendarpFormer,GregorianCalendarpLatter)就
*直接处理而不进行clone动作,因为这里已经做了:
)
**/
publicstaticintdaysBetween(GregorianCalendarpFurtherDay){
GregorianCalendarvToday=newGregorianCalendar();
GregorianCalendarvFurtherDay=(GregorianCalendar)pFurtherDay.clone();
returndaysBetween(vToday,vFurtherDay);
}
/**上面函数的String版本 **/
publicstaticintdaysBetween(StringpFurtherDayStr){
GregorianCalendarvFurtherDay=DateUtil.parse2Cal(pFurtherDayStr);
GregorianCalendarvToday=newGregorianCalendar();
returndaysBetween(vToday,vFurtherDay);
}
/**返回较晚的时间(latter)与较早的时间(former)所差的天数**/
publicstaticintdaysBetween(StringpFormerStr,StringpLatterStr){
GregorianCalendarpFormer=DateUtil.parse2Cal(pFormerStr);
GregorianCalendarpLatter=DateUtil.parse2Cal(pLatterStr);
returndaysBetween(pFormer,pLatter);
}
/**返回较晚的时间(latter)与较早的时间(former)所差的天数**/
publicstaticintdaysBetween(GregorianCalendarpFormer,GregorianCalendarpLatter){
GregorianCalendarvFormer=pFormer,vLatter=pLatter;
booleanvPositive=true;
if(pFormer.before(pLatter)){
vFormer=pFormer;
vLatter=pLatter;
}else{
vFormer=pLatter;
vLatter=pFormer;
vPositive=false;
}
vFormer.set(Calendar.MILLISECOND,0);
vFormer.set(Calendar.SECOND,0);
vFormer.set(Calendar.MINUTE,0);
vFormer.set(Calendar.HOUR_OF_DAY,0);
vLatter.set(Calendar.MILLISECOND,0);
vLatter.set(Calendar.SECOND,0);
vLatter.set(Calendar.MINUTE,0);
vLatter.set(Calendar.HOUR_OF_DAY,0);
intvCounter=0;
while(vFormer.before(vLatter)){
vFormer.add(Calendar.DATE,1);
vCounter++;
}
if(vPositive)
returnvCounter;
else
return-vCounter;
}
1.3.两个日期的月份差
获得两个日期的月份差的方法与获得日期差基本一致。
但需要注意的是,计算月份差不能简单用before()来进行计算。
还需要考虑
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 实用 经验总结