嵌套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); } } } }
解决建议
- 修正布局管理器:确保JTabbedPane的父容器使用
BorderLayout,避免默认FlowLayout导致组件无限制扩张。示例代码:
JFrame frame = new JFrame(); frame.setLayout(new BorderLayout()); frame.add(yourTabbedPane, BorderLayout.CENTER);
- 限制标签区域宽度:在自定义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); }
- 启用标签滚动:开启JTabbedPane的滚动标签布局,避免标签过多时撑大容器:
setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
- 约束组件尺寸:给JTabbedPane设置合适的首选和最大尺寸,结合布局管理器确保不会超出屏幕:
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setPreferredSize(new Dimension(screenSize.width - 50, screenSize.height - 50)); setMaximumSize(getPreferredSize());
- 检查嵌套标签的容器:如果是在某个标签页内添加嵌套JTabbedPane,确保该标签页的面板使用
BorderLayout,并将嵌套标签页放在CENTER位置:
e4cPanel.setLayout(new BorderLayout()); e4cPanel.add(nestedTabbedPane, BorderLayout.CENTER);
内容的提问来源于stack exchange,提问作者GamingLegion
相关产品推荐
相关产品推荐

