Swing覆盖对象的paintComponent方法

4
如果我有一个无法修改的JPanel对象,是否有办法在不使用注入的情况下修改它的paintComponent方法?
我考虑的一种方法是获取JPanelGraphics对象,将其传递给paintComponent(),对该Graphics对象执行操作,最后在我的自定义JPanel中绘制出来。问题是,每次调用原始JPanelpaintComponent()时,都需要能够执行此操作。
我不需要替换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
  }
}

意思是不要这样写:new JPanel(){paintComponent(Graphics g){...}}; - Harmeet Singh
1
请详细说明这个“注入”是什么意思。 - Harmeet Singh
我明白了,为什么不注入 painComponent 呢? - Harmeet Singh
@DavidKroukamp 不,我可以修改实例,但不能修改类。 - LanguagesNamedAfterCofee
但是你可以继承该类并修改继承对象的paintComponent方法,不是吗? - Hovercraft Full Of Eels
显示剩余7条评论
4个回答

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

@LanguagesNamedAfterCofee JPanel panel = new JPanel(new BorderLayout()) { ... overrides...}; panel.add(instanceYouCantModify); - Guillaume Polet
2
@LanguagesNamedAfterCofee 只有当回答解决了你的问题时,才应该接受该回答,并且不要一遍又一遍地更改已接受的答案。你不必立即接受回答,可以等待几天再作出决定。已被“解决”的问题很少会有人再发表另一个答案。 - Guillaume Polet

5
我认为一种可能性是使用GlassPane并将其定位在您的JPanel正上方(如果该面板更改位置,则可以使用侦听器让其跟随面板)。然后,您只需在glasspane中绘制您的内容,它就会被覆盖。

当然,这并不是真正优雅的解决方案...但我没有看到任何在不注入的情况下更改现有实例的paintComponents行为的可能性。(证明我错了,世界上的Java极客们!:P)


谢谢,我会研究一下。我特别想使用ColorConvertOp将其转换为灰度。 - LanguagesNamedAfterCofee
@LanguagesNamedAfterCofee 你可以查看这个链接获取有关如何使用GlassPane的更多信息。 - brimborium

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,以查看其正常呈现方式。

1
如果您有修改类结构的能力,您可以扩展该类,然后在扩展的类中调用super.paintComponent(g)。例如:
public class NewPanel extends OldPanel{

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

在这种情况下,您需要一个新类来构建现有类的绘画。
这样做的作用是执行父类绘画中所做的一切,并为您提供更多选项(这似乎是您正在寻找的内容)。
编辑
但是...鉴于您无法访问此内容,您的选择变得更加有限。如果面板使用空布局管理器,则可以添加一个子面板,该子面板会覆盖父面板(布局管理器将限制子面板可以覆盖的区域)。这是一个不太可能的选择。
您还可以使用反射,但除了字节码注入之外,您几乎没有其他选择。这似乎与字节码注入一样丑陋-这里有一个很好的概述,介绍了您要做的事情:Java reflection: How do I override or generate methods at runtime? 我个人的偏好是反编译类,按照您想要的方式进行修改,重新编译它并将其插入到原始jar中。这可能会使某些保修、许可证失效并引起其他麻烦...但至少它是可维护的。

谢谢,但是像我在之前的回答中所说的那样,我已经得到了这个JPanel的实例,但我不能创建一个新的。这就是为什么我正在考虑注入的原因。 - LanguagesNamedAfterCofee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接