4

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

我正在考虑的一种方法是获取JPanel'Graphics对象,将其传递给paintComponent(),对此对象执行操作Graphics,最后在我的自定义中绘制它JPanelJPanel问题在于,每次调用 original 时我都需要能够做到这一点paintComponent()

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

例如:

JFrame frame = null;
for (Frame f : JFrame.getFrames()) {
  if (((JFrame) f).getTitle().equals("Title")) {
    JPanel panel = null;
    // ... Cycle through all components and get the one that's a JPanel

    // I want to use ColorConvertOp to make panel greyscale
  }
}
4

4 回答 4

6

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

于 2012-07-30T17:46:58.787 回答
5

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

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

于 2012-07-30T17:24:04.927 回答
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,以查看它如何正常呈现。

于 2012-07-30T19:20:30.577 回答
1

如果你有能力修改你的类的构造,你可以扩展那个类,然后调用super.paintComponent(g)你的扩展类。例子:

public class NewPanel extends OldPanel{

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

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

它的作用是执行在父类的绘画中所做的一切,并为您提供更多选择(这似乎是您正在寻找的)。

编辑
但...鉴于您无权访问此内容,您的选择会受到更多限制。如果面板使用空布局管理器,您可以添加一个在父级上绘制的子 jpanel(布局管理器会限制子级可以绘制的区域数量)。这是一个长镜头。

您也可以使用反射来处理您拥有的唯一选项(除了字节码注入)。这似乎与字节码注入一样丑陋 - 这是您想要做的事情的一个不错的概述: Java反射:如何在运行时覆盖或生成方法?

我个人的偏好是反编译类,按照你想要的方式修改它,重新编译它并插入回原来的jar中。这可能会使一些警告、许可证无效并给您带来其他麻烦……但至少它是可维护的。

于 2012-07-30T17:14:34.113 回答