Profiel van 夏之小屋๑۩۞۩๑夏之小屋๑۩۞۩๑Foto'sWeblogLijstenMeer Extra Help

Weblog


    22-7-2006

    与我联系

     

       
     

    1、QQ:323240056/524542352

     
     

    2、MSN:thecomputerofld@hotmail.com

     
         
         
         
    8-6-2006

    稻草誓言


    稻草誓言

    稻草
    他在与风儿作伴
    他们轻轻的立下誓言
    要陪对方海枯石烂
    稻草在风中起舞
    风而与他缠绵
    然而
    时间流逝
    稻草被风而吹断
    他们的誓言
    在风儿的呼喊中
    消散..

    今夜无眠


    今夜无眠



    寂静的夜
    我在期盼
    期盼黎明后见到你的笑脸


    寂静的夜
    我在思念
    思念我们故去很久的誓言


    寂静的夜
    我在眺望
    眺望我无法到达的你的岸边
    今夜无眠
    只有星光与我为伴

    3-5-2006

    极限压缩

    极限压缩
     
    64kb的3D动画是什么样的、96kb的fps游戏和cs效果几乎相同,小小的程序体现了实力的极致。
    Pasta Pronta
    Free Web Counter
    Pasta Pronta
    2-5-2006

    pc2n 操作系统网上玩

                PC2N   操作系统
                                 网上 
        PC2N网站实际上就是一个网络硬盘,只不过网络硬盘界面改为了flash制作的操作系统桌面而已,其中的一些扩展服务会让你感到和自己的系统没有什么区别
     
          step1.  注册
      pc2n为大家免费提供了2G的网络空间,很方便就可以完成注册。
    地址:www.pc2n.com
     
    step2. 使用
           1.登录
              pc2n的登录界面和windowsXP很相似,在用户名一筐填入注册的帐号,接着输入密码,这样就可以登录到pc2n的网络系统了。
     
             2.  操作
                 进入网络操作系统后我们就会有一种非常熟悉的感觉,界面和windowsXP非常相似。
                双击“我的点脑”,这里就是我们上传和下载的地方,可以把自己本地计算机的文件和程序保存到此。
     
    你是不是已经有些心动了呢?那就赶快来体验一下吧!
    Pasta Pronta
    Free Web Counter
    Pasta Pronta
    12-3-2006

    极限压缩!让MP3机装下更多的音乐

    极限压缩!让MP3机装下更多的音乐


      随着MP3播放器价格一再下调,很多人可能手头上都有了MP3播放器。利用MP3播放器欣赏歌曲固然方便,但是它的容量不像硬盘那么大,而 且购买的容量越大,价格也随着水涨船高。

      那我们为什么不在声音文件上做做文章,让我们的MP3播放器放下更多的音乐?

    方案一:转为WMA文件法

      MP3声音虽好,但是微软推出的WMA文件却可以在只有MP3文件一半大小的前提下保持相同的音质。而且,现在的大多数MP3播放器都支持WMA 文件,所以我们可以考虑把MP3文件转换为WMA文件。这个任务可以交给Winamp来实现。

    先从本刊光盘上下载并安装Winamp,然后启动它,再把欲转换的MP3文件添加到播放列表中。

    接着,按下Ctrl+P键打开“Winamp参数设置”窗口,再单击“插件”→“输出”,然后在右侧窗口中单击“Nullsoft WMA 输出插件 v0.90 (x86) [OUT_WM.DLL]”项(如图1),再单击下方的“配置”按钮,在打开的窗口中选择相应的转换存放文件夹,并设置采样率、比特率和声道等。最 后单击“确定”按钮两次返回Winamp。

     

    转换为WMA就能压缩体积

    再在Winamp中打开欲转换的SWA文件,最后单击“播放”按钮,等一会儿,你的所有的SWA文件就被转换成了WAV格式的文件了,可供声音编 辑器再编辑、再利用。

    不过,大家要注意的是,某些采样率、比特率和声道格式组合将会导致现错误信息,并无法播放。

    方案二:把CD录制成为WMA文件

    利用Windows Media Player可以把CD直接录制为WMA文件:选择 “开始”→“程序”→“附件”→“娱乐”→Windows Media Player启动它。再把音乐CD插入CD-ROM中,并点击工作界面中的“CD音频”按钮。

    Windows Media Player会自动读取CD音轨,并将它们显示在窗口中。在音频窗口中选择欲复制的轨道,然后按下该界面中的“复制音乐”按钮(如图2),复制 操作就开始了,过程中会显示复制的百分比,同时“复制音乐”钮也会变成“停止复制”钮。

     

     

    把CD也录制成WMA

    但默认情况下,CD音乐将会被复制到C:\Documents and SettingsAll UsersDocumentsMy Music文件夹下,我们也可以单击“工具”→“选项”→“CD音频”标签下“存档”旁的“更改”钮,重新指定一文件夹,以后CD音乐将会复制 到我们指定文件夹中。

    WMA文件较MP3文件小出几乎一半,又能被MP3播放器识别。因此,上面的技巧,将可以大大减少声音文件容量。从而让播放器装下更多的音 乐。

    方案三:给MP3文件减肥

    MP3的大小与它的采样率、比特率和声道有很大的关系。因此,我们可以考虑把MP3文件以更低的采样率、比特率来进行重新编码,这样可以 给MP3文件减肥。而这个任务可以由免费软件CDex(本地下载 )来完成。

    第一步:进行必要设置

    先把欲“减肥”的MP3文件放到一文件夹下。再启动CDex,选择单击Option→Settings,首先在Filename选项卡中单击WAV→MP3项后的按钮 选择转换文件的路径。

    再单击Encoder标签,通过Encoder项后的下拉菜单选择Lame MP3 Encoder。然后在下面的Encoder Options下选择采样率、比特率和声道,建议直接在Quality(品质)的列表中选择R3Mix(如图3)。


    Mp3的“减肥”方案可以在这里设置

    第二步:开始转换

    返回到CDex中,选择Convert→MPEG→MPEG命令,打开如图4所示的窗口,再单击旁的...按钮选择转换文件夹,CDex即可找出该文件夹下所 有可以转换的MP3文件。

    我们在窗口中配合Ctrl和Shift键选择欲转换的文件(也可以单击Select All选定所有文件),再单击Convert按钮即可把选中的多个MP3文件批量重新编码成体积较小的MP3文件。

    另外,CDex也可以把CD变成MP3,方法是先单击Option→Settings,在Filename选项卡中单击Recorded Tracks项后的按钮选择抓取CD后MP3文件存放的路径,再单击Encoder标签,在其中设置相应的压缩算法。

    最后,把CD光盘放入光驱中,再选择Convert(转换)→Extract CD Track(s) to MPEG files(抓取CD到MPEG文件)或按下F4键抓取即可。

    方案四:把WAV转为SWA文件

    SWA文件是一种超音频格式文件,被广泛用在Macromedia公司的Authorware、Diretor、Flash等软件中。一般来说超级音频格式的声音文件 在容量上与波形文件的比例约是24:1,压缩率比MP3还要高。因此,我们可以考虑把声音文件压缩成SWA文件,再把它装入到MP3文件中。

    启动Authorware 6.0汉化版,选择Xtras→“其它”→“转换WAV到SWA”,再在打开的窗口中单击“添加文件”按钮,再在打开的“选择.WAV 文件 转换到 .SWA”对话框中选定欲转换的WAV文件,单击“打开”按钮返回。再单击“转换文件的目标文件夹 ”按钮,再选择一个转换后文件存放文件夹,再单击“转换”按钮即可(如图4)。

    转换WAV到SWA

    最后,我们还必须把生成后的SWA文件的扩展名由SWA改为MP3,再上传到MP3播放器里就可以了。

    讲到这里,可能有很读者可能会问,为什么MP3播放器可以播放SWA文件?

    其实,MP3和SWA文件采取的都是MPEG Audio Leve3,只是MP3属于MPEG Audio Level3 I格式,而SWA属于MPEG Audio Level3 II格式,所以我们只要把SWA文件的扩展名改为MP3即可装入到MP3播放器中播放。

    同时,现在很多光盘上的评书和英语听力都是SWA格式的,知道了这个原理后,我们只要把这些文件的扩展名改为MP3,则可以直接上传到 MP3播放机中播放,而无需转换。

    菜鸟初学Java的备忘录之Applet

    菜鸟初学Java的备忘录之Applet


    Java的入门比我想象中的还要简单的多,目前为止我已经对Java的基本语法结构有所了解。但我知道,深入的研究任何一门语言,都需要时间和实践的积累.

       Applet是Java编写的可以在浏览器端运行的代码,它与应用程序的明显区别在于其执行方式的不同,应用程序如C程序是从main()主程序开始运行的,而Applet则比较复杂.具体如何复杂我不太清楚,但我会慢慢清楚的。关于Applet的一个重要性质是,我可以将HTML中的值作为参数传递给Applet(通过getParameter()来获取参数值).这样,为了产生不同的效果,我们不需要重新编译Java程序,而只需修改HTML的参数值即可。由于HTML的代码也可以动态生成的,因此我可以对网页的动态效果进行随心所欲的控制.

       在Applet的生命周期中有3个比较主要的方法:init,start,stop。

       init():负责Applet的初始化,在整个Applet的生命周期中,只执行一次该方法.和Delphi中的OnCreate()事件是一样的道理

       start():系统在调用完init()后,将自动调用start(),并且每次当前窗口被重新激活时,都会调用该方法,和Delphi中的OnShow()事件是差不多的。

       stop():用户离开Applet所在页面后调用该方法。它使你在用户不注意Applet的时候,停止一些资源的工作以免影响系统运行效率.并且我们不需要人为地去掉该方法.该方法同Delphi中的OnClose()事件差不多。

       下面是一个Applet版的HelloWorld

       文件名:HelloWorld.java

    import?java.applet.Applet;
    import?java.awt.Graphics;

    public?class?HelloWorld?extends?Applet
    {
    ?? String?title;
    ?? public?void?init(){
    ???? title="Hello?World";?
    ?? }?
    ??
    ?? public?void?paint(Graphics?g)
    ?? {
    ???? g.drawString(title,50,20);
    ?? }

    }

       我们可以看到,程序中是没有主函数的,那它如何运行呢.既然Applet是在浏览器环境下运行的,那我们就需要在HTML文件中来调用它.需要使用的相关标签就是<Applet&gt;标签,我们首先建立HTML文件test.htm,源代码如下:

    <html>
    <body>
    Here?comes?my?first?applet:
    <br>
    <applet?code=HelloWorld.class?width=650?height=500>
    </applet>
    </APPLET>
    </body>
    </html>


    将该文件和HelloWorld.java放在同一个目录中,然后编译HelloWorld.java之后,直接点击test.htm打开,就可以看到Applet程序被启动了.或者使用AppletViewer命令AppletViewer?test.htm也可以不需要浏览器直接运行该Applet。

    下面一个程序可以更好的帮助我们理解Java?Applet在整个生命周期是如何调用上面我们介绍的几个方法的。

       文件名:StartStop.java

    import?java.awt.*;
    import?java.applet.*;

    public?class?StartStop?extends?Applet
    {
    ?? StringBuffer?message;
    ?? public?void?init()
    ?? {
    ???? message=new?StringBuffer("Init?done...");
    ?? }
    ??
    ?? public?void?start()
    ?? {
    ???? message.append("Started...");
    ?? }

    ?? public?void?stop()
    ?? {
    ???? message.append("Stopped...");
    ?? }

    ?? public?void?paint(Graphics?g)
    ?? {
    ???? g.drawString(message.toString(),150,50);
    ?? }

    }

       运行方法同上.(该程序可参考机械工业的<Java?简明教程>)

       同C语言不一样,使用Java实现GUI要轻松的多。由于是纯面向对象的语言,Java的AWT提供了各种界面元素供我们调用,如同Delphi里面的组件一样。下面是Java中的GUI对象和Delphi中的相应组件的对照表。

    Java??????????Delphi
    ??
    Button????????TButton
    Canvas????????TCanvas
    Checkbox??????TCheckbox
    CheckboxGroup?TRadioGroup
    Choice????????TComboBox
    Label?????????TLabel
    TextField?????TEdit
    TextArea??????TMemo

       然而,JDK并不是可视化的RAD(rapid?application?development)开发工具,对于对象的使用不能如Delphi那样只需要拖拉,移位就可以,而需要我们写调用代码.这就产生了一个麻烦,我怎么样才能够让元素按我的要求在界面中摆放呢?在元素不多时可以让Java自动布局(Layout),但是元素很多时,或者需要根据应用程序的要求来摆放元素时,就需要使用面板了(Panel).面板在Delphi里也有相应的组件(TPanel),但主要是用来对界面分割,进行粗略的布局的,精确的定位还需要开发人员手动调节.而Java则只能用Panel来定位,不能不说是个缺陷.也许是我还没学到吧。

       入门之后,就该深入到对象的概念了。

       假设在Java中创建了一个叫做Date的自定义数据类型如下
    ?
    ?public?class?Date{
    ???? int?day;
    ???? int?month;
    ???? int?year;
    ?? }

       那么对于以下三条声名变量的语句,java在为其分配内存的时候有什么不同吗?
    ?
    ?(1)?int?i;
    ?? (2)?Date?mybirth;
    ?? (3)?Date?mybirth=new?Date();

       很显然是有的,分配情况如下:

       (1)Java自动为整数i分配整形变量的内存,一般是两个字节

       (2)Java声明了一个Date类的实例变量mybirth,并为其分配了存储空间,但这个存储空间里面存储的只是一个引用,或者说是一个地址,目前的地址里面什么都没有,那么我们就还不能使用这个实例变量,不能引用它的成员。

       (3)Java创建了一个Date类的实例变量mybirth,并为其成员变量分配了足够的存储空间,最后返回对这个存储空间的一个引用,也就是返回这个存储空间的首地址,以后就通过mybirth,即这个首地址来访问这个实例变量里面的各个成员,如mybirth.day,mybirth.month,mybirth.year。

       当我们声明一个基本数据类型的变量(如boolean,?byte,?short,?char,?int,long,flat,double)时,系统会自动为变量分配内存。但是如果声明了String或者用户自定义的变量时,系统不会立即为其分配内存.为什么会这样呢?

       这是因为,String和用户自定义的变量都属于类的范畴.一个被声明为类的变量,已经不再是一个数据了,而变成对数据的一个引用,就是说mybirth可以看作是一个指向类的一个实例的指针,里面存放的是地址.这样的话就好理解了。

       深入一点,既然类的实例变量值是指针,这个指针指向一个类的实例,那么我们显然可以定义多个名称不同的类的实例变量,而将它们全都指向一个实例。比如:

      ? University?u=new?University();//定义了一个类University的实例变量u,并为其分配对象的存储空间

       University?u2=u;//又定义了一个实例变量u2,将u的值赋给u2

       那么显然u2和u除了名称不同外,其实是一回事,因为它们指向相同一个地址.

       我觉得弄清楚这一点还是很重要的。这些数据结构的东西,是一个程序员需要知道的所以然

    编写跨平台Java程序的十二条规则

    编写跨平台Java程序的十二条规则


    使用Java语言编写应用程序最大的优点在于“一次编译,处处运行”,然而这并不是说所有的Java程序都具有跨平台的特性,事实上,相当一部分的Java程序是不能在别的操作系统上正确运行的,那么如何才能编写一个真正的跨平台的Java程序呢?下面是在编写跨平台的Java程序是需要注意的一些事情:

       1.编写Java跨平台应用程序时,你可以选择JDK1.0,1.1,1.2或支持它们的GUI开发工具如:Jbuilder,Visual Age for Java 等等,但是必须注意你的Java程序只能使用Java核心API包,如果要使用第三方的类库包,则该类库包也要由Java核心包开发完成,否则在发布你的程序的时候还得将支持该Java类库包的JVM发布出去。也就是说,你的程序需要是100%纯Java的。举一个例子,Visual J++ 就不是纯Java的,由Visual J++编写的程序也就不具有平台无关性。

       2.无论你使用的是JDK或其他开发工具,在编译时都要打开所有的警告选项,这样编译器可以尽可能多的发现平台相关的语句,并给出警告。虽然不能保证没有编译时警告错误的程序一定是跨平台的,但含有警告错误的程序却很有可能是非平台无关的。

       3.在程序中使用任何一个方法的时候,要详细察看文档,确保你使用的方法不是在文档中已经申明为过时的方法(Deprecated method),也不是文档中未标明的隐含方法(Undocumented method)。

       4.退出Java程序时尽量不要使用java.lang.System的exit方法。Exit 方法可以终止JVM,从而终止程序,但如果同时运行了另一个Java程序,使用exit方法就会让该程序也关闭,这显然不是我们希望看到的情况。事实上要退出Java程序,可以使用destory()退出一个独立运行的过程。对于多线程程序,必须要关闭各个非守护线程。只有在程序非正常退出时,才使用exit方法退出程序。

       5.避免使用本地方法和本地代码,尽可能自己编写具有相应功能的Java类,改写该方法。如果一定要使用该本地方法,可以编写一个服务器程序调用该方法,然后将现在要编写的程序作为该服务器程序的客户程序,或者考虑CORBA(公共对象请求代理)程序结构。

       6.Java中有一个类似于Delphi中的winexec的方法,java.lang.runtime类的exec方法,作为该方法本身是具有平台无关性的,但是给方法所调用的命令及命令参数却是与平台相关的,因此,在编写程序时要避免使用,如果一定要调用其他的程序的话,必须要让用户自己来设置该命令及其参数。比如说,在windows中可以调用notepad.exe程序,在linux 中就要调用vi程序了。

       7.程序设计中的所有的信息都要使用ASCII码字符集,因为并不是所有的操作系统都支持Unicode字符集,这对于跨平台的Java中文软件程序不能不说是一大噩耗。

       8.在程序中不要硬性编码与平台相关的任何常量,比如行分隔符,文件分隔符,路径分隔符等等,这些常量在不同的平台上是不同的,比如文件分隔符,在UNIX和MAC中是“/”,在windows中是“\”,如果要使用这些常量,需要使用jdava.util.Properties类的getProperty方法,如java.util.Properties.getProperty(“file.separator”)可以获得文件分隔符,getProperty (“line.separator”)返回行分隔符,getProperty(“path.separator”)返回路径分隔符。

       9.在编写跨平台的网络程序时,不要使用java.net.InetAddress类的getHostName方法得到主机名,因为不同的平台的主机名格式是不同的,最好使用getAddress得到格式相同的IP地址,另外,程序中所有的主机名都要换成IP地址,比如www.263.net就要换成相应的IP地址。

       10.涉及文件操作的程序需要注意:不要在程序中硬性编码文件路径,理由和8中一样,只是这一点特别重要,因此单独提出。而且,不同平台对于文件名使用的字符及最大文件名长度的要求不同,编写你的程序的时候要使用一般的ASCII码字符作为文件的名字,而且不能与平台中已存在的程序同名,否则会造成冲突。

       11.如果您写的程序是GUI程序,在使用AWT组件时不能硬性设置组件的大小和位置而应该使用Java的布局管理器(layout manager)来设置和管理可视组件的大小和位置,否则有可能造成布局混乱。

       12.由于不同的操作系统,不同的机器,系统支持的颜色和屏幕的大小和分辨率都不同,如何获得这些属性呢?使用java.awt.Systemcolor类可以获得需要的颜色,如该类的inactiveCaption 就是窗口边框中活动标题的背景颜色,menu则是菜单的背景颜色。使用java.awt.Toolkit的getScreenResolution可以以“象素每英寸”为单位显示屏幕的分辨率。该类的getScreenSize可以得到屏幕大小(英寸),loadSystemColors可以列出所有的系统颜色。

    Java秘史:隐藏在SWT/Swing背后的故事

    Java秘史:隐藏在SWT/Swing背后的故事


    译注:本文来自straight_talking_java@yahoogroups.com讨论组,已经是一年多前的文章。Alan Williamson是Java Developers Journal的编辑,下文来自他在IBM的一个消息来源。SWT和Swing的论争我见过不少,Netbeans和Eclipse的也同样多。译者翻译此文并不是要激起什么争执,也不是支持哪一方(虽然我的确是站在SWT一边的),更不是要攻击Amy。我最重要的理由是,这是一篇有趣的文章。里面有内幕、线人、公司政治、垄断巨头、美女、商界风云……足够拍一出电影。有趣,这就够了。不过此文反映了IBM对Swing的看法和SWT的由来,还是有一点营养的。

       要想弄清楚为什么一切都被弄得如此混乱,要从几年前只存在AWT的时候说起。SUN当时已经建立了一套基本的可移植控件类,这些类映射到不同操作系统上的原生窗口组件(native widget),显然下一步应该继续增强这套模型,除了初始的CUA 92组件(文字、按钮等等),再继续加上表格、树、记事本、滑块等等……当时的AWT还满是漏洞,远不能称为可靠,还需要SUN的coder们去修补。SUN的developer们如Graham和Otto总是习惯于公开把他们的bug归咎为操作系统的差异,比如“Windows和OS/2的焦点次序不同”或者“在……之间Ctrl-X的行为不一样”,以及其他苍白的托辞,好让批评的火力从SUN太早释出代码这个问题的真相上移开。然后Amy Fowler来到了SUN。不是我大男子主义,Amy是个聪明的美女,大多数呆头呆脑只懂技术的开发人员都要被她捏在手里。 Amy来自一家Smalltalk公司,叫做Objectshare,在那里她负责搞UI类库。

       跟Java相比Smalltalk的历史有些悲惨,曾几何时有3家庞大的Smalltalk公司——IBM、Parc-Place和Digitalk。在90年代初期3家公司的市场份额大致相等,生活是美好的。Parc-Place采用仿窗口部件(emulated widgets)的设计(即Swing的设计),IBM和Digitalk则采用原生窗口部件(native widgets)。后来IBM压倒了另外两家,因此他们打算合并成一家,假设叫做Parc-Place Digitalk。随后当他们试图将他们的产品融合到一个叫做Jigsaw的计划中时爆发了一场大战,计划由于政治原因失败了(开发人员实际上已经能让它运转起来),就因为原生和仿造两派的死战。

       Amy赢得了精神上的胜利,不过在IBM我们赢得了他们所有的生意,因为这两家公司在一整年里除了吵架什么都没做。当尘埃落定之后PPD(Parc-Place Digitalk当时已改名为Objectshare,跟Windscale改名为Sellafield的原因相同——让人们淡忘之前发生的灾难)的股票价格从60美元掉到了低于1美元1股。他们因为伪报收入被NASDAQ摘牌,从此消失。此时SUN正走上与PPD类似的技术方向,于是PDD的技术人员都把他们的简历投到了SUN。Amy被雇佣了,她承诺通过轻量级方案解决所有窗口组件的问题,因此说服SUN管理层让她当了GUI开发部门的头头。她是拿着“这里原来的人都搞砸了,我是来解决的”的钥匙进来的。随后Amy雇佣了所有她过去在Parc-Place的旧朋友,让他们来开发Swing。

       显然Swing应该做的是仅仅成为一个绘制框架,给那些希望创建地图软件或者绘图软件的人们使用,无论如何,应该围绕AWT类库来建造它,按钮之类的东西仍然交给AWT来管。SUN的人比如Philip和Mark已经让AWT能够处理表格、树和记事本(notebook,?),所以Swing的方向应该说很明显了。但那些毁了PDD的人不干,他们非要把一切都弄成轻量级的。由于SUN管理层的无知,再加上Amy无情的政治手段,造成了我们今天所见的混乱局面。Amy还使SUN相信Swing是作为Mozilla项目的一部分与Netscape联合开发的,事实上这只是她的宣传伎俩。

       在IBM,我们从第一天起就憎恶Swing。庞大、满是错误,而且难看至极。原先我们的工具如VisualAge for Java都是用Smalltalk(用的是原生窗口组件)写的,所以当我们将这些工具向Java代码库迁移时,我们需要一套窗口组件。IBM这边的开发人员都是原来搞Smalltalk的那一批人,我们对管理层要求用Swing来构建WebSphere Studio工具都非常不情愿。Swing是个可怕的充满缺陷的怪兽。

       在WebSphere Studio最初的预览中,当与Microsoft Visual Studio作对比演示的时候,我们所有的客户都讨厌它,就因为它的外观,而不管它的功能有多强。大多数消费者都不会买一辆让人觉得难看的车,哪怕这车有一台出色的引擎。因此我们开始了一个项目,是把我们的Smalltalk原生窗口组件移植到Java上去。这个项目是加拿大的Object Technology International小组做的。这个项目获得了成功,被运用在在我们发布的VisualAge Micro Edition产品中,VisualAge Micro Edition后来成为J2ME开发方面一个非常成功的IDE。但是OTI的人发现,Swing在读取Windows事件方面有极严重的缺陷,我们甚至无法进行SWT(S开始是Simple的缩写,不过后来变成了Standard的缩写)和Swing间的互操作。他们在读事件队列的时候用了一种可能留下内存漏洞的方式,所以我们不得不采用我们自己的查询Windows事件队列的循环,以纠正这个错误。

       我们试了一次又一次让SUN修复这个错误,但Amy就是听不进去,所以我们才决定SWT和AWT/Swing不能共存。我们甚至在SWT中定义了自己的Point和Rectangle类——整个工具包对AWT或Swing都没有任何依赖。我们把这个工具包放到了Eclipse中,这是一个工具平台,它的总体设计目标就是要战胜Micrsoft和Visual Studio。Eclipse是开源的,所以任何人都可以在上面构建自己的东西,我们已经有像TogetherSoft和Rational这样的公司移植到了上面。我们的竞争者是Microsoft,所以我们所有努力和注意力都是从正面针对Microsoft。

       不管怎么说SUN对此非常不满。他们的Netbeans跟Eclipse做的是相同的事,因此他们向IBM高层抱怨。他们认为SWT是要将你绑到Windows上,这纯粹是胡说,因为SWT能通过GTK在Mac/Linux上运行,以及一大堆嵌入式平台。他们拒绝让Eclipse获得Java认证,因为里面有原生代码,所以Eclipse产品必须很小心地使用单词“Java”这个SUN的商标。Eclipse甚至不能把自己称为一个Java IDE,SUN已经威胁过要采取法律行动来制止IBM在任何时候把Eclipse称作一个Java IDE。结果之一就是IBM在Eclipse上创建的GUI设计工具,允许你构建Swing/AWT GUI,却不让你往里面拖放SWT窗口控件。

       将SWT从Eclipse中分离出来是完全可能的,只需要把DLL抠出来放到路径中,并使用窗口组件工具包来给你的银行或者保险或者其他什么应用程序开发GUI。再次说明,我们无法更进一步,因为SUN把我们的双手绑上了。虽然作为Eclipse开放源码协议的一部分,CPL允许我们提供这样的解决方案,但SUN已经很清楚地表明他们不希望我们这样做。 对于用户社区来说,无论IBM和SUN的最终动机是什么,我发现有一点总是很有趣:喜爱Swing的人总会说“一旦你花上几年时间去掌握它,你就能正确地使用它”,这基本上是他们在试图证明和维护他们辛苦得来的用途有限的专门技术;而SWT的拥护者们说的是“哇,这真快,这跟原生的一样,还可以用XP皮肤……它还又轻又小”。有一句话是我喜欢的,我们的一个用户说,Swing就像Java决定不通过操作系统来实现原生的IO,而是通过磁头马达API自己来读磁盘的扇区。Swing基本上就是这样的,它拿着个底层的“paint(Graphics)”方法,自己来绘制所有的窗口组件。

    Java桌面应用程序设计新贵:SWT 简介

    Java桌面应用程序设计新贵:SWT 简介

     

    Java语言的声望和它在桌面应用程序(GUI程序)所取得的成就显然极不相符,至今仍然很少能看到非常成功Java桌面程序。虽然有JBuilder,Netbean,JProbe等大型软件作为代表,但这仍不能证明Java的GUI程序是成功的:它们的外观总是和同一操作系统平台下的其它软件显得格格不入。对机器配置的需求也似乎永无止境,这使得它们只能被一些总是拥有当前最高性能PC的程序员们所容忍,或是那些不在乎金钱和时间的专业用户所接受。对绝大多数计算机使用者来说,AWT或SWING代表着怪异的界面和无法接受的速度。Standard Widget Toolkit(SWT)或许是Java这一噩梦的终结者,广大Java程序员终于可以开发出高效率的GUI程序,它们拥有标准的外观,几乎没有人能看出你的程序是用Java写出来的,更为重要的是,这些程序是跨平台的。

       SWT本身仅仅是Eclipse组织为了开发Eclipse IDE环境所编写的一组底层图形界面 API。或许是无心插柳,或是有意为之,至今为止,SWT无论是在性能和外观上,都超越了SUN公司提供的AWT和SWING。目前Eclipse IDE已经开发到了2.1版本,SWT已经十分稳定。这里指的稳定应该包含两层意思:

       一是指性能上的稳定,其中的关键是源于SWT的设计理念。SWT最大化了操作系统的图形构件API,就是说只要操作系统提供了相应图形的构件,那么SWT只是简单应用JNI技术调用它们,只有那些操作系统中不提供的构件,SWT才自己去做一个模拟的实现。可以看出SWT的性能上的稳定大多时候取决于相应操作系统图形构件的稳定性。

       另一个稳定是指SWT API包中的类、方法的名称和结构已经少有改变,程序员不用担心由于Eclipse组织开发进度很快(Eclipse IDE每天都会有一个Nightly版本的发布),而导致自己的程序代码变化过大。从一个版本的SWT更新至另一版本,通常只需要简单将SWT包换掉就可以了。

       第一个SWT程序

       下面让我们开始一个SWT程序。(注意:以下的例子和说明主要针对Windows平台,其它的操作系统应该大同小异)。首先要在Eclipse安装文件中找到SWT包,Eclipse组织并不提供单独的SWT包下载,必须下载完整的Eclipse开发环境才能得到SWT包。SWT是作为Eclipse开发环境的一个插件形式存在,可以在${你的eclipse安装路径}\plugins路径下的众多子目录下去搜索SWT.JAR文件,在找到的JAR文件中包含了SWT全部的Java类文件。因为SWT应用了JNI技术,因此同时也要找到相对应的JNI本地化库文件,由于版本和操作平台的不同,本地化库文件的名称会有些差别,比如SWT-WIN32-2116.DLL是Window平台下Eclipse Build 2116的动态库,而在Unix平台相应版本的库文件的扩展名应该是.so,等等。注意的是,Eclipse是一个开放源代码的项目,因此你也可以在这些目录中找到SWT的源代码,相信这会对开发很有帮助。下面是一段打开空窗口的代码(只有main方法)。

    import com.e2one.example;
    public class OpenShell{
      public static void main(String [] args) {
       Display display = new Display();
       Shell shell = new Shell(display);
       shell.open();
       // 开始事件处理循环,直到用户关闭窗口
       while (!shell.isDisposed()) {
        if (!display.readAndDispatch())
         display.sleep();
       }
       display.dispose();
      }
    }

       确信在CLASSPATH中包括了SWT.JAR文件,先用Javac编译例子程序。编译无错后可运行java -Djava.library.path=${你的SWT本地库文件所在路径} com.e2one.example.OpenShell,比如SWT-WIN32-2116.DLL件所在的路径是C:\swtlib,运行的命令应该是java -Djava.library.path=c:\swtlib com.e2one.example.OpenShell。成功运行后,系统会打开了一个空的窗口。

       剖析SWT API

       下面再让我们进一步分析SWT API的组成。所有的SWT类都用org.eclipse.swt做为包的前缀,下面为了简化说明,我们用*号代表前缀org.eclipse.swt,比如*.widgets包,代表的是org.eclipse.swt.widgets包。

       我们最常用的图形构件基本都被包括在*.widgets包中,比如Button,Combo,Text,Label,Sash,Table等等。其中两个最重要的构件当数Shell和Composite。Shell相当于应用程序的主窗口框架,上面的例子代码中就是应用Shell构件打开一个空窗口。Composite相当于SWING中的Panel对象,充当着构件容器的角色,当我们想在一个窗口中加入一些构件时,最好到使用Composite作为其它构件的容器,然后再去*.layout包找出一种合适的布局方式。SWT对构件的布局也采用了SWING或AWT中Layout和Layout Data结合的方式,在*.layout包中可以找到四种Layout和与它们相对应的布局结构对象(Layout Data)。在*.custom包中,包含了对一些基本图形构件的扩展,比如其中的CLabel,就是对标准Label构件的扩展,上面可以同时加入文字和图片,也可以加边框。StyledText是Text构件的扩展,它提供了丰富的文本功能,比如对某段文字的背景色、前景色或字体的设置。在*.custom包中也可找到一个新的StackLayout布局方式。

       SWT对用户操作的响应,比如鼠标或键盘事件,也是采用了AWT和SWING中的Observer模式,在*.event包中可以找到事件监听的Listener接口和相应的事件对象,例如常用的鼠标事件监听接口MouseListener,MouseMoveListener和MouseTrackListener,及对应的事件对象MouseEvent。

       *.graphics包中可以找到针对图片、光标、字体或绘图的API。比如可通过Image类调用系统中不同类型的图片文件。通过GC类实现对图片、构件或显示器的绘图功能。

       对不同平台,Eclipse还开发了一些富有针对性的API。例如,在Windows平台,可以通过*.ole.win32包很容易的调用ole控件,这使Java程序内嵌IE浏览器或Word、Excel等程序成为可能!

       更复杂的程序

       下面让我们展示一个比上面例子更加复杂一些的程序。这个程序拥有一个文本框和一个按键,当用户点击按键的时候,文本框显示一句欢迎信息。

       为了文本框和按键有比较合理的大小和布局,这里采用了GradLayout布局方式。这种布局是SWT中最常用也是最强大的布局方式,几乎所有的格式都可能通过GradLayout去达到。下面的程序也涉及到了如何应用系统资源(Color),以及如何释放系统资源。

    private void initShell(Shell shell) {
      //为Shell设置布局对象
      GridLayout gShellLay = new GridLayout();
      shell.setLayout(gShellLay);
      //构造一个Composite构件作为文本框和按键的容器
      Composite panel = new Composite(shell,SWT.NONE);
      //为Panel指定一个布局结构对象。
       这里让Panel尽可能的占满Shell,
       也就是全部应用程序窗口的空间。
      GridData gPanelData = new GridData(GridData.GRAB_HORIZONTAL| GridData.GRAB_VERTICAL|GridData.FILL_BOTH);
      panel.setLayoutData(gPanelData);
      //为Panel也设置一个布局对象。文本框和按键将按这个布局对象来显示。
      GridLayout gPanelLay = new GridLayout();
      panel.setLayout(gPanelLay);
      //为Panel生成一个背景色
      final Color bkColor = new Color(Display.getCurrent(),200,0,200);
      panel.setBackground(bkColor);
      //生成文本框
      final Text text = new Text(panel,SWT.MULTI|SWT.WRAP);
      //为文本框指定一个布局结构对象,
       这里让文本框尽可能的占满Panel的空间。
      GridData gTextData = new GridData (GridData.GRAB_HORIZONTAL| GridData.GRAB_VERTICAL|GridData.FILL_BOTH);
      text.setLayoutData(gTextData);
      //生成按键
      Button butt = new Button(panel,SWT.PUSH);
      butt.setText("Push");
      //为按键指定鼠标事件
      butt.addMouseListener(new MouseAdapter(){
       public void mouseDown(MouseEvent e){
        //当用户点击按键的时候,显示信息
        text.setText("Hello SWT");
       }
      });
      //当主窗口关闭时,会触发DisposeListener。这里用来释放Panel的背景色。
      shell.addDisposeListener(new DisposeListener(){
       public void widgetDisposed(DisposeEvent e) {
        bkColor.dispose();
       }
      });
    }


    把这段代码中的方法initShell()加入到第一个打开空窗口的例子中,得到的是一段能成功运行的完整GUI应用程序。运行方法可参考第一个例子。

    系统资源的管理

       在一个图形化的操作系统中开发程序,都要调用系统中的资源,如图片、字体、颜色等。通常这些资源都是有限的,程序员务必非常小心的使用这些资源:当不再使用它们时,就请尽快释放,不然操作系统迟早会油尽灯枯,不得不重新启动,更严重的会导致系统崩溃。
    SWT是用Java开发的,Java语言本身的一大优势就是JVM的"垃圾回收机制",程序员通常不用理会变量的释放,内存的回收等问题。那么对SWT而言,系统资源的操作是不是也是如此?答案是一个坏消息,一个好消息。

       坏消息是SWT并没采用JVM的垃圾回收机制去处理操作系统的资源回收问题,一个关键的因素是因为JVM的垃圾回收机制是不可控的,也就是说程序员不能知道,也不可能做到在某一时刻让JVM回收资源!这对系统资源的处理是致命的,试想你的程序希望在一个循环语句中去查看数万张图片,常规的处理方式是每次调入一张,查看,然后就立即释放该图片资源,而后在循环调入下一张图片,这对操作系统而言,任何时刻程序占用的仅仅是一张图片的资源。但如果这个过程完全交给JVM去处理,也许会是在循环语句结束后,JVM才会去释放图片资源,其结果可能是你的程序还没有运行结束,操作系统已经宕掉。

       但下面的好消息也许会让这个坏消息变得无关紧要。对于SWT,只需了解两条简单的"黄金"法则就可以放心的使用系统资源!之所以称为黄金法则,一是因为少,只有两条,二是因为它们出奇的简单。第一条是"谁占用,谁释放",第二条是"父构件被销毁,子构件也同时被销毁"。第一条原则是一个无任何例外的原则,只要程序调用了系统资源类的构造函数,程序就应该关心在某一时刻要释放这个系统资源。比如调用了

    Font font = new Font (display, "Courier", 10, SWT.NORMAL);

       那么就应该在不在需要这个Font的时候调用

    font.dispose();

       对于第二个原则,是指如果程序调用某一构件的dispose()方法,那么所有这个构件的子构件也会被自动调用dispose()方法而销毁。通常这里指的子构件与父构件的关系是在调用构件的构造函数时形成的。比如,

    Shell shell = new Shell();
    Composite parent = new Composite(shell,SWT.NULL);
    Composite child = new Composite(parent,SWT.NULL);

       其中parent的父构件是shell,而shell则是程序的主窗口,所以没有相应的父构件,同时parent又包括了child子构件。如果调用shell.dispose()方法,应用第二条法则,那么parent和child构件的dispose()方法也会被SWT API自动调用,它们也随之销毁。

       线程问题

       在任何操作平台的GUI系统中,对构件或一些图形API的访问操作都要被严格同步并串行化。例如,在一个图形界面中的按键构件可被设成可用状态(enable)或禁用状态(disable),正常的处理方式是,用户对按键状态设置操作都要被放入到GUI系统的事件处理队列中(这意味着访问操作被串行化),然后依次处理(这意味着访问操作被同步)。想象当按键可用状态的设置函数还没有执行结束的时候,程序就希望再设置该按键为禁用状态,势必会引起冲突。实际上,这种操作在任何GUI系统都会触发异常。

       Java语言本身就提供了多线程机制,这种机制对GUI编程来说是不利的,它不能保证图形构件操作的同步与串行化。SWT采用了一种简单而直接的方式去适应本地GUI系统对线程的要求:在SWT中,通常存在一个被称为"用户线程"的唯一线程,只有在这个线程中才能调用对构件或某些图形API的访问操作。如果在非用户线程中程序直接调用这些访问操作,那么SWTExcepiton异常会被抛出。但是SWT也在*.widget.Display类中提供了两个方法可以间接的在非用户线程的进行图形构件的访问操作,这是通过的syncExec(Runnable)和asyncExec(Runnable)这两个方法去实现的。例如:

    //此时程序运行在一个非用户线程中,并且希望在构件panel上加入一个按键。

    Display.getCurrent().asyncExec(new Runnable() {
      public void run() {
       Button butt = new Button(panel,SWT.PUSH);
       butt.setText("Push");
      }
    });

       方法syncExec()和asyncExec()的区别在于前者要在指定的线程执行结束后才返回,而后者则无论指定的线程是否执行都会立即返回到当前线程。

    SWT的扩展:JFace

       JFace与SWT的关系好比Microsoft的MFC与SDK的关系,JFace是基于SWT开发,其API比SWT更加易于使用,但功能却没SWT来的直接。比如下面的代码应用JFace中的MessageDialog打开一个警告对话框:

    MessageDialog.openWarning(parent,"Warning","Warning message");

       如果只用SWT完成以上功能,语句不会少于30行!

       JFace原本是为更加方便的使用SWT而编写的一组API,其主要目的是为了开发Eclipse IDE环境,而不是为了应用到其它的独立应用程序。因此在Eclipse 2.1版本之前,很难将JFace API完整的从Eclipse的内核API中剥离出来,总是要多多少少导入一些非JFace以外的Eclipse核心代码类或接口才能得到一个没有任何编译错误的JFace开发包。但目前Eclipse组织似乎已经逐渐意识到了JFace在开发独立应用程序起到的重要作用,在正在开发的2.1版本中,JFace也开始变成了和SWT一样的完整独立的开发包,只是这个开发包还在变动中(笔者写本文时,应用的Eclipse2.1M3版本)。JFace开发包的包前缀是以org.eclipse.jface开头。JAR包和源代码也和SWT一样,也在${你的eclipse安装路径}\plugins路径下去找。

       对开发人员来说,在开发一个图形构件的时候,比较好的方式是先到JFace包去找一找,看是不是有更简洁的实现方法,如果没有再用SWT包去自己实现。比如JFace为对话框提供了很好的支持,除了各种类型的对话框(比如上面用的MessageDialog,或是带有Title栏的对话框),如要实现一个自定义的对话框也最好从JFace中的Dialog类继承,而不是从SWT中的*.widget.Dialog继承。

       应用JFace中的Preference包中的类很容易为自己的软件做出一个很专业的配置对话框。对于Tree、Table等图形构件,它们在显示的同时也要和数据关联,例如Table中显示的数据,在JFace中的View包中为此类构件提供了Model-View方式的编程方法,这种方法使显示与数据分开,更加利于开发与维护。JFace中提供最多的功能就是对文本内容的处理。可以在org.eclipse.jface.text.*包中找到数十个与文本处理相关类。

       与应用程序更近一步

       Java程序通常是以class文件的方式发布的,运行class需要JRE或JDK的支持。这又是Java GUI程序的另一个致命的弱点,想象对一个面向广大用户的应用程序来说,无论你的程序功能有多简单,或是你的代码十分的精简,你都不得不让用户去下载一个7、8M的JRE,那是多么令人沮丧的一件事。而且对程序员来说,Class通常意味着源代码的暴露,反编译的工具让那些居心叵测的人轻易得到你的源代码。虽然有很多对Class的加密方法,但那总是以牺牲性能为代价的。好在我们还有其它的方式可用:把Class编译成exe文件!

       通过SWT开发包,简单、跨平台、可靠等这些Java语言本身所具有的优点正渐渐融合到图形界面的应用程序开发中去。因此,我相信Java语言的另一扇成功之门正在逐渐打开。

    Java Applet编程显示图像

    Java Applet编程显示图像


    Java Applet常用来显示存储在GIF文件中的图像。Java Applet装载GIF图像非常简单,在Applet内使用图像文件时需定义Image对象。多数Java Applet使用的是GIF或JPEG格式的图像文件。Applet使用getImage方法把图像文件和Image对象联系起来。
     Graphics类的drawImage方法用来显示Image对象。为了提高图像的显示效果,许多Applet都采用双缓冲技术:首先把图像装入内存,然后再显示在屏幕上。

       Applet可通过imageUpdate方法测定一幅图像已经装了多少在内存中。

       装载一幅图像

       Java把图像也当做Image对象处理,所以装载图像时需首先定义Image对象,格式如下所示:

    Image picture;

       然后用getImage方法把Image对象和图像文件联系起来:

    picture=getImage(getCodeBase(),"ImageFileName.GIF");
      
       getImage方法有两个参数。第一个参数是对getCodeBase方法的调用,该方法返回Applet的URL地址,如www.sun.com/Applet。第二个参数指定从URL装入的图像文件名。如果图文件位于Applet之下的某个子目录,文件名中则应包括相应的目录路径。

       用getImage方法把图像装入后,Applet便可用Graphics类的drawImage方法显示图像,形式如下所示:

    g.drawImage(Picture,x,y,this);

       该drayImage方法的参数指明了待显示的图像、图像左上角的x坐标和y坐标以及this。

       第四个参数的目的是指定一个实现ImageObServer接口的对象,即定义了imageUpdate方法的对象(该方法随后讨论)。

       显示图像(ShowImage.java)

    //源程序清单
    import java.awt.*;
    import java.applet.*;
    public class ShowImage extends Applet
    Image picure; //定义类型为Image的成员变量
    public void init()
    {
      picture=getImage(getCodeBase(),"Image.gif"); //装载图像
    }
    public void paint(Graphics g)
    {
      g.drawImage(picture,0,0,this); //显示图像
    }
    }

       为此,HTML文件中有关Applet的语句如下:

    <HTML>
    <TITLE>Show Image Applet</TITLE>
    <APPLET
    CODE="ShowImage.class" //class文件名为ShowImage.class
    WIDTH=600
    HEIGHT=400>
    </APPLET>
    </HTML>

       编译之后运行该Applet时,图像不是一气呵成的。这是因为程序不是drawImage方法返回之前把图像完整地装入并显示的。与此相反,drawImage方法创建了一个线程,该线程与Applet的原有执行线程并发执行,它一边装入一边显示,从而产生了这种不连续现象。为了提高显示效果。许多Applet都采用图像双缓冲技术,即先把图像完整地装入内存然后再显示在屏幕上,这样可使图像的显示一气呵成。

    双缓冲图像

       为了提高图像的显示效果应采用双缓冲技术。首先把图像装入内存,然后再显示在Applet窗口中。

       使用双缓冲图像技术例子(BackgroundImage.java)

    //源程序清单
    import java.awt.*;
    import java. applet.*;
    public class BackgroundImage extends Applet //继承Applet
    {
      Image picture;
      Boolean ImageLoaded=false;
      public void init()
      {
       picture=getImage(getCodeBase(),"Image.gif"); //装载图像
       Image offScreenImage=createImage(size().width,size().height);
       //用方法createImage创建Image对象
       Graphics offScreenGC=offScreenImage.getGraphics(); //获取Graphics对象
       offScreenGC.drawImage(picture,0,0,this); //显示非屏幕图像
      }
      public void paint(Graphics g)
      {
       if(ImageLoaded)
       {
        g.drawImage(picture,0,0,null); //显示图像,第四参数为null,不是this
        showStatus("Done");
       }
       else
        showStatus("Loading image");
      }
      public boolean imageUpdate(Image img,int infoflags,int x,int y,int w,int h)
      {
       if(infoflags= =ALLBITS)
       {
        imageLoaded=true;
        repaint();
        return false;
       }
       else
        reture true;
      }
    }

       分析该Applet的init方法可知,该方法首先定义了一个名为offScreenImage的Image对象并赋予其createImage方法的返回值,然后创建了一个名为offScreenGC的Graphics对象并赋予其图形环境——非屏幕图像将由它来产生。因为这里画的是非屏幕图像,所以Applet窗口不会有图像显示。

       每当Applet调用drawImage方法时,drawImage将创建一个调用imageUpdate方法的线程。Applet可以在imageUpdate方法里测定图像已有装入内存多少。drawImage创建的线程不断调用imageUpdate方法,直到该方法返回false为止。

       imageUpdate方法的第二个参数infoflags使Applet能够知道图像装入内存的情况。该参数等于ImageLoaded设置为true并调用repaint方法重画Applet窗口。该方法最终返回false,防止drawImage的执行线程再次调用imageUpdate方法。

       该Applet在paint方法里的操作是由ImageLoaded变量控制的。当该变量变为true时,paint方法便调用drawImage方法显示出图像。paint方法调用drawImage方法时把null作为第四参数,这样可防止drawImage调用imageUpdate方法。因为这时图像已装入内存,所以图像在Applet窗口的显示可一气呵成。
     
    *