我们先讲第一个,如何把图片导入程序中,在这里我们用的是Toolkit的方法createImage,它确实是一个很实用的方法,它是一个重载的方法,可以传入很多种参数,除了可以传入URL之处,还可以有如下的重载方法:
|
|
|
createImage (byte[] imagedata, int imageoffset, int imagelength) 创建一幅图像,该图像对存储在指定字节数组中指定偏移量和长度处的图像进行解码。
|
|
|
|
|
|
|
有一点需要注意的是,它的createImage是一个异步的方法,也就是说我们调用了这个方法以后,程序会立即返回,并不会等到图片完全加载进内存之后才返回,所以当我们用这种方法加载比较大的图片的时候,如果图片又没有完全进入内存,而我们却去draw它,这个时候就会出现撕裂的情况,大大影响了我们程序的性能以及可玩性,那怎么办呢?
办法有两种,一种是像我们在程序里实现的一样,用一个媒体跟踪器来跟踪我们要加载的图片,然后调用一个同步方法等待它们全部加载进入内存之后才继续往下运行,这样就可以保存在初始化以后,所需要用到的图片确实都全部加载进内存了,这样画的时候,才能保证效果。如下所示:
MediaTracker mt = new MediaTracker(this);
…
mt.addImage(star[i], i);
...
mt.waitForAll();
我们生成一个媒体跟踪器,然后把我们需要跟踪的图片放到里面去,然后等待所有的图片加载,mt.waitForAll()方法是会抛出一个InterruptedException的方法。我们需要捕获处理它。
另外一种办法就是利用javax.imageio.ImageIO的方法,它的read方法可以同步的把图片完全读入内存,某些情况下这是更方便的方法,因为使用它免去了加媒体跟踪器的代码。javax.imageio.ImageIO的read方法也有很多重载的版本,它的方法如下:
|
read (File input) 返回一个 BufferedImage ,作为使用 ImageReader (它是从当前已注册 ImageReader 中自动选择的)解码所提供 File 的结果。
|
|
read (ImageInputStream stream) 返回一个 BufferedImage ,作为使用 ImageReader (它是从当前已注册 ImageReader 中自动选择的)解码所提供 ImageInputStream 的结果。
|
|
read (InputStream input) 返回一个 BufferedImage ,作为使用 ImageReader (它是从当前已注册 ImageReader 中自动选择的)解码所提供 InputStream 的结果。
|
|
read (URL input) 返回一个 BufferedImage ,作为使用 ImageReader (它是从当前已注册 ImageReader 中自动选择的)解码所提供 URL 的结果。
|
所以我们用read方法的话,会显得更加方一些,但是为什么我们在程序当中不使用它,而使用再加繁琐的Toolkit加上MediaTracker的方法呢?因为ImageIO读入内存的图片在呈现的过程中会有如下缺点:
1,当加载的图片是动态的gif图片的时候,图片在呈现的时候,将没有动画效果,它只会读取第一帧。
2,当加载的图片是半透明的时候,图片在呈现的时候,会比用Toolkit加载进来的图片更耗CPU。
所以我们选择了用Toolkit而不是ImageIO,当我们没有用到以上两种情况的图片的时候,是完全可以用ImageIO来加载图片的。
图片导入程序中的问题解决了,我们现在来看一看如何把图片打包进JAR包,然后又如何在程序运行的时候把JAR包里面的资源提取出来。在这里我们用的是一个很有用的方法getResource(),它是定义在Class类里面的,当我们把我们的的图片提取出来的时候,可以用相对路径也可以用绝对路来来提取,当我们用相对路径的时候,路径就是相对于当前的class文件所在目录的路径,如果是用绝对路径的时候,路径就是从JAR内部的根目录开始算的。把图片等一些资源打入JAR包有很多好处,一是可以实现资源的初步隐藏,二是可以利用JAR的特性对文件进行一些压缩,因为JAR包就是一个压缩包,只不过后缀名改了而已。
下面我们再来看一下paintComponent方法,它是一个重写的方法,它重写了父类JPanel里面的paintComponent方法,一般来说,当我们要绘制一些内容的时候,都是采用重写此方法的办法,在以前AWT的编程中,对重量型组件进行重写,一般重写的是paint方法,所以在用轻量级组件的时候,这一点要注意,最好不要再重写paint方法了,而是改为重写paintComponent。在它里面我们看到如下三句:
Graphics2D gd = (Graphics2D) g;
gd.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gd.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
它的意思就是设置图形上下文在绘制的时候,要注意哪些方面,我们可以利用这个方法给图形上下文一些提示。在本文里面我们提示了两点,一点是图片抗锯齿打开,二是文本抗据齿我们也打开,除了这两个之外还要很多提示我们可以设置的,在兴趣的朋友可以查看java.awt.RenderingHints这个类。利用这个特性,我们可以使我们呈现的界面更加完美,不过完美是需要代价的,呈现的越清晰越完美就越需要更多的CPU的运算,所以当电脑的性能不太好的时候,我们可以把这两个提示去掉,让JVM自行把握绘制的质量。
还有一点我们要注意的地方,那就是我们调用repaint的地方。在我们需要重绘的时候,我们可以调用repaint方法,它会发送一个重绘的请求,那个会把这个请求放到重绘线程里面去,在我们调用repaint的时候,有很重要的一点就是尽量不要去调用repaint的默认方法,而要调用repaint(int x,int y,int width,int height)方法,因为它只会请求重绘某一个区域,而repaint()则会重绘整个区域,所以为了性能着想,最好不要重绘整个区域,当你开发了有关JAVA2D的程序后,你会发现,程序的大部份CPU都耗在重绘上面,所以优化重绘区域对于优化整个程序的性能是很有效果的。
对于ClientView的讲解就到这里,对于ClientControl,ClientModel,登录窗口以及播放声音的实现,各位可以自行下载源代码查看,欢迎大家参与讨论。本工程是用NetBeans开发的,NetBeans工程的源代码请点击这里下载。
4. 对未来的展望
JAVA经过十几年的发展,到今天已经成为世界上使用人数最多的语言,这得益于它的平台所提供的功能
的全面,从机顶盒到手机到电脑桌面到企业级的大型应用,JAVA都可以做到。SUN一直都很看重JAVA在桌面上的成绩,每次版本的更新,都会花大力气在构造桌面应用的程序包上,最近又推出了javafx来丰富桌面的开发,相信在JAVA开源以后,有这么多JAVA爱好者以及开源社区的支持,JAVA的明天一定会更加美好。