如何为Java Swing组件添加边框?

7
我正在开发一个应用程序,它将包含几个选项卡面板。在每一个面板上,我想要放置一组组件,彼此之间由边框分隔。它看起来像这样:
|- Titled Border 1 ---

[JTextField] [JComboBox] [JTextField] [JComboBox]

|--------

|- Titled Border 2 ---

[JTextField] [JComboBox] [JTextField] [JComboBox]

|--------

... and so forth.

当我尝试简单地向面板添加新的边框 - Titled Border 2时,它被添加并覆盖了第一个边框,组件仍显示在最上方。在一些示例中,我看到有许多JPanels在一个框架内定义,每个面板都有自己的边框。这在我的情况下可能有效,但如何将这些面板添加到一个选项卡中进行显示呢?
Oracle的教程之一展示了一个带有许多种边框演示的选项卡面板。当我尝试编辑它并放置一个组件时,它出现在两个边框之间而不是被包围。这也是对我失败的另一个选项。
其次,我不使用任何布局管理器,组件位置是固定的,老实说,我想保持这种设置。或者您推荐在这种特殊情况下使用任何布局管理器吗?
您有任何关于如何解决这个问题的提示吗?
编辑:似乎我还不允许附加屏幕截图,但这里有负责显示边框部分的代码。
    lenMicro = new JPanel();
    lenMicro.setLayout(null);

    bGreyLine = BorderFactory.createLineBorder(Color.GRAY, 1, true);
    bTitled1 = BorderFactory.createTitledBorder(bGreyLine, "Length (1/2)", TitledBorder.LEFT, TitledBorder.TOP);
    lenMicro.setBorder(bTitled1);
    bTitled2 = BorderFactory.createTitledBorder(bGreyLine, "Length (2/2)", TitledBorder.LEFT, TitledBorder.TOP);
    lenMicro.setBorder(bTitled2); 

当取消注释最后两行时,标题为“长度(2/2)”的边框将显示。

3
我会说 - 在99.99%的情况下使用LayoutManager。唯一的例外可能是如果您提供一些随机对象,例如用户绘制的形状。 - StanislavL
你能否附上一些截图来描述情况,而不是口头叙述? - Eng.Fouad
2
API指示将边框放在包含组件的容器上。 - Catalina Island
你应该使用布局管理器,并且你应该调查BorderFactory类。 - Gilbert Le Blanc
请发布一个SSCCE - Guillaume Polet
你发布的那部分代码没问题(除了你两次设置lenMicro的边框)。那么问题出在哪里? - Rempelos
2个回答

18

使用绝对定位时,你必须为每个组件提供位置,这些组件将成为视图的一部分。因此,如果不仔细审视你所做的事情,很难预测哪里出了问题。然而,使用布局管理器将减轻你在视图上放置不同组件的大负担。

此外,你必须为相应的组件设置边框。因此,我无法假设你添加了一个组件并出现在两个边界之间(尽管考虑到你正在使用绝对定位,可能已经在视图上为该组件给出了错误的坐标)。请看一下这个示例代码,可能会对你有所帮助:

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

public class BorderExample
{
    private JPanel topPanel;
    private JPanel centerPanel;
    private JPanel bottomPanel;
    private int hgap;
    private int vgap;
    private JTextField tfield1, tfield2;
    private JComboBox cbox1, cbox2;
    private String[] data = {"One", "Two"};

    public BorderExample()
    {
        hgap = 5;
        vgap = 5;
    }

