[英]How can a Swing JWindow be resized without flickering?
我正在嘗試基於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下測試此行為?
我承認這不是一個特別有用的答案,但了解Swing究竟是什么可能會有所幫助。
看看,Swing做了所有自己的工作,實際上沒有從操作系統中獲得一點空間。 所有繪圖,小部件等都是Java代碼。 這並不是因為它在沒有2D圖形卡加速和操作系統渲染技巧的情況下運行的速度很慢。
還記得DirectDraw嗎? 現在一切都有它,窗口操作是黃油般光滑的。 但是,如果你抓住這沒有它由於某種原因,計算機(比方說,一個XP安裝沒有驅動程序)你注意到的正是這種類型的放緩。
使用Swing,因為它管理所有自己的空間,操作系統無法執行任何這些渲染技巧來幫助您。
有人可能會帶來一些優化來解決你的計算機上的問題,但我擔心它不會真正解決基本問題 - Swing很慢而且速度不快。
您應該查看本機工具包。 AWT沒關系,但缺少很多小部件/等等。 它是原生的,內置的,所以如果你需要的話它應該足夠快。 我偏愛SWT,這是Eclipse,Vuze(以及其他人)使用的東西。 它結合了AWT的本土特性和Swing的易用性和功能,當然可以隨處運行。
編輯 :在閱讀了一些你的評論之后很明顯,你完全理解窗口是如何發生的 - 我不想因為居高臨下而退縮。 不僅如此,而且你對調整大小更感興趣,我的評論沒有那么多。 我仍然推薦使用SWT,因為它的本機代碼更快,但這與上面的答案不同。
我剛試過你的例子。 在調整窗口大小時,我確實看到了一些小的輕彈。 我試圖從setBounds()
開始替換代碼片段,並在AWTUtilities.setWindowShape(this, newShape);
通過
setSize(newBounds.width, newBounds.height);
並看到輕彈消失了。 因此,除非您有任何特殊原因要使用setBounds
否則我建議您使用此解決方案。
另一種方法可能是等待用戶在繪畫之前完成拖動/調整大小。 也許使用邊界框向用戶顯示窗口大小,直到調整大小事件完成。
我注意到很多窗口應用程序要么采用這種方法,要么處理閃爍。 幾乎所有的應用程序我嘗試過像這樣的攻擊性調整具有相同的問題(非Java)所以問題可能只是圖形子系統而不是Swing本身。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.