簡體   English   中英

JPanel什么時候繪制(或重繪)它的子組件?

[英]When does a JPanel paint (or repaint) its child components?

我有一個JButton,它使用自定義UI委托繪制(CustomButtonUI擴展BasicButtonUI)。 CustomButtonUI的paint()方法繪制帶有圓角“抗鋸齒”角的按鈕,使外觀盡可能“平滑”。

每當我將鼠標拖到按鈕上時,按鈕的“抗鋸齒”邊緣就會消失。 這使得按鈕邊緣看起來“像素化”。 但是,一旦我添加了一行代碼來重新繪制按鈕的父級,即使我將鼠標拖到按鈕上,抗鋸齒也會啟動。

現在,我的問題涉及到這是一個好主意嗎? 畢竟我從子組件重繪父組件。 我想知道這是否會導致重繪的循環? 如果父級嘗試重新繪制其子級並且子級嘗試重新繪制其父級 - 那么我假設我們正在討論循環。

我附上了我的代碼作為參考。 任何評論都非常歡迎!

public class JCustomButtonUI extends BasicButtonUI {

    @Override
    public void installUI(JComponent c) {
        super.installUI(c);

        AbstractButton b = (AbstractButton) c;
        b.setBorderPainted(false);
    }

    @Override
    public void paint(Graphics g, JComponent c) {

        //Cast the Graphics instance to a Graphics2D instance.
        Graphics2D g2d = (Graphics2D) g;
        JButton b = (JButton) c;

        //Repaint parent component to make sure that we get "antialiased"
        //edges.
        b.getParent().repaint();

        //Get the component's height and width.
        int w = (int) g.getClipBounds().getWidth();
        int h = ((int) g.getClipBounds().getHeight());

        //Make sure the button is drawn with "antialiased" edges.
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(Color.GRAY);
        g2d.fillRoundRect(0, 0, w, h, w, h);       
    }
}

更新1

只是為了說明別名和抗鋸齒邊框,請看下面的兩張圖片。 當我(從ButtonUI的paint()方法)手動調用父JPanel的重繪方法時,所有邊框都是完全抗鋸齒的。 但是,當我不手動調用父JPanel的重繪方法時,一旦我將鼠標懸停在按鈕上,就不再對邊框進行抗鋸齒處理。

別名抗鋸齒

更新2

我已經共享了整個“組件”,它由一個JPanel,一個JSlider和一些Snipt上的JButton組成。 請從http://snipt.org/wnllg獲取。

更新3

似乎我已經設法讓它工作。 我沒有在paintComponent()方法中繪制JPanel的背景,而是創建了一個安裝在JPanel上的JCustomPanelUI。 我不認為這是解決方案本身,但我沒有使用Graphics實例的寬度和高度,而是嘗試使用JPanel本身的widht和height。 當我使用Graphics實例的寬度和高度時,我不太確定為什么出錯了。 我認為Graphics實例的寬度和高度已經根據JPanel組件的尺寸進行了“准備”。 您可以在這里查看最終組件: http//snipt.org/wnlli ,

我已經將示例簡化為抗鋸齒,我無法重現該問題。 它似乎不依賴於平台。 我不確定你為什么使用getClipBounds()

附錄:

JPanel背景(漸變)需要閃耀......

我更新了示例,在透明按鈕后面使用漸變背景; 我並排放置了反鋸齒(左)和別名(右)的例子。 我沒有看到意外的行為。

ButtonUITest.png

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.plaf.basic.BasicButtonUI;

/** @see http://stackoverflow.com/questions/5169647 */
public class ButtonUITest extends JPanel {

    public ButtonUITest() {
        this.setLayout(new GridLayout(1, 0));
        this.setPreferredSize(new Dimension(640, 480));
        this.add(new CustomButton(true));
        this.add(new CustomButton(false));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        int w = this.getWidth();
        int h = this.getHeight();
        Graphics2D g2d = (Graphics2D) g;
        g2d.setPaint(new GradientPaint(0, 0, Color.blue, w, h, Color.red));
        g2d.fillRect(0, 0, w, h);
    }

    private void display() {
        JFrame f = new JFrame("ButtonUITest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static class CustomButton extends JButton {

        public CustomButton(boolean antialiased) {
            this.setOpaque(false);
            this.setUI(new CustomButtonUI(antialiased));
        }
    }

    private static class CustomButtonUI extends BasicButtonUI {

        private boolean antialiased;

        public CustomButtonUI(boolean antialiased) {
            this.antialiased = antialiased;
        }

        @Override
        public void paint(Graphics g, JComponent c) {
            int w = c.getWidth();
            int h = c.getHeight();
            Graphics2D g2d = (Graphics2D) g;
            if (antialiased) {
                g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            }
            g2d.setColor(Color.LIGHT_GRAY);
            g2d.fillOval(0, 0, w, 2 * h);
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ButtonUITest().display();
            }
        });
    }
}

要使抗鋸齒一致地工作,您的組件需要為isOpaque返回false 否則,RepaintManager可以跳過繪制組件后面的區域。

我懷疑,如果你使用屏幕放大鏡來查看“不帶鋸條”的邊緣,你會發現它們確實是抗鋸齒的。 但這是針對黑色未上漆的背景,而不是父母的背景。

知道他們正在談論什么的Swing專家很快就會在這里。 在此期間,讓我對此發表評論:

現在,我的問題涉及到這是一個好主意嗎? 畢竟我從子組件重繪父組件。 我想知道這是否會導致重繪的循環? 如果父級嘗試重新繪制其子級並且子級嘗試重新繪制其父級 - 那么我假設我們正在討論循環。

一旦你試一試並發現它在你的機器上不是問題,很可能在你嘗試的所有JVM上都是如此。 也就是說,證據是在布丁中,或隨機的笨拙通常會導致Swing的積極結果。 遞歸循環有一種方法可以使程序在Java中快速停止,所以答案是......如果這是完全錯誤的, 你已經知道了。 另外,你可以將sysouts放在那里,看看是否發生這種情況(顯然不是這樣)。

也就是說,可能有更好的方法來處理您的問題,但如果您的答案有效,請暫時堅持下去。

AFAIK,repaint()只是告訴系統在下一個繪制周期中重新繪制該組件,即如果你在paint()方法中調用repaint(),重繪可能會在下一個周期完成。

通常,Swing在自己的線程中運行,因此重復重繪不應該停止應用程序邏輯。 但是,它可能正在使用處理能力,如果系統總是重新繪制ui,即使沒有變化。

您可以嘗試編寫日志消息,以查看按鈕的繪制時間和頻率。 如果這種情況持續發生,即使沒有任何事情會導致ui更新,您可能必須找到解決方案。 如果沒有,你應該沒事。

編輯:有一個名為RepaintManager的類,您可能會感興趣。

在那里做一個重畫肯定是一個壞主意。 嘗試在installUI()添加b.setOpaque(false) installUI() 由於抗鋸齒,您的按鈕不再繪制其整個邊界。 您需要讓背景顯示以填補空白。

暫無
暫無

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

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