选项卡自定义JTabbedPane中的渲染顺序

时间:2017-01-10 10:31:28

标签: java swing tabs rendering jtabbedpane

嗨大家好,祝你2017年第一篇文章新年快乐! :)

问题解释

我目前正在编写自定义JTabbedPane并且一切正常,但在呈现标签时,我遇到了一个意外(对我来说)设计问题。

问题是所有未选中的选项卡都是从左到右渲染的,因为我使用GeneralPath类自定义的形状超出了默认的选项卡边界,所以每个选项卡都会重叠其左侧选项卡的一部分。您可以在下图中查看它:

enter image description here

正如您所看到的,选定的选项卡与来自右侧的任何选项卡重叠,但未选中的选项卡,如命名的" CustomPanel2"在下一个标签之前呈现,依此类推。

问题

我看到一篇帖子讨论了来自paintTab课程的BasicTabbedPaneUI方法,这是我正在使用的方法,但我无法实现如何做,所以我希望你能展示我是正确的方式来呈现标签,以获得谷歌浏览器中的标签,如下所示:

enter image description here

提前感谢您,祝您有个愉快的一天! ;)

PD:我认为没有相关的代码可以添加。如果您需要,请向我询问。

2 个答案:

答案 0 :(得分:3)

如何设计未选中五边形的标签?

pentagon

screenshot

注意:此示例不会在设置JTabbedPane#setTabLayoutPolicy (JTabbedPane.WRAP_TAB_LAYOUT)JTabbedPane#setTabPlacement (JTabbedPane.BOTTOM)时进行测试:

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;

public class TabsOverlapTest {
  private JComponent makeUI() {
    Color selectedTabColor = UIManager.getColor("TabbedPane.selected");
    Color tabBackgroundColor = Color.LIGHT_GRAY;
    Color tabBorderColor = Color.GRAY;
    UIManager.put("TabbedPane.highlight", tabBorderColor);

    JTabbedPane tabs = new JTabbedPane();
    tabs.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
    tabs.setUI(new BasicTabbedPaneUI() {
      @Override protected void paintTabBorder(
          Graphics g, int tabPlacement, int tabIndex,
          int x, int y, int w, int h, boolean isSelected) {
      }
      @Override protected void paintFocusIndicator(
          Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex,
          Rectangle iconRect, Rectangle textRect, boolean isSelected) {
      }
      @Override protected void paintContentBorderTopEdge(
          Graphics g, int tabPlacement, int selectedIndex,
          int x, int y, int w, int h) {
        super.paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
        Rectangle selRect = getTabBounds(selectedIndex, calcRect);
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setColor(selectedTabColor);
        g2.drawLine(selRect.x - 2, y, selRect.x + selRect.width + 2, y);
        g2.dispose();
      }
      @Override protected void paintTabBackground(
          Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h,
          boolean isSelected) {

        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        int a = isSelected ? 0 : 1;

        GeneralPath shape = new GeneralPath();
        shape.moveTo(x - 3, y + h);
        shape.lineTo(x + 3, y + a);
        shape.lineTo(x + w - 3, y + a);
        shape.lineTo(x + w + 3, y + h);
        shape.closePath();
        g2.setColor(isSelected ? selectedTabColor : tabBackgroundColor);
        g2.fill(shape);

        GeneralPath border = new GeneralPath();
        if (isSelected || tabIndex == 0) {
          border.moveTo(x - 3, y + h - 1);
        } else {
          border.moveTo(x + 3, y + h - 1);
          border.lineTo(x, (y + h - 1) / 2);
        }
        border.lineTo(x + 3, y + a);
        border.lineTo(x + w - 3, y + a);
        border.lineTo(x + w + 3, y + h - 1);

        g2.setColor(tabBorderColor);
        g2.draw(border);

        g2.dispose();
      }
    });
    tabs.addTab("JTextArea",  new JScrollPane(new JTextArea()));
    tabs.addTab("JTree",      new JScrollPane(new JTree()));
    tabs.addTab("JButton",    new JButton("button"));
    tabs.addTab("JSplitPane", new JSplitPane());
    return tabs;
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new TabsOverlapTest().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

答案 1 :(得分:1)

好吧,我终于通过覆盖paintTabArea类的BasicTabbedPaneUI方法找到了解决方案。

默认代码为:

protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
    int tabCount = tabPane.getTabCount();

    Rectangle iconRect = new Rectangle(),
              textRect = new Rectangle();
    Rectangle clipRect = g.getClipBounds();

    for (int i = runCount - 1; i >= 0; i--) {
        int start = tabRuns[i];
        int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
        int end = (next != 0? next - 1: tabCount - 1);
        for (int j = start; j <= end; j++) {
            if (j != selectedIndex && rects[j].intersects(clipRect)) {
                paintTab(g, tabPlacement, rects, j, iconRect, textRect);
            }
        }
    }

    if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
        paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
    }

}

在第二个for声明中,您会看到以下情况:

(int j = start; j <= end; j++)

要切换标签呈现顺序,您只需将该条件更改为:

(int j = end; j >= start; j--)