JTabbedPane 自定义选项卡外观

9
我想自定义JTabbedPane标签的外观。
我想从最简单和最朴素的行为开始:无边框,纯色。
问题在于非纯净性仍然存在:选项卡略微重叠的边距。

enter image description here

你可以看到,由于选择了第二个选项卡,它被“带到前面”。 这是通过轻微的边距重叠实现的。 有没有一种(不棘手的)方法来禁用此行为?
以下是简单的、可测试的代码:
public class TabbedPane_LookStudy extends JFrame{

public static void main(String [] args) throws UnsupportedLookAndFeelException {
    UIManager.setLookAndFeel(new NimbusLookAndFeel());
    new TabbedPane_LookStudy().setVisible(true);
}

public TabbedPane_LookStudy() {
    JTabbedPane tp = new JTabbedPane();
    tp.setUI(new MyTabbedPaneUI());
    add(tp);

    tp.addTab("first",new JPanel());
    tp.addTab("second", new JPanel());
    tp.addTab("third", new JPanel());

    setPreferredSize(new Dimension(180,100));
    pack();
}

public static class MyTabbedPaneUI extends javax.swing.plaf.basic.BasicTabbedPaneUI {

    @Override
    protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, 
               int tabIndex, Rectangle iconRect, Rectangle textRect) {
        Color savedColor = g.getColor();
        g.setColor(Color.PINK);
        g.fillRect(rects[tabIndex].x, rects[tabIndex].y, 
               rects[tabIndex].width, rects[tabIndex].height);
        g.setColor(Color.BLUE);
        g.drawRect(rects[tabIndex].x, rects[tabIndex].y, 
               rects[tabIndex].width, rects[tabIndex].height);
        g.setColor(savedColor);
    }
 }

}

4个回答

4

我从那篇文章开始。但在那个UI实现中,只有paintXXX方法、calculateTabWidth和calculateTabHeight,而没有选项卡放置。因此,我认为选项卡放置逻辑必须包含在其他地方。 - AgostinoX
什么是“非制表符放置”,你可以从顶部经过边框绘制到底部,而制表符放置在顶部吗? - mKorbel
抱歉,mKorbel,我不明白。你能换个说法吗? - AgostinoX

2

第一(部分)解决方案。 我已经找到了“定位”代码。
它是BasicTabbedPaneUI的内部类TabbedPaneLayout中的calculateTabRects方法。该方法非常复杂,但好消息是,“将选项卡提前”部分很好地注释并隔离在自己的可重写方法中!它是padSelectedTab

创建一个什么也不做的类,而不是提高组件就像这样简单:

    protected class MyTabbedPaneLayout extends TabbedPaneLayout {
        @Override
        protected void padSelectedTab(int tabPlacement, int selectedIndex) {
            //do nothing!
            //super.padSelectedTab(tabPlacement, selectedIndex);
        }
    }
请注意,它必须是MyTabbedPane的内部类。它必须通过覆盖MyTabbedPane.createLayoutManager进行实例化。
@Override
    protected LayoutManager createLayoutManager() {
        //return super.createLayoutManager();
         return new MyTabbedPaneLayout();
    }

非常简单且实际可行...除了一个例外情况。 如果tabLayoutPolicy是WRAP_TAB_LAYOUT,则createLayoutManager将实例化TabbedPaneLayout,但如果tabLayoutPolicy是SCROLL_TAB_LAYOUT,则将实例化TabbedPanelScrollLayout。后者具有私有而不是受保护的访问权限,因此无法对其进行子类化! 我的createLayoutManager实现失去了可滚动行为。

2
您可以将Html标签放入第一个参数中,如下所示:

MyJTabbedPane.addTab("<html><h1 style='padding:20px;'>测试</h1></html>", new JPanel());


注意:请保留HTML标签。

1
你可以在MyTabbedPaneUI中覆盖paintContentBorderTopEdge方法,这样它就不会认为任何选项卡被选中。这不是一个很好的解决方案,但根据我的经验,对UI类进行黑客攻击很少有一个好的解决方案 :)
@Override
protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
                       int selectedIndex, int x, int y, int w, int h) {
    super.paintContentBorderTopEdge(g, tabPlacement, -1, x, y, w, h);
}

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