JPanel什么时候绘制(或重绘)它的子组件?

时间:2011-03-02 15:26:19

标签: java swing custom-painting

我有一个使用自定义UI委托绘制的JButton(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的重绘方法时,一旦我将鼠标悬停在按钮上,边框就不再是抗锯齿的了。

Aliased Antialiased

更新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

5 个答案:

答案 0 :(得分:4)

我已将示例简化为抗锯齿,我无法重现该问题。它似乎不依赖于平台。我不确定您使用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();
            }
        });
    }
}

答案 1 :(得分:2)

要使抗锯齿功能始终如一,您的组件需要为false返回isOpaque。否则,RepaintManager可以跳过绘制组件后面的区域。

我怀疑如果你使用屏幕放大镜来查看“不带锯条”的边缘,你会发现它们确实是抗锯齿的。但这是针对黑色未上漆的背景,而不是父母的背景。

答案 2 :(得分:1)

知道他们正在谈论什么的Swing专家很快就会在这里。与此同时,让我对此发表评论:

  

现在,我的问题涉及到了   这是一个好主意?我毕竟做了   从a重绘父组件   子组件。我想知道这是不是   到一个重绘的循环?如果是父母   试图重绘它的孩子和   孩子们试图重绘其父母 -   然后我假设我们正在谈论一个   循环。

一旦你试一试并发现它在你的机器上不是问题,你可能会尝试在所有JVM上都是如此。也就是说,证据是在布丁中,或随机的笨拙通常会导致Swing的积极结果。递归循环有一种方法可以使程序在Java中快速停止,所以答案是......如果这完全错误你已经知道了。另外你可以把sysouts放在那里看看如果发生这种情况(显然不是这样)。

尽管如此,可能有更好的方法来解决您的问题,但如果您的答案有效,请暂时坚持下去。

答案 3 :(得分:0)

AFAIK,repaint()只是告诉系统在下一个绘制周期中重新绘制该组件,即 如果你在paint()方法中调用repaint(),重绘可能会在下一个循环中完成。

通常,Swing在自己的线程中运行,因此重复重绘不应该停止应用程序逻辑。但是,它可能正在使用处理能力,如果系统总是重新绘制ui,即使没有变化。

您可以尝试编写日志消息,以查看按钮的绘制时间和频率。 如果这种情况持续发生,即使没有任何事情会导致ui更新,您可能必须找到解决方案。如果没有,你应该没事。

编辑:有一个名为RepaintManager的课程,您可能会感兴趣。

答案 4 :(得分:0)

在那里做一个重绘肯定是一个坏主意。尝试在b.setOpaque(false)中添加installUI()。由于抗锯齿,您的按钮不再绘制其整个边界。您需要让背景显示以填补空白。