如何在没有闪烁的情况下调整Swing JWindow的大小?

wil*_*roz 24 java swing resize flicker jwindow

我正在尝试基于a创建自定义UI JWindow,以便选择要共享的屏幕区域.我已经扩展JWindow并添加了代码以使其可调整大小并使用"切出"窗口的中心AWTUtilities.setWindowShape().

当运行代码时,我正在经历闪烁,因为窗口在负x和y方向上调整大小,即向上和向左.似乎正在发生的是在更新组件之前调整窗口大小并绘制窗口.下面是代码的简化版本.运行时,顶部面板可用于向上和向左调整窗口大小.窗口的背景设置为绿色,以清楚显示我不想显示的像素的位置.

编辑:改进代码以使用a正确地塑造窗口,ComponentListener并在底部添加虚拟组件以进一步说明闪烁(也更新了屏幕截图).

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Area;

import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;

import com.sun.awt.AWTUtilities;

public class FlickerWindow extends JWindow implements MouseListener, MouseMotionListener{

    JPanel controlPanel;
    JPanel outlinePanel;
    int mouseX, mouseY;
    Rectangle windowRect;
    Rectangle cutoutRect;
    Area windowArea;

    public static void main(String[] args) {
        FlickerWindow fw = new FlickerWindow();
    }

    public FlickerWindow() {
        super();
        setLayout(new BorderLayout());
        setBounds(500, 500, 200, 200);
        setBackground(Color.GREEN);

        controlPanel = new JPanel();
        controlPanel.setBackground(Color.GRAY);
        controlPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
        controlPanel.addMouseListener(this);
        controlPanel.addMouseMotionListener(this);

        outlinePanel = new JPanel();
        outlinePanel.setBackground(Color.BLUE);
        outlinePanel.setBorder(new CompoundBorder(new EmptyBorder(2,2,2,2), new LineBorder(Color.RED, 1)));

        add(outlinePanel, BorderLayout.CENTER);
        add(controlPanel, BorderLayout.NORTH);
        add(new JButton("Dummy button"), BorderLayout.SOUTH);
        setVisible(true);
        setShape();

        addComponentListener(new ComponentAdapter() {           
            @Override
            public void componentResized(ComponentEvent e) {
                setShape();
            }});
    }


    public void paint(Graphics g) {
        // un-comment or breakpoint here to see window updates more clearly
        //try {Thread.sleep(10);} catch (Exception e) {}
        super.paint(g);
    }

    public void setShape() {
        Rectangle bounds = getBounds();
        Rectangle outlineBounds = outlinePanel.getBounds();
        Area newShape = new Area (new Rectangle(0, 0, bounds.width, bounds.height));
        newShape.subtract(new Area(new Rectangle(3, outlineBounds.y + 3, outlineBounds.width - 6, outlineBounds.height - 6)));
        setSize(bounds.width, bounds.height);
        AWTUtilities.setWindowShape(this, newShape);
    }

    public void mouseDragged(MouseEvent e) {
        int dx = e.getXOnScreen() - mouseX;
        int dy = e.getYOnScreen() - mouseY;

        Rectangle newBounds = getBounds();
        newBounds.translate(dx, dy);
        newBounds.width -= dx;
        newBounds.height -= dy;

        mouseX = e.getXOnScreen();
        mouseY = e.getYOnScreen();

        setBounds(newBounds);
    }

    public void mousePressed(MouseEvent e) {
        mouseX = e.getXOnScreen();
        mouseY = e.getYOnScreen();
    }

    public void mouseMoved(MouseEvent e) {}
    public void mouseClicked(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
}
Run Code Online (Sandbox Code Playgroud)

重写的paint()方法可以用作断点,Thread.sleep()也可以在那里取消注释,以便在更新时提供更清晰的更新视图.

我的问题似乎源于setBounds()在布局之前导致窗口被绘制到屏幕的方法.


调整大小之前的窗口,因为它应该看起来:

替代文字


调整大小(向上和向左)时的窗口,如在重写paint()方法的断点处所示):

替代文字


调整大小时的窗口(向下和向右),在重写paint()方法的断点处看到):

替代文字


这些屏幕截图是在激进的鼠标拖动动作期间拍摄的,但即使对于更温和的鼠标拖动,闪烁也变得非常烦人.

调整大小到更大屏幕截图的绿色区域显示在任何绘制/布局完成之前绘制的新背景,它似乎发生在底层ComponentPeer或本机窗口管理器中."调整大小"屏幕截图上的蓝色区域显示了JPanel背景被推入视图但现在已过时.这发生在Linux(Ubuntu)和Windows XP下.

有没有人找到一种方法在对屏幕进行任何更改之前导致WindowJWindow调整后台缓冲区的大小,从而避免这种闪烁效应?也许有一个java.awt....系统属性可以设置为避免这种情况,但我找不到一个.


编辑#2:注释掉调用AWTUtilities.setWindowShape()(并可选择取消注释该Thread.sleep(10)paint()),然后积极地拖动顶部面板,以便清楚地看到闪烁的性质.

编辑#3:是否有人能够在Windows 7或Mac OSX上的Sun Java下测试此行为?

Ale*_*exR 1

我刚刚尝试过你的例子。调整窗口大小时我确实看到了一些小的闪烁。我尝试替换从以下位置开始setBounds()和结束的代码片段:AWTUtilities.setWindowShape(this, newShape);

setSize(newBounds.width, newBounds.height);
Run Code Online (Sandbox Code Playgroud)

然后看到闪烁消失了。因此,我建议您使用此解决方案,除非您有任何特殊原因要使用setBounds.