You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套JTabbedPane新增标签后超出屏幕边界的技术问题

问题:嵌套JTabbedPane添加标签后边界超出屏幕

我正在开发一款清单类程序,使用嵌套的JTabbedPane组件。此前一切正常,但在某标签下添加另一组标签后,整体JTabbedPane的边界开始扩大并超出屏幕。

添加额外标签前的面板状态:
添加前的面板状态

添加额外标签后的面板状态:
添加后的面板状态

我怀疑存在标签数量限制,但其他顶层标签页包含更多右侧标签,这一猜测并不合理。

初始化代码

private void init() {
        e4cPanel = new JPanel();
        e4cPanel.setBackground(back);

        gceaScroll = new JScrollPane();
        gceaScroll.setBackground(back);
        gceaScroll.setBorder(null);
        gceaScroll.setHorizontalScrollBar(null);
        gceaScroll.setVerticalScrollBar(new JCustomScrollBar());
        gceaScroll.getVerticalScrollBar().setUnitIncrement(25);
        gceaPanel = new JPanel();
        gceaPanel.setBackground(back);
        gceaScroll.setViewportView(gceaPanel);

        formScroll = new JScrollPane();
        formScroll.setBackground(back);
        formScroll.setBorder(null);
        formScroll.setHorizontalScrollBar(null);
        formScroll.setVerticalScrollBar(new JCustomScrollBar());
        formScroll.getVerticalScrollBar().setUnitIncrement(25);
        formPanel = new JPanel();
        formPanel.setBackground(back);
        formScroll.setViewportView(formPanel);

        legScroll = new JScrollPane();
        legScroll.setBackground(back);
        legScroll.setBorder(null);
        legScroll.setHorizontalScrollBar(null);
        legScroll.setVerticalScrollBar(new JCustomScrollBar());
        legScroll.getVerticalScrollBar().setUnitIncrement(25);
        legPanel = new JPanel();
        legPanel.setBackground(back);
        legScroll.setViewportView(legPanel);

        mytScroll = new JScrollPane();
        mytScroll.setBackground(back);
        mytScroll.setBorder(null);
        mytScroll.setHorizontalScrollBar(null);
        mytScroll.setVerticalScrollBar(new JCustomScrollBar());
        mytScroll.getVerticalScrollBar().setUnitIncrement(25);
        mytPanel = new JPanel();
        mytPanel.setBackground(back);
        mytScroll.setViewportView(mytPanel);

        giftScroll = new JScrollPane();
        giftScroll.setBackground(back);
        giftScroll.setBorder(null);
        giftScroll.setHorizontalScrollBar(null);
        giftScroll.setVerticalScrollBar(new JCustomScrollBar());
        giftScroll.getVerticalScrollBar().setUnitIncrement(25);
        giftPanel = new JPanel();
        giftPanel.setBackground(back);
        giftScroll.setViewportView(giftPanel);

        colScroll = new JScrollPane();
        colScroll.setBackground(back);
        colScroll.setBorder(null);
        colScroll.setHorizontalScrollBar(null);
        colScroll.setVerticalScrollBar(new JCustomScrollBar());
        colScroll.getVerticalScrollBar().setUnitIncrement(25);
        colPanel = new JPanel();
        colPanel.setBackground(back);
        colScroll.setViewportView(colPanel);

        upgScroll = new JScrollPane();
        upgScroll.setBackground(back);
        upgScroll.setBorder(null);
        upgScroll.setHorizontalScrollBar(null);
        upgScroll.setVerticalScrollBar(new JCustomScrollBar());
        upgScroll.getVerticalScrollBar().setUnitIncrement(25);
        upgPanel = new JPanel();
        upgPanel.setBackground(back);
        upgScroll.setViewportView(upgPanel);

        batScroll = new JScrollPane();
        batScroll.setBackground(back);
        batScroll.setBorder(null);
        batScroll.setHorizontalScrollBar(null);
        batScroll.setVerticalScrollBar(new JCustomScrollBar());
        batScroll.getVerticalScrollBar().setUnitIncrement(25);
        batPanel = new JPanel();
        batPanel.setBackground(back);
        batScroll.setViewportView(batPanel);

        tmScroll = new JScrollPane();
        tmScroll.setBackground(back);
        tmScroll.setBorder(null);
        tmScroll.setHorizontalScrollBar(null);
        tmScroll.setVerticalScrollBar(new JCustomScrollBar());
        tmScroll.getVerticalScrollBar().setUnitIncrement(25);
        tmPanel = new JPanel();
        tmPanel.setBackground(back);
        tmScroll.setViewportView(tmPanel);

        hmPanel = new JPanel();
        hmPanel.setBackground(back);

        berryScroll = new JScrollPane();
        berryScroll.setBackground(back);
        berryScroll.setBorder(null);
        berryScroll.setHorizontalScrollBar(null);
        berryScroll.setVerticalScrollBar(new JCustomScrollBar());
        berryScroll.getVerticalScrollBar().setUnitIncrement(25);
        berryPanel = new JPanel();
        berryPanel.setBackground(back);
        berryScroll.setViewportView(berryPanel);

        setTabPlacement(JTabbedPane.RIGHT);
        addTab("Elite 4/Champion", e4cPanel);
        addTab("Gotta Catch 'Em All", gceaScroll);
        addTab("Forms", formScroll);
        addTab("Legendaries", legScroll);
        addTab("Mythicals", mytScroll);
        addTab("Mystery Gifts", giftScroll);
        addTab("Collectables", colScroll);
        addTab("Upgrades", upgScroll);
        addTab("Battle", batScroll);
//      addTab("TM's", tmScroll);
//      addTab("HM's", hmPanel);
//      addTab("Berries", berryScroll);
}