    private void displayGUI()
    {
        JFrame frame = new JFrame("Border Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel contentPane = new JPanel();
        contentPane.setOpaque(true);
        contentPane.setBackground(Color.WHITE);
        contentPane.setBorder(
            BorderFactory.createEmptyBorder(hgap, hgap, hgap, hgap));
        contentPane.setLayout(new BorderLayout(hgap, vgap));

        topPanel = new JPanel();
        topPanel.setOpaque(true);
        topPanel.setBackground(Color.WHITE);
        topPanel.setBorder(
            BorderFactory.createTitledBorder("Top Panel"));
        tfield1 = new JTextField(10);   
        tfield1.setBorder(
            BorderFactory.createTitledBorder(
            BorderFactory.createEtchedBorder(
                    EtchedBorder.RAISED, Color.GRAY
                    , Color.DARK_GRAY), "JTextField"));
        JPanel comboPanel = new JPanel();           
        cbox1 = new JComboBox(data);
        cbox1.setBorder(
            BorderFactory.createTitledBorder("JComboBox")); 
        topPanel.add(tfield1);  
        topPanel.add(cbox1);

        centerPanel = new JPanel(); 
        centerPanel.setOpaque(true);
        centerPanel.setBackground(Color.WHITE);
        centerPanel.setBorder(
            BorderFactory.createTitledBorder("Center Panel"));
        tfield2 = new JTextField(10);   
        tfield2.setBorder(
            BorderFactory.createLoweredBevelBorder());
        cbox2 = new JComboBox(data);
        cbox2.setBorder(
            BorderFactory.createRaisedBevelBorder());   
        centerPanel.add(tfield2);   
        centerPanel.add(cbox2);

        bottomPanel = new JPanel(); 
        bottomPanel.setOpaque(true);
        bottomPanel.setBackground(Color.WHITE);
        bottomPanel.setBorder(
            BorderFactory.createTitledBorder("Center Panel"));

        contentPane.add(topPanel, BorderLayout.PAGE_START);
        contentPane.add(centerPanel, BorderLayout.CENTER);
        contentPane.add(bottomPanel, BorderLayout.PAGE_END);

        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                new BorderExample().displayGUI();
            }
        });
    }
}

以下是相同代码的输出结果:

BORDER_EXAMPLE


非常感谢!这帮助我理解了这个问题的进展。我决定使用布局管理器GridBagLayout,现在正在处理它的设置。完成后,我将发布代码的最相关部分。 - AbreQueVoy
当然,问任何问题都可以,可能会给你带来麻烦,布局总是很好的。对于其他方面,非常欢迎并保持微笑 :-) 如果有一个SSCCE可以处理,那将不胜感激。 - nIcE cOw

3

好的,这解决了我关于布局管理器的使用和为组件添加边框的大部分疑虑:

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import java.awt.*;

