簡體   English   中英

如何在沒有閃爍的情況下調整Swing JWindow的大小?

[英]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下。

有沒有人找到一種方法使WindowJWindow在對屏幕進行任何更改之前調整大小到后台緩沖區,從而避免這種閃爍效應? 也許有一個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM