如何获取Swing组件的绘制大小?

3

当我将Swing组件(比如JButton)添加到JPanel中时,它会按照其“首选大小”进行渲染。

然而,首选大小实际上比绘制的按钮要大。它似乎周围有一个不可见的边框。

这是一个包含我的测试面板的简单框架:

JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

TestPanel pnl = new TestPanel();
frame.getContentPane().add(pnl);

frame.pack();
frame.setVisible(true);

这是我的测试面板...
public class TestPanel extends JPanel {

    JButton btn1 = new JButton("Test1");
    JButton btn2 = new JButton("Test2");

    public TestPanel() {
        this.add(btn1);
        this.add(btn2);
    }

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

        g.setColor(Color.RED);
        Dimension dim = btn1.getPreferredSize();
        g.drawRect(btn1.getX(), btn1.getY(), (int)(dim.getWidth()), (int)(dim.getHeight()));
    }

}

注意,我将btn1的“PreferredSize”标记为红色,以证明preferredSize实际上比按钮本身要大。
我的问题是,如何确定绘制的按钮的宽度和高度,而不是JButton的preferredSize?
非常感谢任何帮助!
更新
因为我需要让这适用于所有Swing组件,所以这里有一个屏幕截图,其中包含更多组件。
不幸的是,我需要解决这个问题,确定可见小部件的“真实”大小对我的应用程序至关重要。

我相信(虽然不确定)这是JButton实现的一部分,因此被“隐藏”了(您可以通过阅读代码来检查如何计算额外边框)。无论如何,您需要那个值做什么?可能有另一种方法来实现同样的事情。 - asermax
@asermax 我需要做的一个例子是动态定位JButton(或任何Swing小部件/JComponent),使其与窗口的左侧或右侧顶部或底部相距10像素。目前,JButton距离边缘有10个像素+不可见边框。肯定有一种编程方式可以确定该不可见部分的宽度/高度,对吧? - user550738
@asermax 感谢您的建议。不幸的是,btn1.setBorderPainted(false)只会导致按钮本身不被绘制,但并不影响PreferredSize。我会查看getBorder并查看其中是否定义了某种类似于CSS的边距或填充。 - user550738
如果使用了不可见的边框,则组件必须使用CompoundBorder。您应该能够确定所使用的外部边框的大小。 - camickr
@camickr 感谢您的指引,我正在尝试使用 btn1.getBorder().getBorderInsets(btn1),我会及时更新进展。 :) - user550738
显示剩余4条评论
2个回答

3
如果没有更好的选择,作者建议您将组件放在 JPanel 中,并在 JPanel 上设置 border
补充说明:根据您下面的评论,很明显您的问题不是关于渲染边框,而是关于建立组件的边界。您所感知到的未使用空间实际上是由UI委托保留的,用于任何数量的用途,例如选择高亮或美学一致性。您可以通过在这里选择不同的外观主题来了解这种变化herehere
使用 getbounds():

image

使用 setBorder()

image

import component.Laf;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

/**
 * @see https://dev59.com/lG_Xa4cB1Zd3GeqP5dOZ#15490187
 */
public class Test {

    private void display() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new FlowLayout());
        // https://dev59.com/62fWa4cB1Zd3GeqPfD6V#11949899
        f.add(Laf.createToolBar(f));
        f.add(decorate(new JButton("Test")));
        f.add(decorate(new JTextField("Test")));
        f.add(decorate(new JTextArea(3, 8)));
        f.add(decorate(new JCheckBox("Test")));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private JPanel decorate(final JComponent c) {
        JPanel p = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Rectangle r = c.getBounds();
                g.setColor(Color.red);
                // NB pen hangs down and to the right
                g.drawRect(r.x - 1, r.y - 1, r.width + 1, r.height + 1);
            }
        };
        p.add(c);
        return p;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Test().display();
            }
        });
    }
}

谢谢,但我只是在我的TestPanel上画了一条红线,以展示小部件的“preferredSize”的范围。我想确定的是如何获取按钮(或其他小部件)绘制区域的大小。我认为应该使用getPreferredSize,但实际上它更大。 - user550738
可能有助于编辑您的问题以澄清目标;可能存在一种不会干扰 L&F 装饰的不同标记方式。 - trashgod
1
@RobertHume没有帮助你解决问题,因为在这种情况下是相同的,只需使用comp.getSize()来获取组件的实际大小。prefSize是一个布局管理器的大小提示,它可以自由地忽略。 - kleopatra
1
似乎感知边界与实际边界之间的巨大差异在Mac上特别明显(在Windows上差异不大),因此您应该阅读Mac样式指南,了解如何处理它(处理意味着正确对齐)。 - kleopatra
@kleopatra 感谢您的帮助,我会切换到getSize。 - user550738
显示剩余4条评论

3

我认为这并不是特别的或实际可行的。

问题是,按钮使用“未涂色”的区域来绘制其他元素,如焦点高亮。

您可以尝试查看AbstractButton#set / getMargin


谢谢您的建议,但是btn1.getMargin()看起来是[top=0,left=2,bottom=0,right=2] -- 这并不能解释所有的额外空间。 :( - user550738
最终结果是,在Mac OS X上,“未涂色”的区域被认为是组件的一部分。在Windows上,我看到了预期的行为。我想我只能听从于在目标平台上实现小部件的人。感谢您的帮助。 - user550738
这正是我想的。我在文本字段和组件边框方面也遇到了类似的问题 :P - MadProgrammer

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