自定义标签面板UI代码

package customs.tabs;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import javax.swing.JComponent;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.metal.MetalTabbedPaneUI;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.TimingTarget;
import org.jdesktop.animation.timing.interpolation.PropertySetter;

@SuppressWarnings("serial")
public class MaterialTabbed extends JTabbedPane {
    private final int orientation;

    public MaterialTabbed(int orientation) {
        this.orientation = orientation;
        MaterialTabbedUI ui = new MaterialTabbedUI();
        setTabPlacement(orientation);
        setUI(ui);
    }

    public class MaterialTabbedUI extends MetalTabbedPaneUI {
        private Animator animator;
        private Rectangle currentRectangle;
        private TimingTarget target;

        public void setCurrentRectangle(Rectangle currentRectangle) {
            this.currentRectangle = currentRectangle;
            repaint();
        }

        public MaterialTabbedUI() {
        }

        @Override
        public void installUI(JComponent jc) {
            super.installUI(jc);
            animator = new Animator(500);
            animator.setResolution(0);
            animator.setAcceleration(.5f);
            animator.setDeceleration(.5f);
            tabPane.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent ce) {
                    int selected = tabPane.getSelectedIndex();
                    if (selected != -1) {
                        if (currentRectangle != null) {
                            if (animator.isRunning()) {
                                animator.stop();
                            }
                            animator.removeTarget(target);
                            target = new PropertySetter(MaterialTabbedUI.this, "currentRectangle", currentRectangle, getTabBounds(selected, calcRect));
                            animator.addTarget(target);
                            animator.start();
                        }
                    }
                }
            });
        }

        @Override
        protected Insets getTabInsets(int i, int i1) {
            if(orientation == 1 || orientation == 3) {
                return new Insets(10, 20, 10, 20);
            } else if(orientation == 2 || orientation == 4) {
                return new Insets(7, 10, 7, 10);
            } else {
                return new Insets(10, 10, 10, 10);
            }
        }

        @Override
        protected void paintTabBorder(Graphics grphcs, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
            Graphics2D g2 = (Graphics2D) grphcs.create();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setColor(new Color(3, 155, 216));
            if (currentRectangle == null || !animator.isRunning()) {
                if (isSelected) {
                    currentRectangle = new Rectangle(x, y, w, h);
                }
            }
            if (currentRectangle != null) {
                if (tabPlacement == TOP) {
                    g2.fillRect(currentRectangle.x, currentRectangle.y + currentRectangle.height - 3, currentRectangle.width, 3);
                } else if (tabPlacement == BOTTOM) {
                    g2.fillRect(currentRectangle.x, currentRectangle.y, currentRectangle.width, 3);
                } else if (tabPlacement == LEFT) {
                    g2.fillRect(currentRectangle.x + currentRectangle.width - 3, currentRectangle.y, 3, currentRectangle.height);
                } else if (tabPlacement == RIGHT) {
                    g2.fillRect(currentRectangle.x, currentRectangle.y, 3, currentRectangle.height);
                }
            }
            g2.dispose();
        }

        @Override
        protected void paintContentBorder(Graphics grphcs, int tabPlacement, int selectedIndex) {
            Graphics2D g2 = (Graphics2D) grphcs.create();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setColor(new Color(200, 200, 200));
            Insets insets = getTabAreaInsets(tabPlacement);
            int width = tabPane.getWidth();
            int height = tabPane.getHeight();
            if (tabPlacement == TOP) {
                int tabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
                g2.drawLine(insets.left, tabHeight, width - insets.right - 1, tabHeight);
            } else if (tabPlacement == BOTTOM) {
                int tabHeight = height - calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
                g2.drawLine(insets.left, tabHeight, width - insets.right - 1, tabHeight);
            } else if (tabPlacement == LEFT) {
                int tabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
                g2.drawLine(tabWidth, insets.top, tabWidth, height - insets.bottom - 1);
            } else if (tabPlacement == RIGHT) {
                int tabWidth = width - calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - 1;
                g2.drawLine(tabWidth, insets.top, tabWidth, height - insets.bottom - 1);
            }
            g2.dispose();
        }

        @Override
        protected void paintFocusIndicator(Graphics grphcs, int i, Rectangle[] rctngls, int i1, Rectangle rctngl, Rectangle rctngl1, boolean bln) {

        }

        @Override
        protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
            if (tabPane.isOpaque()) {
                super.paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
            }
        }
    }
}

