簡體   English   中英

無法在Swing中正確繪制自定義組件

[英]Can't paint a custom component in Swing correctly

我知道Swing非常差,所以很抱歉這個愚蠢的問題。 我需要做的是使我的自定義組件(它們是JPanel祖先)具有從緩沖區( BufferedImage實例)執行的繪制。 這是一個要求,因為繪制過程可能非常繁瑣,所以重寫了PaintComponent方法以從該緩沖區繪制並立即返回(但是如果您知道更好的方法來告訴Java,第二次消耗對象的時間不應超過200-300次)所有的CPU-我將不勝感激,也許有一些公共方法可以將Graphics上下文的區域標記為“未更改”,因此Swing不會嘗試重新繪制它們)。

我的代碼有什么問題,它根本不會在重疊的JPanels上繪制數據。 我已經提出了一個可以重現該問題核心的例子。 請參見下面的代碼:

public class Dr extends JPanel {

    public Dr(int x, int y, int w, int h, int fw, int fh) {
        left = x;
        top = y;
        width = w;
        height = h;
        full_width = fw;
        full_height = fh;
        setOpaque(true);
    }

    public void draw() {
        if (buffer == null && width > 0 && height > 0) {
            buffer = new BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_ARGB);
        }
        if (buffer == null) return;
        Graphics g = buffer.getGraphics();
        setBounds(0, 0, full_width, full_height);
        Graphics2D g2d = (Graphics2D)g;
        g2d.setBackground(Color.WHITE);
        g2d.clearRect(0, 0, full_width, full_height);

        g2d.setColor(new Color(255, 0, 80, 128));

        g2d.fillRect(left, top, width, height);
        System.out.println(left + ", " +  top);
    }

    @Override
    public void repaint() {
        draw();
    }

    @Override
    public void paintComponent(Graphics g) {
        g.drawImage(buffer, 0, 0, null);
    }

    private BufferedImage buffer;

    private int left;
    private int top;
    private int width;
    private int height;
    private int full_width;
    private int full_height;
}

這是一個示例類,除了保存他的坐標以繪制完整尺寸(我想等於其父尺寸)外,什么也不做,這又是一個要求,因為我需要支持可見的溢出內容才能顯示)繪制一個紅色矩形,其不透明度為100%的一半。

這是使用它的外部代碼:

    Dr dr1 = new Dr(4, 4, width-10, 80, width-2, height-2);
    Dr dr2 = new Dr(4, 88, width-10, 80, width-2, height-2);
    root.add(dr1);
    root.add(dr2);
    dr1.setBounds(0, 0, width-2, height-2);
    dr2.setBounds(0, 0, width-2, height-2);
    dr1.draw();
    dr2.draw();

預期為:

想要什么

當我刪除緩沖區並直接繪制paintComponent方法時顯示的內容:

直接油漆

當我不填充背景時顯示的內容(這里的背景具有系統L&F顏色,但是我需要元素父級的顏色(在我的示例中是白色)

直接上漆,無背景填充

當我從緩沖區拖動時會顯示什么

在此處輸入圖片說明

最后一個完全模糊。 從右側和底部部分切除了紅色矩形,並從父面板的(0,0)坐標中剪切了整個圖像(包括白色背景)。 第二個對象根本不顯示。 控制台中的坐標絕對可以。

我覺得我與Swing有一些關系,要它在猜測要繪畫什么和不繪畫什么時告訴它不要執行其“魔術”。 但是我該怎么辦呢?

更新:

我發現,即使我在添加子組件之后不對其子集的draw()方法進行調用,它們仍會隱藏其父級的背景(這不應該發生,因為bacase JPanels應該是不透明的)。 因此,核心問題是我無法使我的Dr對象具有不透明的背景(例如,沒有背景),其中沒有任何內容被繪畫(這也解釋了為什么我只看到第一個矩形-Swing中的z順序似乎被反轉了) ,例如,最后添加的組件畫在底部,因為它離我們最遠。

因此,我“認為”您有一個根本性的誤解,那就是Swing的所有工作原理。

您似乎對布局管理系統如何工作,繪畫系統如何工作以及坐標系如何工作存在問題

讓我們從您的Dr組件開始。

您似乎想開發重疊的組件,這些組件可以在其下方顯示子組件,但是您可以使用setOpaque(true); ,這意味着該組件將無法看穿

這個...

public void draw() {
    if (buffer == null && width > 0 && height > 0) {
        buffer = new BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_ARGB);
    }
    if (buffer == null) return;
    Graphics g = buffer.getGraphics();
    setBounds(0, 0, full_width, full_height);
    Graphics2D g2d = (Graphics2D)g;
    g2d.setBackground(Color.WHITE);
    g2d.clearRect(0, 0, full_width, full_height);

    g2d.setColor(new Color(255, 0, 80, 128));

    g2d.fillRect(left, top, width, height);
    System.out.println(left + ", " +  top);
}

