【发布时间】:2011-07-26 02:57:40
【问题描述】:
我已经研究了几个月,到目前为止,这是我想出的最好的。
结构(在 EDT 之外渲染)没有争议,因为我们的应用程序以这种方式运行并且不会被重写。该应用程序有一个布局模型和一个脚本模型,它们集成在一起并驱动渲染,因此必须在 AWT 绘制模型之外执行渲染。
我想要达到的是执行自定义渲染的最佳且可靠的方法。
以下 SSCCE 非常适合我们。但是,在调整帧大小时,它有两个缺点:
- 偶尔会出现闪烁,尤其是在快速调整大小时
- 从paint() 调用中调用resize(此处通过checkSize)的“平滑调整大小”hack 仅适用于扩展。缩小框架时,它通常在释放鼠标按钮之前不会渲染
- 此外,但在这里不那么明显,它确实会偶尔引发 IllegalStateExceptions - 是否可以简单地捕获/忽略这些?
关于这是否是在 EDT 之外发生的自定义渲染路径的最佳方法的输入也很有用。我已经尝试了最多,并进行了相当广泛的研究。这种组合(后缓冲图像、双缓冲策略)似乎效果最好。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferStrategy;
public class SmoothResize extends Frame implements ComponentListener, MouseMotionListener {
public SmoothResize() {
addComponentListener(this);
addMouseMotionListener(this);
}
private boolean sizeChanged = false;
private Dimension old = new Dimension(0, 0);
private synchronized void checkSize(String source) {
int width = getWidth();
int height = getHeight();
if (old.width == width && old.height == height)
return;
sizeChanged = true;
String type =
(old.width > width && old.height > height) ? "shrink" :
(old.width < width && old.height < height) ? "expand" : "resize";
System.out.println(source + " reports " + type + ": "+getWidth()+", "+getHeight());
old.setSize(width, height);
}
public void componentResized(ComponentEvent arg0) { checkSize("componentResized"); }
public void mouseMoved(MouseEvent e) { checkSize("mouseMoved"); }
public void paint(Graphics g) { checkSize("paint"); }
public void update(Graphics g) { paint(g); }
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
private synchronized void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy==null || !sizeChanged) return;
sizeChanged = false;
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
System.out.println("render");
Graphics draw = strategy.getDrawGraphics();
Insets i = getInsets();
int w = getWidth()-i.left-i.right;
int h = getHeight()-i.top-i.bottom;
draw.setColor(Color.YELLOW);
draw.fillRect(i.left, i.top+(h/2), w/2, h/2);
draw.fillRect(i.left+(w/2), i.top, w/2, h/2);
draw.setColor(Color.BLACK);
draw.fillRect(i.left, i.top, w/2, h/2);
draw.fillRect(i.left+(w/2), i.top+(h/2), w/2, h/2);
draw.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
public static void main(String[] args) {
Toolkit.getDefaultToolkit().setDynamicLayout(true);
System.setProperty("sun.awt.noerasebackground", "true");
SmoothResize srtest = new SmoothResize();
//srtest.setIgnoreRepaint(true);
srtest.setSize(100, 100);
srtest.setVisible(true);
while (true) {
srtest.render();
}
}
public void componentHidden(ComponentEvent arg0) { }
public void componentMoved(ComponentEvent arg0) { }
public void componentShown(ComponentEvent arg0) { }
public void mouseDragged(MouseEvent e) { }
}
【问题讨论】:
-
@trashgod:具体是哪些适配器,我不知道使用适配器与使用监听器相比有什么好处 - 或者它是一种编码练习?
-
ComponentListener->ComponentAdapter等。减少空实现带来的混乱。 -
@trashgod:Java 中没有多重继承,所以必须使用匿名类。事实上,我们的实现使用了那些空的实现,所以它是从一些函数派生的。 :)
-
有道理,但适配器是抽象的;具有命名子类型的组合是常见的approach。这是避免leaking
this的一种方法。