支持显示元素拖动的用户图形界面的框架实现
看看我们框架中的绘制。在拖动时要动态地进行界面更新,主要思路是在鼠标按下及拖动时改变当前作用的显示单元的坐标位置,再调用 repaint() 进行界面的刷新。通过上面的 AWT 绘制介绍,可以看出,我们要做的第一个优化工作就是在DemoPane 中重写 update() 方法,也就是不对当前区域进行清除工作,而直接进行绘制。当然这要求我们要对整个区域进行重绘,否则一般情况下局部重绘会导致不正确的显示。update()方法如下:
2 paint(g) ;
3 }
4
在DemoPane中,用backImage表示显示区域的背景图片,以一个数组保存所有加入的显示单元的引用。前面已经说过,对于 DisplayUnit,我们重写了它的 paint 方法,那么在DemoPane的paint()方法中,首先绘制背景,然后对加入的每一个 DisplayUnit 调用其paint() 就可以完成显示。DemoPane的 paint() 方法代码如下:
2 g.drawImage(backImage, 0, 0,width, height, this);
3 for ( int i=0 ; i<nunits ; i++ ) {
4 getUnit(i).paint(g) ;
5 }
6 }
7
但这个方法实现的结果还是会让人感觉到闪烁,其原因是我们现在是直接对代表屏幕对象的Graphics 对象进行操作,而这个操作是比较费时的操作。解决的办法是采用“双缓冲”,即我们创建一个绘制缓冲区,以 bufImage 表示,先将主要的图形元素一个一个地绘制到此缓冲图像上,再将此缓冲图像一次性绘到代表屏幕的 Graphics 对象,即 paint() 方法传入的“g”上。则代码改变为:
2 bufImage = prepareBufImage(); // 准备缓冲图像
3 g.drawImage(bufImage, 0, 0,width, height, this);
4 }
5
再做进一步的考察我们可以发现,我们在对某一选中的显示单元进行拖动操作时,背景和其它所有的显示单元都没有改变,也就是说只有当前正在操作的显示单元需要重绘。我们将正在进行拖动操作的显示单元称为“活动单元”,那么在拖动时将活动单元与其它所有元素(包括背景和其它显示单元)分开绘制,则可大大提高绘制效率,减少无用操作。在程序中我们通过DemoPane的方法getUnit(index)可以获得相应显示单元,而以activeIndex记录当前的活动单元,则经过再次绘制优化的代码如下:
2 boolean redraw=false ;
3 if (lastActIndex!=activeIndex) { // lastActIndex代表上次的活动单元
4 redraw=true ; //与上次活动单元不一致,说明缓冲图像需要全部重绘
5 lastActIndex = activeIndex ;
6 }
7 if (redraw) {
8 Graphics g = bufImage.getGraphics() ;
9 g.clearRect(0,0,width,height) ;
10 g.drawImage(backImage, 0,0,width,height,this) ;
11 for ( int i=0 ; i<nunits ; i++ ) {
12 if (i!=activeIndex) {
13 getUnit(i).paint(g) ;
14 }
15 }
16 }
17 return bufImage ;
18 }
19 public void paint(Graphics g) {
20 bufImage = prepareBufImage() ;
21 g.drawImage(bufImage, 0,0,getSize().width,getSize().height,this) ;
22 DisplayUnit u = getUnit(activeIndex) ;
23 if (u!=null) {
24 u.paint(g) ;
25 }
26 }
27
绘制缓冲及优化如图所示。