對我來說似乎很奇怪。 您將BufferedImage定義為height width ,然后使用full_widthfull_height填充它...在我看來,以另一種方式做起來更有意義

@Override
public void repaint() {
    draw();
}

好的,這是Swing中的重要課程,您無法控制繪畫過程,因此請勿嘗試。 Swing具有完善的記錄和建立良好的繪畫過程,可提供定義明確的掛鈎,您可以在其中插入自定義繪畫(即paintComponent )。 如果需要“控制”,則需要考慮使用BufferStrategy ,它將為您提供完全的控制以定義您自己的繪畫過程。

好的,答案是什么?

嗯,這不是那么簡單,因為我不確定100%知道我要解決的問題是什么。

但是,讓我們從Dr面板開始...

public class Dr extends JPanel {

    public Dr(int x, int y, int w, int h, int fw, int fh) {
        left = x;
        top = y;
        width = w;
        height = h;
        full_width = fw;
        full_height = fh;
        setOpaque(false);

        setBounds(x, y, fw, fh);
    }

    public void draw() {
        if (buffer == null && width > 0 && height > 0) {
            buffer = new BufferedImage(getWidth(), getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB);
        }
        Graphics g = buffer.getGraphics();
        Graphics2D g2d = (Graphics2D) g;

        g2d.setColor(new Color(255, 0, 80, 128));

        g2d.fillRect(0, 0, width, height);
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {
        draw();
        g.drawImage(buffer, 0, 0, this);
        g.setColor(Color.RED);
        g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
    }

    private BufferedImage buffer;

    private int left;
    private int top;
    private int width;
    private int height;
    private int full_width;
    private int full_height;
}

因此,在這里,我對其進行了更改,以便將面板定位在傳遞給構造函數的xy位置,並將其大小設置為fwfh屬性。

然后在draw方法中,我創建一個BufferedImage大小,該大小調整為組件的當前大小並進行繪制...基於widthheight屬性的內容...提出了有關為什么必須調整大小的問題,但是在那里是...

然后將緩沖區繪制到組件的頂部/左側位置,這一點很重要,Swing的坐標系基於組件本身,因此0x0始終是組件的頂部/左側角,與坐標系無關父容器。

ps-紅色矩形用於調試目的,您不需要它。

然后我用...

EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        }

        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(new JLayeredPane());
        frame.add(new Dr(10, 10, 180, 180, 200, 200));
        frame.add(new Dr(100, 100, 180, 180, 200, 200));
        frame.setSize(400, 400);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
});

瞧,重疊的組件...

重疊組件

現在,我強烈建議您停下來仔細閱讀以下內容:

而且,我的直覺是,您可能真的不想使用單獨的組件,而您想要的是一個可以繪制許多緩沖區的組件

我終於解決了這個問題。

問題出在我沒有在此處發布的代碼中。

我將我的JPanels Dr添加到Block JPanel中。 Block已重寫repaint方法以從緩沖區進行繪制,但在當前版本中未重寫paintComponent方法。 它還具有方法forceRepaint來重建緩沖區,而方法draw(Graphics g)draw()分別在內部緩沖區或傳遞的Graphics上下文上進行繪制。 paint方法也沒有被覆蓋。

結果,在我給孩子add調用之后,Swing在我的Block對象上調用了repaint 但是我的repaint版本只是更新了內部緩沖區,僅此repaint 屏幕上的結果沒有任何改變。

因此,這只是一個邏輯錯誤。

解決方案是在Block類中重新實現paintComponent的重寫版本,該類的實例是我在其中添加元素的root對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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