如何在不闪烁的情况下调整 Swing JWindow 的大小?
Posted
技术标签:
【中文标题】如何在不闪烁的情况下调整 Swing JWindow 的大小?【英文标题】:How can a Swing JWindow be resized without flickering? 【发布时间】:2011-05-18 11:25:35 【问题描述】:我正在尝试基于 JWindow
制作自定义 UI,以便选择要共享的屏幕区域。我扩展了JWindow
并添加了代码以使其可调整大小并使用AWTUtilities.setWindowShape()
“切掉”窗口的中心。
运行代码时,当窗口在负 x 和 y 方向(即向上和向左)调整大小时,我遇到了闪烁。似乎正在发生的事情是在更新组件之前调整窗口大小并绘制。下面是代码的简化版本。运行时,顶部面板可用于向上和向左调整窗口大小。窗口的背景设置为绿色,以明确我不想显示的像素在哪里。
编辑:改进了代码以使用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
或本机窗口管理器中。 “调整为更小”屏幕截图上的蓝色区域显示 JPanel
的背景已被推入视野,但现在已过时。这发生在 Linux(Ubuntu) 和 Windows XP 下。
有没有人找到一种方法让Window
或JWindow
在对屏幕进行任何更改之前调整为后台缓冲区,从而避免这种闪烁效果?也许可以设置一个java.awt....
系统属性来避免这种情况,但我找不到。
编辑 #2: 注释掉对AWTUtilities.setWindowShape()
的调用(并可选择取消注释paint()
中的Thread.sleep(10)
行)然后积极拖动顶部面板以便清楚地看到性质的闪烁。
编辑 #3: 是否有人能够在 Windows 7 或 Mac OSX 上的 Sun Java 下测试此行为?
【问题讨论】:
@willjcroz:+1,非常好的问题。对所有人:请不在没有ALSO的情况下支持任何答案。 我也想知道同一个问题的答案——多年来我一直讨厌它,但从来没有找到一个解决办法。在 Windows 7 上测试,虽然我没有得到与你完全相同的效果,但我得到的效果非常相似。每个 Java 应用程序都会看到调整大小的人工制品,而不仅仅是您的示例。奇怪的是,移动窗口没问题,只是调整大小很糟糕(可能是因为 Windows 7 自己处理移动窗口并且不会在 Java 中触发调整大小 - 如果我没记错的话,与 Windows XP 不同)。 @Domchi:感谢您确认它发生在 Windows 7 上 :-)。在 XP 上移动窗口看起来很流畅,没有重绘。也许您指的是 XP 上的窗口“修复”很差。合成 WM(如在 Linux 上的 Vista/Win7、OSX 和 Compiz 等中看到的)处理以前被遮盖的窗口部分的“修复”而不请求重新绘制,这就是我认为 XP 可能会失败的地方。 @willjcroz:是的,你是对的 - 我指的是 Win7 缓存当前窗口图片的事实,所以如果 EDT 因任何原因卡住,你仍然可以移动窗口并拥有内部可见,而不是在 XP 上移动窗口后立即显示灰色矩形。 【参考方案1】:我承认这不是一个特别有用的答案,但了解 Swing 究竟是什么可能会有所帮助。
你看,Swing 做所有自己的工作,实际上没有从操作系统中获得一点空间。所有的绘图、小部件等都是 Java 代码。与其说它运行缓慢,不如说它在没有 2D 显卡加速和操作系统渲染技巧的情况下运行。
还记得 DirectDraw 吗?现在一切都有了,而且窗户操作非常顺利。但是,如果您曾经因为某种原因(例如,没有安装驱动程序的 XP 安装)拿到一台没有它的计算机,您会注意到正是这种类型的减速。
使用 Swing,因为它管理自己的所有空间,操作系统无法使用任何这些渲染技巧来帮助您。
有人可能会提出一些优化来解决您计算机上的问题,但我担心它并不能真正解决基本问题 - Swing 很慢而且无法变得更快。
您应该研究原生工具包。 AWT 还可以,但缺少很多小部件/等。它是原生的,内置的,所以如果你需要的话,它应该足够快。我偏爱 SWT,这是 Eclipse、Vuze(以及其他)使用的。它结合了 AWT 的原生特性与 Swing 的易用性和特性,当然可以在任何地方运行。
编辑:在阅读了更多的 cmets 之后,您非常清楚您完全了解窗口是如何发生的 - 我不想表现得居高临下。不仅如此,您对调整大小更感兴趣,我的评论与此无关。我仍然推荐 SWT,因为它是本机代码且速度更快,但与上面的答案不同。
【讨论】:
感谢您的想法,别担心我没有感到光顾;)。是的,SWT 看起来确实是为一些未来项目解决这个问题的一种方法。但是由于这个项目的最后期限迫在眉睫,而且这个 UI 方面是在一个需要快速加载时间的小程序中的事实,SWT 不提供我现在的解决方案。 这个问题似乎实际上存在于 Swing 的JWindow
底层的 AWT 层中。具体来说,本机对等点ComponentPeer
及其相应的本机代码无法要求子组件在对屏幕设备进行任何更新之前将其自身绘制到提供的缓冲区中。因此,这似乎是所有*** AWT 和(重量级)Swing 组件的问题。【参考方案2】:
我刚刚尝试了您的示例。调整窗口大小时,我真的看到了一些小闪烁。我试图用
替换从setBounds()
开始到AWTUtilities.setWindowShape(this, newShape);
结束的代码片段
setSize(newBounds.width, newBounds.height);
并看到轻弹消失了。因此,除非您有任何特殊原因使用setBounds
,否则我建议您使用此解决方案。
【讨论】:
感谢您的意见。为了获得正确的调整大小行为(此示例表示向上和向左调整大小的用例),我需要移动窗口并调整其大小(JWindow
的左上角原点需要更改)因此使用setBounds()
.
@AlexR: 好的,但是很多其他平台 AWTUtilities.setWindowShape(this, newShape);不工作只是尝试和失败。 Fedora linux/Ubuntu 和 OpenJDK。【参考方案3】:
另一种方法可能是在绘画之前等待用户完成拖动/调整大小。也许使用边界框向用户显示窗口的大小,直到他们的调整大小事件完成。
我注意到很多窗口应用程序要么采用这种方法,要么处理闪烁。几乎所有我尝试过的积极调整大小的应用程序都有相同的问题(非 Java),所以问题可能出在图形子系统而不是 Swing 本身。
【讨论】:
是的,我目前正在考虑边界框方法,唯一的问题是用户需要精确度(选择屏幕的确切区域),因此不希望在两种状态之间更改界面。至于其他应用程序,您是否尝试过在 Eclipse 中调整大小?尽管它的布局复杂,但对我来说它的闪烁为零,生涩是的,但无闪烁!这让我认为我在学习和使用 SWT 时可能会更幸运。 是的,我想生涩/闪烁对我来说是其中之一。无论哪种方式,看到任何重绘都是不可取的。我在写评论时正在调整我的 IDE 的大小——所以我想我们在同一页上。我在工作中使用一些 SWT/RCP 应用程序,尽管它提供了一个非常好的框架,但在可扩展性方面还有很多不足之处。它使用的事件模型也不是很友好。您是否尝试过查看 NetBeans 平台?我认为这是一个值得研究的基于 Swing 的框架。 PS:我正在使用一台旧机器来测试你的机器并尝试修复(Windows XP 的 2.0 单核)——当我进行任何调整大小时,我仍然可以看到闪烁/抖动。跨度>以上是关于如何在不闪烁的情况下调整 Swing JWindow 的大小?的主要内容,如果未能解决你的问题,请参考以下文章