1

我正在尝试paint(Graphics)JPanel. 下面的代码片段工作得很好,JButton在我的 a 中画得很好JPanel

        @Override
        public void paint(Graphics g) {
            super.paint(g);
            JButton btn = new JButton("hello");
            Dimension dim = btn.getPreferredSize();
            btn.setSize(dim.width, dim.height);
            btn.paint(g);   // paint the button
        }

该代码片段也适用JLabelJTreeJPanel. 下面的代码会引起很奇怪NullPointerException at java.awt.Window.access$700(Window.java:132)

        @Override
        public void paint(Graphics g) {
            super.paint(g);
            JPanel panel = new JPanel();
            panel.setSize(10, 10);
            panel.paint(g);   // paint the panel
        }

这里是完整的堆栈跟踪:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at java.awt.Window.access$700(Window.java:132)
    at java.awt.Window$1.isOpaque(Window.java:3458)
    at javax.swing.RepaintManager.getVolatileOffscreenBuffer(RepaintManager.java:983)
    at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1395)
    at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:294)
    at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
    at javax.swing.JComponent.paint(JComponent.java:1015)
    at test.paintcontainer.TestPaintContainerMain$TestContentPane.paint(TestPaintContainerMain.java:48)
    at javax.swing.JComponent.paintChildren(JComponent.java:862)
    at javax.swing.JComponent.paint(JComponent.java:1038)
    at javax.swing.JLayeredPane.paint(JLayeredPane.java:567)
    at javax.swing.JComponent.paintChildren(JComponent.java:862)
    at javax.swing.JComponent.paintToOffscreen(JComponent.java:5131)
    at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:278)
    at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
    at javax.swing.JComponent.paint(JComponent.java:1015)
    at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:21)
    at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
    at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
    at java.awt.Container.paint(Container.java:1780)
    at java.awt.Window.paint(Window.java:3375)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)
    at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)
    at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:641)
    at java.awt.EventQueue.access$000(EventQueue.java:84)
    at java.awt.EventQueue$1.run(EventQueue.java:602)
    at java.awt.EventQueue$1.run(EventQueue.java:600)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:611)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

知道如何解决这个问题吗?我需要绘制JPanel内部paint(Graphics)方法。我编写了一个简单的测试应用程序,您可以复制粘贴它来重现上述异常:

package test.paintcontainer;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

public class TestPaintContainerMain extends JFrame {

    public static void main(String[] args) {
        TestPaintContainerMain test = new TestPaintContainerMain();
        test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        test.setBounds(0, 0, 300, 200);
        test.setContentPane(new TestContentPane());
        test.setVisible(true);
    }

    static class TestContentPane extends JPanel {

        JRadioButton paintButtonCheck;

        JRadioButton paintPanelCheck;

        public TestContentPane() {
            paintButtonCheck = createRadioButton("paint button", true);
            paintPanelCheck = createRadioButton("paint panel", false);
            ButtonGroup buttonGroup = new ButtonGroup();
            buttonGroup.add(paintButtonCheck);
            buttonGroup.add(paintPanelCheck);
            add(paintButtonCheck);
            add(paintPanelCheck);
        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);

            g.translate(100, 100);
            if (paintButtonCheck.isSelected()) {
                createButton().paint(g);
            } else {
                createPanel().paint(g);
            }
        }

        private JButton createButton() {
            JButton button = new JButton("button");
            button.setSize(button.getPreferredSize().width, button.getPreferredSize().height);
            return button;
        }

        private JPanel createPanel() {
            JPanel panel = new JPanel();
            panel.setBackground(Color.GREEN);
            panel.add(createButton());
            panel.setSize(panel.getPreferredSize().width, panel.getPreferredSize().height);
            return panel;
        }

        private JRadioButton createRadioButton(String title, boolean selected) {
            JRadioButton radio = new JRadioButton(title, selected);
            radio.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    TestContentPane.this.repaint();
                }
            });
            return radio;
        }
    }

}
4

2 回答 2

2

这很可能不是Swing 中的错误,而是更多的问题,因为您正在尝试绘制尚未实现的组件,这意味着它没有活动的图形上下文。您可以通过将组件添加到已实现的组件(如 JFrame)来实现组件 - 它本身由setVisible(true).

