在写这个问题时,我已经找到了一种使它按照我想要的方式工作的方法。基于this,我仍然在发布问题,因为其他人可能会遇到类似的问题。

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

使用默认的WRAP_TAB_LAYOUT,选项卡窗格的首选大小不会考虑这些选项卡当前是否实际上是堆叠在一起或彼此相邻显示,如hereherethis bug report中所述。如果选项卡式窗格是基于堆叠的选项卡进行布局的,则当有足够的空间可让这些选项卡彼此相邻显示时,添加的每个选项卡的内容高度都会增加大约20个像素(一个选项卡的高度)。如果基于彼此相邻显示的选项卡对选项卡式窗格进行布局,则当必须堆叠选项卡时,内容高度会降低。

当策略设置为SCROLL_TAB_LAYOUT时,选项卡栏的高度是固定的,布局基本上是正确的。但是,根据外观,选项卡内容的大小会减少几个像素。我已经发现这是由于L&F定义的选项卡区域的插入而引起的,这些未计入选项卡窗格的首选大小计算中(请参见this bug report)。设置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):

    java - JTabbedPane的尺寸问题和固定高度的内容-LMLPHP

    使用堆叠的标签和宽内容,当窗口较小时(此处为160px而不是200px),内容将被切断:

    java - JTabbedPane的尺寸问题和固定高度的内容-LMLPHP

    使用堆叠的标签页和狭窄的内容,当窗口较大时,内容将变大(240px,而不是200px):

    java - JTabbedPane的尺寸问题和固定高度的内容-LMLPHP

    最佳答案

    在阅读了有关计算选项卡式窗格的首选大小的方式的更多信息之后,我已经能够针对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案件,我还没有找到令人满意的解决方案,因此,如果有人有想法,将不胜感激。

    10-06 09:02
    查看更多