我已经看了好几个月了,到目前为止这是我提出的最好的。
结构(在EDT之外呈现)不值得辩论,因为我们的应用程序以这种方式运行,不会被重写。该应用程序具有布局模型和脚本模型,它们是集成和驱动渲染,因此渲染必须在AWT绘制模型之外执行。
我想要达到的是执行自定义渲染的最佳和可靠方式。
以下SSCCE对我们来说效果很好。但是,在帧大小调整期间,它有两个缺点:
同样有用的是输入这是否是在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) { }
}
答案 0 :(得分:3)
以下是使用外部线程执行所有工作的代码。它通过能够呈现实现Renderable
接口的任何内容来实现此目的。我用Swing和AWT(JFrame
和Frame
)对它进行了测试,它没有闪烁。请注意,如果您在JRootPane
上实现并将该窗格设置为JFrame
的根窗格,则会闪烁。这与组件的缓冲方式有关,如果您想要使用它,可以修复。
如果这仍然不是你想要的,那就说吧,我会再给它一次。这实际上很有趣,因为我已经完成了任何Java GUI工作。
无论如何,你走了:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class SmoothResize extends Frame implements Renderable {
public static void main(String[] args) {
Toolkit.getDefaultToolkit().setDynamicLayout(true);
System.setProperty("sun.awt.noerasebackground", "true");
SmoothResize srtest = new SmoothResize();
RenderThread renderThread = new RenderThread(srtest);
renderThread.start();
srtest.setSize(100, 100);
srtest.setVisible(true);
}
public SmoothResize() {
}
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
@Override
public Dimension getSize() {
return new Dimension(getWidth(), getHeight());
}
@Override
public Graphics acquireGraphics() {
return this.getGraphics();
}
}
class RenderThread extends Thread {
Renderable target;
Dimension last_size = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
public RenderThread(Renderable d) {
if (d == null) {
throw new NullPointerException("Drawable target cannot be null.");
}
target = d;
}
@Override
public void run() {
while (true) {
render(false);
}
}
private synchronized void render(boolean force) {
Dimension size;
do {
size = target.getSize();
if (size == null) {
return;
}
Graphics draw = target.acquireGraphics();
if (draw == null) {
return;
}
draw.setPaintMode();
int w = (int) (((double) (size.width)) / 2 + 0.5);
int h = (int) (((double) (size.height)) / 2 + 0.5);
draw.setColor(Color.YELLOW);
draw.fillRect(0, h, w, h);
draw.fillRect(w, 0, w, h);
draw.setColor(Color.BLACK);
draw.fillRect(0, 0, w, h);
draw.fillRect(w, h, w, h);
draw.dispose();
// Repeat the rendering if the target changed size
} while (!size.equals(target.getSize()));
}
}
interface Renderable {
public Graphics acquireGraphics();
public Dimension getSize();
}
答案 1 :(得分:2)
此答案留待此处参考,但不是正确答案,因为它在EDT主题内呈现。
这是一个有效的解决方案! :D基本上问题是在缩小后释放鼠标之前,ComponentResized没有被适当调用。此外,由于paint和checkSize方法是同步的,因此在极少数情况下它们可以相互排斥。修复是覆盖Frame类中的validate方法。如果Frame更改状态(包括收缩和增长),则始终会调用此方法。所以我们只需要检查验证中的大小,我们实际上完全可以忘记使用ComponentResized方法。
所以,这里是按原样编译的工作代码。我更改了一些变量名称以提高我的个人可读性。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
import java.awt.Frame;
public class SmoothResize extends Frame {
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);
}
public SmoothResize() {
render();
}
private Dimension old_size = new Dimension(0, 0);
private Dimension new_size = new Dimension(0, 0);
public void validate() {
super.validate();
new_size.width = getWidth();
new_size.height = getHeight();
if (old_size.equals(new_size)) {
return;
} else {
render();
}
}
public void paint(Graphics g) {
validate();
}
public void update(Graphics g) {
paint(g);
}
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
protected synchronized void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null) {
return;
}
// 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 {
Graphics draw = strategy.getDrawGraphics();
Insets i = getInsets();
int w = (int)(((double)(getWidth() - i.left - i.right))/2+0.5);
int h = (int)(((double)(getHeight() - i.top - i.bottom))/2+0.5);
draw.setColor(Color.YELLOW);
draw.fillRect(i.left, i.top + h, w,h);
draw.fillRect(i.left + w, i.top, w,h);
draw.setColor(Color.BLACK);
draw.fillRect(i.left, i.top, w, h);
draw.fillRect(i.left + w, i.top + h, w,h);
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());
}
}
我希望这适合你!
此外,最后一次编辑,我更改了逻辑三元操作以选择缩小或扩展String。最后的比较是不必要的,因为对于所讨论的比较,值可以大于,小于或等于彼此。没有其他可能无法生成 NullPointerException
。
受影响的文本不再相关,因为我已完全删除了整个方法。我指出我对原帖作为评论所做的其他更改。