也可能永远不要JComponent.paint(Graphics)手动调用,因为这是 Swing 的工作(更准确地说是 Event Dispatcher Thread)——它甚至在paint方法的文档中这么说:

应用程序不应直接调用paint,而应使用repaint 方法来安排组件重绘。

您可以调用的是方法printAll(Graphics g),它绘制组件及其所有子组件。同样在 Swing 中,一个也不应该覆盖paint但是paintComponent

所以这是一个测试代码:

    JButton button = createButton();
    JPanel panel = createPanel();

    public TestContentPane() {
        paintButtonCheck = createRadioButton("paint button", true);
        paintPanelCheck = createRadioButton("paint panel", false);
        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.add(paintButtonCheck);
        buttonGroup.add(paintPanelCheck);
        add(paintButtonCheck);
        add(paintPanelCheck);

        //Hack, just prove something (realize both components)
        add(panel);
        add(button);
    }

    ...

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.translate(100, 100);
        if (paintButtonCheck.isSelected()) {
            button.paintAll(g);
        } else {
            panel.paintAll(g);
        }
        g.translate(-100, -100);
    }

这应该可以工作(尽管您显然会在屏幕上有两个您不想要的组件)。还要注意“重置”图形对象,因为之后 Swing 仍会使用它。


所以这是理论,但它还不是一个实际的解决方案。

我对您的问题的解决方案是:“不要这样做”!

组件不像图像,因为它们在任何地方看起来都不一样。调用的输出paintAll会有所不同,具体取决于实现组件的方式(或位置)。

所以一个建议是展示实际的组件。创建您的工具提示框,添加您的面板和按钮,然后让它们自己绘制。您甚至可以对这些组件进行子类化并覆盖它们的paintComponent()方法,添加透明度等等。这将需要一些工作,但 Swing 从来就不是一件容易的事。

于 2012-04-13T11:32:36.910 回答
1

我刚刚找到了解决方案。我的问题中对示例代码的唯一修改是我试图绘画时调用panel.setDoubleBuffered(false)的那个。JPanel

但是,我仍然认为例外是 Swing 错误。如果应按设计关闭双缓冲,则不应得到NullPointerException其他更有意义的异常来解释该情况。

这是一个固定的示例应用程序:

package test.paintcontainer;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

public class TestPaintContainerMain extends JFrame {

    public static void main(String[] args) {
        TestPaintContainerMain test = new TestPaintContainerMain();
        test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        test.setBounds(0, 0, 300, 200);
        test.setContentPane(new TestContentPane());
        test.setVisible(true);
    }

    static class TestContentPane extends JPanel {

        JRadioButton paintButtonCheck;

        JRadioButton paintPanelCheck;

        public TestContentPane() {
            paintButtonCheck = createRadioButton("paint button", false);
            paintPanelCheck = createRadioButton("paint panel", true);
            ButtonGroup buttonGroup = new ButtonGroup();
            buttonGroup.add(paintButtonCheck);
            buttonGroup.add(paintPanelCheck);
            add(paintButtonCheck);
            add(paintPanelCheck);
        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);

            g.translate(100, 100);
            if (paintButtonCheck.isSelected()) {
                createButton().paint(g);
            } else {
                createPanel().paint(g);
            }
        }

        private JButton createButton() {
            JButton button = new JButton("button");
            button.setSize(button.getPreferredSize().width, button.getPreferredSize().height);
            return button;
        }

        private JPanel createPanel() {
            JPanel panel = new JPanel();
            panel.setBackground(Color.GREEN);
            panel.add(createButton());
            // --------------------------------
            panel.setDoubleBuffered(false);  // <-- TURN OFF DOUBLE BUFFERING
            // --------------------------------
            panel.setSize(panel.getPreferredSize().width, panel.getPreferredSize().height);
            return panel;
        }

        private JRadioButton createRadioButton(String title, boolean selected) {
            JRadioButton radio = new JRadioButton(title, selected);
            radio.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    TestContentPane.this.repaint();
                }
            });
            return radio;
        }
    }

}
于 2012-04-13T10:33:57.370 回答