当JMenu存在时,JPanel#paintChildren(Graphics)的行为不正确?

afk*_*min 4 java swing jpanel

我想做的事:
创建一个JPanel子类,在包含的组件之上绘制一个简单的叠加层.

我为什么不用JLayeredPane
JComponent#isOptimizedDrawingEnabled().

当a JMenu中存在a时JFrame,JPanel使用重写paintChildren(Graphics)方法添加a 时,在传递的Graphics对象中提供了不正确的坐标起点,如此代码示例所示:

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public final class Sscce {
    public static void main(String[] args) {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                @Override
                public void run() {
                    // a normal frame
                    JFrame f = new JFrame();

                    // set up a simple menu
                    JMenuBar mb = new JMenuBar();
                    JMenu m = new JMenu("Test");
                    JMenuItem mi = new JMenu("Whatever");
                    m.add(mi);
                    mb.add(m);
                    f.setJMenuBar(mb);

                    // a panel with a simple text overlay over components.
                    // works much faster than JLayeredPane, which doesn't have
                    // isOptimizedDrawingEnabled()
                    JPanel p = new JPanel() {
                        @Override
                        public void paint(Graphics g) {
                            // I'm not so stupid to draw stuff here
                            super.paint(g);
                            // JavaDoc: delegates to paintComponent, paintBorder, paintChildren
                            // in that order
                        }

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

                            // it is common knowledge that children are painted after parent
                            Graphics tmp = g.create();
                            try {
                                tmp.setColor(Color.MAGENTA);
                                tmp.fillRect(0, 0, getWidth(), getHeight());
                            } finally {
                                tmp.dispose();
                            }
                        }

                        @Override
                        protected void paintChildren(Graphics g) {
                            super.paintChildren(g);

                            // draw some text
                            FontMetrics fm = g.getFontMetrics();
                            // will be drawn outside panel; under menu
                            g.drawString("TEST TOP/LEFT", 0 + getX(), 0 + getY());
                            final String s = "TEST BOTTOM/RIGHT";
                            // will be drawn noticeably above the bottom
                            g.drawString(s,
                                    getWidth() - fm.charsWidth(s.toCharArray(), 0, s.length()),
                                    getHeight() - fm.getHeight());
                        }
                    };
                    // add something to the panel
                    p.add(new JTextArea(10, 15));
                    f.add(p);

                    f.pack();
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.setVisible(true);
                }
            });
        } catch (Throwable t) {
            // this is a SSCCE
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

第一个字符串是在JPanel外部(在JMenu下)绘制的,即使两个坐标都是非负的.
第二个字符串未在右下角绘制.它被JMenu的高度推高了.

图片

即使:

当AWT调用此方法时,Graphics对象参数已预先配置为在此特定组件上绘制的适当状态:

  • Graphics对象的颜色设置为组件的foreground属性.
  • Graphics对象的字体设置为组件的font属性.
  • 设置Graphics对象的转换,使得坐标(0,0)表示组件的左上角.
  • Graphics对象的剪辑矩形设置为需要重新绘制的组件区域.

程序必须使用此Graphics对象(或从其派生的对象)来呈现输出.他们可以根据需要随意更改Graphics对象的状态.

我究竟做错了什么?

tra*_*god 7

第一个字符串是在JPanel(在JMenu)下面绘制的,即使两个坐标都是非负的.第二个字符串未在右下角绘制.它被推高了JMenu.

在这两种情况下,请注意,drawString()预计坐标来表示基线String.在这种情况下,字体的上升和下降很有用.这可能是巧合,mb.getHeight()并且fm.getHeight()具有可比性.

在此输入图像描述

@Override
protected void paintChildren(Graphics g) {
    super.paintChildren(g);

    // draw some text
    FontMetrics fm = g.getFontMetrics();
    // will be drawn outside panel; under menu
    g.drawString("TEST TOP/LEFT", 0, fm.getAscent());
    final String s = "TEST BOTTOM/RIGHT";
    // will be drawn noticeably above the bottom
    g.drawString(s, getWidth() - fm.stringWidth(s),
        getHeight() - fm.getDescent());
}
Run Code Online (Sandbox Code Playgroud)