在JPanel中的不透明组件上绘制自定义内容

3

我有一个包含多个不透明自定义组件的JPanel。现在,我想通过覆盖paintComponent()方法在这些组件上方绘制一些内容。我的问题是绘制的内容被放置在嵌入式组件的后面,并且由于它们是不透明的,被它们覆盖了。

有没有办法让绘制的内容显示在组件的上方?

这里是我正在尝试做的简短示例:

public class DrawOnTop {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame f = new JFrame("Draw on top");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add(new MyPanel());
                f.pack();
                f.setVisible(true);
            }
        });
    }
}

class MyPanel extends JPanel {

    public MyPanel() {
        setLayout(new BorderLayout(3, 3));
        add(new JButton("Button 1"), BorderLayout.NORTH);
        add(new JButton("Button 2"), BorderLayout.CENTER);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.red);
        g.drawLine(0, 0, getVisibleRect().width, getVisibleRect().height);
    }
}
1个回答

2
你的想法是正确的。 唯一的问题是你应该像下面的代码一样覆盖paintChildren()方法。这是因为paintComponent()方法首先被调用并绘制组件本身(MyPanel),然后调用paintBorders(),最后调用paintChildren(),它绘制所有调用它的组件内部的内容。
public class DrawOnTop {

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

            @Override
            public void run() {
                JFrame f = new JFrame("Draw on top");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add(new MyPanel());
                f.pack();
                f.setVisible(true);
            }
        });
    }
}

class MyPanel extends JPanel {

    public MyPanel() {
        setLayout(new BorderLayout(3, 3));
        JButton b1 = new JButton("Button 1");
        MouseListener ml = new MouseAdapter() {

            @Override
            public void mouseExited(MouseEvent e) {
                super.mouseExited(e);
                MyPanel.this.repaint();
            }
        };
        b1.addMouseListener(ml);
        JButton b2 = new JButton("Button 2");
        b2.addMouseListener(ml);
        add(b1, BorderLayout.NORTH);
        add(b2, BorderLayout.CENTER);
    }

    @Override
    protected void paintChildren(Graphics g) {
        super.paintChildren(g);
        g.setColor(Color.red);
        g.drawLine(0, 0, getVisibleRect().width, getVisibleRect().height);
    }
}

请注意,在代码示例中,我还添加了一个MouseListener来重新绘制面板,当鼠标退出按钮时,否则按钮将始终停留在行上一旦鼠标进入其中之一。

但是,如果您想要一个始终位于组件顶部的自定义绘画,则建议使用玻璃窗格。 这里提供了使用玻璃窗格的示例:

  1. 简单的示例。
  2. 更复杂的示例。

谢谢,这正是我在寻找的。我已经在多个地方阅读到自定义绘制应该始终发生在paintComponent()中。同时非常感谢您建议使用玻璃窗格,我会看看是否有必要。 - Moritz
1
@Moritz 是的,我知道你在指什么。我同意paintComponent()应该用于自定义绘制,而不涉及其内容。但在我们的情况下并非如此。在我们的情况下,面板包含子元素,它们始终会显示在其上方,因为它们是在父元素完成绘制后绘制的。出于这个简单的原因,为了在子元素之上绘制,我们必须在paintChildren()方法中调用它。 - Boro

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