public class TestingGround extends JFrame {

private JTextField tLenUnit, tLenResult, tAreUnit, tAreResult;
private JComboBox<String> cLenInUnit, cLenOutUnit, cAreInUnit, cAreOutUnit;
private JPanel lenMicro, lenMicro1, lenMicro2, lenNormal, lenMacro, area, volume;
private Border bGreyLine, bTitled1, bTitled2;

private TestingGround() {

    setTitle("Testing Ground for an Application");
    setVisible(true);
    setResizable(true);
    setLocationRelativeTo(null);

    try {
        UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (UnsupportedLookAndFeelException e) {
        e.printStackTrace();
    }

    lenMicro = new JPanel();
    lenMicro.setLayout(new GridBagLayout());

    lenMicro1 = new JPanel();
    lenMicro1.setLayout(new GridBagLayout());

    bGreyLine = BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1, true);
    bTitled1 = BorderFactory.createTitledBorder(bGreyLine, "Length", TitledBorder.LEFT, TitledBorder.TOP);
    lenMicro1.setBorder(bTitled1);

    tLenUnit = new JTextField("0.0");
    tLenUnit.setColumns(10);
    lenMicro1.add(tLenUnit, new GBC(0, 0, 2, 1).setAnchor(GBC.WEST).setInsets(5, 5, 5, 5));

    cLenInUnit = new JComboBox<String>();
    cLenInUnit.addItem("");
    cLenInUnit.addItem("First");
    cLenInUnit.addItem("Second");
    cLenInUnit.setSelectedIndex(0);
    cLenInUnit.setPreferredSize(new Dimension(120, 25));
    lenMicro1.add(cLenInUnit, new GBC(2, 0, 3, 1));

    tLenResult = new JTextField("");
    tLenResult.setColumns(10);
    lenMicro1.add(tLenResult, new GBC(5, 0, 2, 1).setInsets(5, 5, 5, 5));

    cLenOutUnit = new JComboBox<String>();
    cLenOutUnit.addItem("First");
    cLenOutUnit.addItem("Second");
    cLenOutUnit.setSelectedIndex(1);
    cLenOutUnit.setPreferredSize(new Dimension(120, 25));
    lenMicro1.add(cLenOutUnit, new GBC(7, 0, 1, 1));

    // Area part:

    lenMicro2 = new JPanel();
    lenMicro2.setLayout(new GridBagLayout());

    bTitled2 = BorderFactory.createTitledBorder(bGreyLine, "Area", TitledBorder.LEFT, TitledBorder.TOP);
    lenMicro2.setBorder(bTitled2);

    tAreUnit = new JTextField("0.0");
    tAreUnit.setColumns(10);
    lenMicro2.add(tAreUnit, new GBC(0, 1, 2, 1).setAnchor(GBC.WEST).setInsets(5, 5, 5, 5));

    cAreInUnit = new JComboBox<String>();
    cAreInUnit.addItem("");
    cAreInUnit.addItem("One sqm");
    cAreInUnit.addItem("Two sqm");
    cAreInUnit.setSelectedIndex(0);
    cAreInUnit.setPreferredSize(new Dimension(120, 25));
    lenMicro2.add(cAreInUnit, new GBC(2, 1, 3, 1));

    tAreResult = new JTextField("");
    tAreResult.setColumns(10);
    lenMicro2.add(tAreResult, new GBC(5, 1, 2, 1).setInsets(5, 5, 5, 5));

    cAreOutUnit = new JComboBox<String>();
    cAreOutUnit.addItem("One sqm");
    cAreOutUnit.addItem("Two sqm");
    cAreOutUnit.setSelectedIndex(1);
    cAreOutUnit.setPreferredSize(new Dimension(120, 25));
    lenMicro2.add(cAreOutUnit, new GBC(7, 1, 1, 1));

    // Joining all lenMicroX panels into one:

    lenMicro.add(lenMicro1, new GBC(0, 0, 8, 1).setAnchor(GBC.FIRST_LINE_START).setInsets(5, 5, 5, 5).setIpad(10, 10));
    lenMicro.add(lenMicro2, new GBC(0, 1, 8, 1).setAnchor(GBC.LINE_START).setInsets(5, 5, 5, 5).setIpad(10, 10));

    volume = new JPanel();
    volume.setLayout(null);

    // Panel definition --begin:

    JTabbedPane tPane = new JTabbedPane();

    tPane.addTab("Length & Area", null, lenMicro, "Length & Area units");
    tPane.addTab("Volume", null, volume, "Volume units");
    add(tPane);

    // Panel --end.
}

public static void main(String[] args) {

    TestingGround app = new TestingGround();
    app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    app.pack();
    app.setVisible(true);

}

}

GridBagConstraints助手类取自Cay Horstmann和Gary Cornell的Core Java,可以在此处找到:http://www.horstmann.com/articles/GBC.java

我从Gagandeep的示例中发现,面板可以放在顶部,我们称之为母板。这样每个子面板都可以有自己的边框。

第二件事:我决定使用GridBagLayout,因为它似乎是最准确的用于我的这个和后续项目。嗯,我还需要学习很多关于它的知识(例如,为了使组件对齐到某些线条;-) [编辑:刚刚添加了setPreferredSize()到JComboBoxes;使布局看起来更好;-)])。

感谢大家的帮助和宝贵的提示!

经过更正后的应用程序。


1
提醒一下,添加完所有内容到 JFrame 后再使用像 frame.setVisible(true/false) 这样的调用。这样它就会在实现大小之后呈现出来。如果像你这样,在框架实际计算出大小之前过早地调用可见属性,有时可能会给出异常结果。但是,还是因为你做出了努力,所以点个赞 +1 :-) - nIcE cOw
1
好的,谢谢你的建议!(那我得去跟我的老师聊聊了,哈哈 :-)) - AbreQueVoy

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