JTabbedPane和固定高度内容的大小问题

时间:2016-01-22 10:53:12

标签: java swing jtabbedpane preferredsize

在写这个问题时,我已经找到了一种让它按照我想要的方式行事的方法。根据{{​​3}},我仍然会发布问题,因为其他人可能会遇到类似的问题。

我对JTabbedPane的大小调整存在以下问题:JTabbedPane用于水平拉伸但具有固定高度的内容。 setTabLayoutPolicy()的两个选项似乎都会改变内容的高度,并且不会始终将其显示在首选或最小高度。

使用默认WRAP_TAB_LAYOUT时,标签窗格的首选大小不会考虑标签实际上是堆叠还是显示在彼此旁边,正如所讨论的this,{{ 3}}和here。如果基于堆叠选项卡布置选项卡式窗格,则当有足够的空间使选项卡彼此相邻显示时,内容的高度将增加约20个像素(一个选项卡的高度)。如果基于彼此相邻显示的选项卡布置选项卡式窗格,则必须堆叠选项卡时,内容高度会降低。

当政策设置为SCROLL_TAB_LAYOUT时,标签栏的高度是固定的,布局大部分都是正确的。但是,根据外观和感觉,标签内容的大小会减少几个像素。我已经能够发现这是由于L& F定义的标签区域的插入,它们没有被考虑到标签窗格的首选大小计算中(参见here)。设置UIManager.getDefaults().put("TabbedPane.tabAreaInsets", new Insets(0,0,0,0))适用于某些L& F(例如Metal)但不适用于其他L& F(例如Nimbus)。

似乎只有以下选项:

  • 使用堆叠标签并为内容添加额外高度
  • 使用堆叠标签,并在空间不足时覆盖内容
  • 使用滚动标签并在标签内容的最小/首选尺寸上添加几个像素,使每个L& F看起来有点不同(但至少内容不应该被切断)
  • 使用滚动标签并将选项卡式窗格的UI设置为新的BasicTabbedPaneUI,看起来不太好

是否有一种干净的方法可以强制标签式窗格的内容始终以固定的高度显示?

以下代码和屏幕截图说明了问题

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;

public class TabbedPaneTest extends JFrame {

    TabbedPaneTest() {

        JPanel mainPanel = new JPanel();
        mainPanel.setBackground(Color.white);

        JTabbedPane tabs = new JTabbedPane();
        //tabs.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); // content cut off by a few pixels
        tabs.setTabPlacement(JTabbedPane.BOTTOM);

        Dimension min  = new Dimension(100,200);
        Dimension max = new Dimension(Short.MAX_VALUE,Short.MAX_VALUE);
        //Dimension pref = new Dimension(Short.MAX_VALUE,200); // content cut off when small
        Dimension pref = new Dimension(0,200); // content gets extra space when large

        int tabCount = 3;
        for (int i = 0; i < tabCount; i++) {
            JLabel content = new JLabel();
            content.setMinimumSize(min);
            content.setMaximumSize(max);
            content.setPreferredSize(pref);
            tabs.addTab("lorem ipsum dolor sit amet", content);
        }

        // set up and render window
        getContentPane().add(mainPanel, BorderLayout.CENTER);
        getContentPane().add(tabs, BorderLayout.PAGE_END);
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("JScrollPane Test");
        pack();
        setSize(700,400);

        tabs.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                JTabbedPane tabbedPane = (JTabbedPane) e.getComponent();
                int tabCount = tabbedPane.getTabCount();
                for (int i = 0; i < tabCount; i++) {
                    Component c = tabbedPane.getComponentAt(i);
                    ((JLabel) c).setText("<html>"
                            + getSizes(c, "content")
                            + getSizes(tabbedPane, "tabs")
                            + "</html>");
                }
            }
        });
    }

    private static String getSizes(Component c, String name) {
        return "<p>" + name + " - "
                + "  minimum:" + Integer.toString(c.getMinimumSize().width)
                         + "x" + Integer.toString(c.getMinimumSize().height)
                + "  maximum:" + Integer.toString(c.getMaximumSize().width)
                         + "x" + Integer.toString(c.getMaximumSize().height)
                + "  preferred:" + Integer.toString(c.getPreferredSize().width)
                         + "x" + Integer.toString(c.getPreferredSize().height)
                + "  actual:" + Integer.toString(c.getSize().width)
                         + "x" + Integer.toString(c.getSize().height)
                + "</p>";
    }

    public static void main(String args[]) {

        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(TabbedPaneTest.class
                    .getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override public void run() {
                new TabbedPaneTest().setVisible(true);
            }
        });
    }

}

使用滚动标签,内容会被几个像素(此处为193px而不是200px)切断:

this bug report

使用堆叠标签和广泛内容时,窗口较小时会切断内容(此处为160px而不是200px):

this bug report

使用堆叠标签和窄内容,当窗口很大时,内容会变大(此处为240像素而不是200像素):

screenshot with scrolled tabs

1 个答案:

答案 0 :(得分:2)

在详细了解计算选项卡式窗格的首选大小的方式后,我能够为WRAP_TAB_LAYOUT案例提出以下解决方案。

问题在于,对于选项卡式窗格,首选高度和宽度是耦合的。计算首选宽度的首选高度,而不是实际的当前宽度。如果父级的布局管理器遵循首选高度而不是首选宽度,则会出现问题。

我想出的解决方案是设置一个侦听器,将每个选项卡内容的首选宽度设置为当前宽度。这样,选项卡式窗格正确计算其首选高度,并且可以例如布局。使用BorderLayout。

tabs.addComponentListener(new ComponentAdapter() {
    @Override
    public void componentResized(ComponentEvent e) {
        JTabbedPane tabbedPane = (JTabbedPane) e.getComponent();
        int tabCount = tabbedPane.getTabCount();
        for (int i = 0; i < tabCount; i++) {
            Component c = tabbedPane.getComponentAt(i);
            c.setPreferredSize(new Dimension(c.getSize().width, c.getPreferredSize().height));
        }
    }
});

我还没有找到SCROLL_TAB_LAYOUT案件的满意解决方案,所以如果有人有想法,我们将不胜感激。