AWT自定义渲染 - 捕获平滑调整大小并消除调整大小闪烁

Cha*_*win 6 java resize awt frame

我已经看了好几个月了,到目前为止这是我提出的最好的.

由于我们的应用程序以这种方式运行并且不会被重写,因此结构(在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) { }
}
Run Code Online (Sandbox Code Playgroud)

Jos*_*osh 3

下面是使用外部线程完成所有工作进行渲染的代码。它通过能够渲染实现该Renderable接口的任何内容来实现这一点。JFrame我已经用 Swing 和 AWT(和)对此进行了测试Frame,并且它没有闪烁。JRootPane请注意,如果您在 a 上实现并将该窗格设置为 的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();
}
Run Code Online (Sandbox Code Playgroud)