Swing覆盖对象的paintComponent

时间:2012-07-30 16:28:32

标签: java swing paintcomponent

如果我有一个我无法修改的JPanel对象,有没有办法可以在不使用注入的情况下修改它的paintComponent方法?

我想到的一种方法是获取JPanel的{​​{1}}对象,将其传递给Graphics,对此paintComponent()对象执行操作,最后绘制在我的自定义Graphics中。这个问题是我每次调用原始JPanel的{​​{1}}时都需要这样做。

我不需要替换paintComponent()中的内容,我只需要添加它。

例如:

JPanel

4 个答案:

答案 0 :(得分:6)

一种方法是使用Decorator Pattern来包装现有的类。然后,您的装饰器可以实现paintComponent以首先委托给原始组件,然后在其上面绘制。对于这种方法,您需要实际控制组件的创建,或者在创建组件层次结构后需要替换它们(使用父容器的getComponents()来查找要更改的组件)。

答案 1 :(得分:5)

我认为一种可能性是使用GlassPane并将其准确定位在JPanel上(如果面板更改其位置,可以让它使用侦听器跟随面板)。然后你可以简单地在玻璃板上画出你的东西,然后叠加。

当然这不是很优雅......但是我没有看到任何可能在没有注入的情况下更改已存在实例的paintComponents行为的可能性。 (证明我错了,这个世界的Java极客!:P)

答案 2 :(得分:3)

我想这将完成@Durandal的回答:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TestPanels {

    private static final boolean GRAY_SCALE = true;

    protected void initUI() {
        final JFrame frame = new JFrame(TestPanels.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel unmodifiablePanel = new JPanel(new GridBagLayout());
        JLabel label = new JLabel("Some unmodifiable test label");
        unmodifiablePanel.add(label);
        unmodifiablePanel.setBackground(Color.GREEN);
        JPanel wrappingPanel = new JPanel(new BorderLayout()) {
            private ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
            private BufferedImage image;

            @Override
            public void paint(Graphics g) {
                if (!GRAY_SCALE) {
                    super.paint(g);
                    return;
                }
                BufferedImage bi = getImage();
                if (bi != null) {
                    Graphics big = bi.createGraphics();
                    super.paint(big);
                    big.dispose();
                    bi = op.filter(bi, null);
                    g.drawImage(bi, 0, 0, null);
                }
            }

            protected BufferedImage getImage() {
                if (image == null) {
                    if (getWidth() > 0 && getHeight() > 0) {
                        image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
                    }
                } else if (image.getWidth() != getWidth() || image.getHeight() != image.getHeight()) {
                    image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
                }
                return image;
            }

        };
        wrappingPanel.add(unmodifiablePanel);

        frame.add(wrappingPanel);
        frame.setSize(200, 200);
        frame.setVisible(true);
    }

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

            @Override
            public void run() {
                new TestPanels().initUI();
            }
        });
    }
}

您可以将GRAY_SCALE标志设置为false,以查看它是如何正常渲染的。

答案 3 :(得分:1)

如果您能够修改类的构造,则可以扩展该类,然后在扩展类中调用super.paintComponent(g)。例如:

public class NewPanel extends OldPanel{

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        // Insert additional painting here
    }
}

在这种情况下,您需要一个新类才能构建现有类的绘画。

这样做是执行在父类绘画中完成的所有内容,并为您提供更多的选项(这似乎是您正在寻找的内容)。

修改
但是......鉴于您无法访问此选项,您的选项会受到更多限制。如果面板使用空布局管理器,则可以添加绘制父级的子jpanel(布局管理器会限制子项可以绘制的区域数量)。这是一个很长的镜头。

您也可以使用反射,但关于您获得的唯一选项(除了字节码注入)。这似乎与字节代码注入一样丑陋 - 这里是对你要做的事情的一个不错的概述:Java reflection: How do I override or generate methods at runtime?

我个人的偏好是反编译该类,按照您想要的方式对其进行修改,重新编译并将其插回到原始jar中。这可能会使一些警察,许可证无效并让你陷入其他麻烦......但至少它是可维护的。