一:加载延缓
相信大家都遇到这样的情况,一个系统需要加载一个模块,但是此模块的加载要耗费相当长的时间,因此,系统要显示一段"正在加载"的信息,同时将模块加载,在模块加载后,系统将"正在加载"的文字取消掉,并启动此模块,常见的例子有Netscape浏览器,JBuilder等大型软件.有时候加载一个图像也会造成时间的延迟,因此系统需要在放置图像的地方放一段文字信息,请用户用心等待。系统会在另一个线程中加载图像,而一旦图像加载完毕,就将等待信息换成该图像,这种做法叫做加载延缓,加载延缓显然是比较友好的用户界面的设计方案.
二:虚拟代理模式的应用
当一个真实主题对象的加载需要耗费资源时,一个虚拟代理对象可以代理真实对象接受请求,一旦接到请求,代理对象马上打出一段"正在加载"的信息,并在适当的时候加载真实主题对象,也就是模块或者图像.本章将给出一个加载图像的例子,由于图像的加载会耗费一定的资源,因此,要求设计一个虚拟代理对象,以替代图像对象接受客户端的请求,当虚拟代理对象接到请求后,会按照预定的逻辑首先显示一段等待信息,然后在另一个线程中加载图像.当图像加载完成后,主线程会决定将图像显示出来.
三:系统的设计
系统由一个JFrame对象,一个Icon对象以及此Icon对象的虚拟代理对象组成.系统的客户端对象调用代理ImageIconProxy对象,而此代理则负责将调用传递给真实主题角色,即一个ImageIcon对象。客户端调用代理ImageIconProxy对象时,此代理对象调用所传入的Graphics对象的drawRec()方法显示一段"Loading photo.."的等待信息,然后代理对象创建一个内部线程对象,并将之传入SwingUtilities的invokeLater()方法中,让它负责加载真实主题,至此,代理对象成功地延迟了真实主题的加载,随后,代理主题认为显示真实主题的时机成熟了,于是将真实主题(也就是图像)显示出来.
下面是系统的源代码:
package cai.milenfan.basic.test;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
public class ImageIconProxy implements Icon{
private ImageIcon realIcon = null;
private String imageName;
private int width;
private int height;
boolean isIconCreated = false;
public ImageIconProxy(String imageName,int width,int height){
this.imageName = imageName;
this.width = width;
this.height = height;
}
public int getIconHeight() {
return realIcon.getIconHeight();
}
public int getIconWidth() {
return realIcon.getIconWidth();
}
//加载图像
public void paintIcon(final Component c, Graphics g, int x, int y) {
if(isIconCreated){
realIcon.paintIcon(c, g, x, y);
g.drawString("Java and Patterns by Milenfan", x 20, y 370);
}else{
g.drawRect(x, y, width-1, height-1);
g.drawString("Loading...", x 20, y 20);
//图像在另外一个线程中被加载
synchronized(this){
SwingUtilities.invokeLater(new Runnable(){
public void run() {
try {
//延缓图像的加载过程,以显示效果
Thread.currentThread().sleep(2000);
//将图像加载
realIcon = new ImageIcon(imageName);
isIconCreated = true;
} catch (InterruptedException e) {
e.printStackTrace();
}
//当图像被完全加载后,重新描绘视窗构件
c.repaint();//c要用final修饰
}});
}
}
}
}
package cai.milenfan.basic.test;
import javax.swing.Icon;
import javax.swing.JFrame;
import java.awt.Graphics;
import java.awt.Insets;
public class Client extends JFrame{
private static int IMAGE_WIDTH = 400;
private static int IMAGE_HEIGHT = 300;
private Icon imageIconProxy = null;
public Client(){
super("使用代理进行图像的延迟加载...");
imageIconProxy = new ImageIconProxy("c:/ZK-Logo.gif",IMAGE_WIDTH,IMAGE_HEIGHT);
setBounds(100,100,IMAGE_WIDTH 10,IMAGE_HEIGHT 30);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//置换java.awt.Container的方法
public void paint(Graphics g){
super.paint(g);
Insets insets = getInsets();
imageIconProxy.paintIcon(this, g, insets.left, insets.top);
}
static public void main(String[] args){
Client app = new Client();
app.show();
}
}
在例子中使用了SwingUtilities.invokeLater()方法开启一个独立的新线程,用以执行一个新的任务,这个新的任务就是首先休眠2秒钟,然后加载图像,在加载完成后重新描绘视窗.SwingUtilities另有一个与invokeLater()方法相似的方法---invokeAndWait(),后者与前者不同的是,它会将主线程的执行封锁住,直到新线程的任务完成为止.
这个模式挺实用的..