解决建议

  1. 修正布局管理器:确保JTabbedPane的父容器使用BorderLayout,避免默认FlowLayout导致组件无限制扩张。示例代码:
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.add(yourTabbedPane, BorderLayout.CENTER);
  1. 限制标签区域宽度:在自定义UI中重写calculateTabAreaWidth方法,给右侧标签区域设置最大宽度,防止超出屏幕:
@Override
protected int calculateTabAreaWidth(int tabPlacement, int runCount, int maxTabWidth) {
    int calculatedWidth = super.calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
    // 限制最大宽度为屏幕宽度的1/4
    int maxAllowedWidth = (int) (Toolkit.getDefaultToolkit().getScreenSize().width * 0.25);
    return Math.min(calculatedWidth, maxAllowedWidth);
}
  1. 启用标签滚动:开启JTabbedPane的滚动标签布局,避免标签过多时撑大容器:
setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
  1. 约束组件尺寸:给JTabbedPane设置合适的首选和最大尺寸,结合布局管理器确保不会超出屏幕:
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setPreferredSize(new Dimension(screenSize.width - 50, screenSize.height - 50));
setMaximumSize(getPreferredSize());
  1. 检查嵌套标签的容器:如果是在某个标签页内添加嵌套JTabbedPane,确保该标签页的面板使用BorderLayout,并将嵌套标签页放在CENTER位置:
e4cPanel.setLayout(new BorderLayout());
e4cPanel.add(nestedTabbedPane, BorderLayout.CENTER);

内容的提问来源于stack exchange,提问作者GamingLegion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 23:47:33