我正在尝试基于 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) {}
}
被覆盖的paint()
方法可以用作断点,Thread.sleep()
也可以在此处取消注释,以便在更新发生时提供更清晰的视图。
我的问题似乎源于setBounds()
导致窗口在布局之前被绘制到屏幕上的方法。
调整大小之前的窗口,应如下所示:
调整大小时的窗口(向上和向左),如在覆盖paint()
方法的断点处所示):
调整大小时的窗口更小(向下和向右),如在覆盖方法的断点处所见paint()
):
当然,这些屏幕截图是在激进的鼠标拖动运动期间拍摄的,但即使是更温和的鼠标拖动,闪烁也会变得非常烦人。
调整为更大屏幕截图的绿色区域显示了在任何绘画/布局完成之前绘制的新背景,它似乎发生在底层ComponentPeer
或本机窗口管理器中。'resize to small' 屏幕截图上的蓝色区域显示JPanel
' 背景被推入视野,但现在已经过时了。这发生在 Linux(Ubuntu) 和 Windows XP 下。
在对屏幕进行任何更改之前,有没有人找到一种方法来导致Window
或JWindow
调整后缓冲区的大小,从而避免这种闪烁效果?也许java.awt....
可以设置一个系统属性来避免这种情况,但我找不到。
编辑#2:注释掉对的调用AWTUtilities.setWindowShape()
(并且可以选择取消注释中的Thread.sleep(10)
行paint()
)然后积极地拖动顶部面板以清楚地看到闪烁的性质。
编辑#3:是否有人能够在 Windows 7 或 Mac OSX 上的 Sun Java 下测试此行为?