java - JTabbedPane自定义组件布局

标签 java swing jtabbedpane

我正在看这个例子Using Swing Components; Examples 。此示例显示带有自定义选项卡组件的 JTabbedPane,该组件在每个选项卡中都有关闭按钮。

如果 JTabbedPane 位于 WRAP_TAB_LAYOUT 中,则选项卡标题和关闭按钮位于宽选项卡的中间。如何更改此示例,使选项卡标题仍然在选项卡中心可见,但关闭按钮将出现在选项卡右边框旁边?

这是一张图片:

enter image description here

最佳答案

  • 这是使用 UIManager.put("TabbedPane.tabInsets", insets)JLayer 的一种可能实现
    • 使用 JLayer 在选项卡的内边缘绘制关闭按钮
    • 我没有在 NumbusLookAndFeel 上进行测试

enter image description here

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.*;

public class RightCloseTabButtonTest {
  public JComponent makeUI() {
    JTabbedPane tabs = new JTabbedPane();
    tabs.addTab("Tab 1", new JLabel("aaa"));
    tabs.addTab("Tab 2", new JLabel("bbb"));
    tabs.addTab("Tab 3", new JLabel("ccc"));
    tabs.addTab("Tab 4", new JLabel("ddd"));
    return new JLayer<JTabbedPane>(tabs, new CloseableTabbedPaneLayerUI());
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      UIManager.put("TabbedPane.tabInsets", new Insets(2, 2 + 16, 2, 2 + 16));
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new RightCloseTabButtonTest().makeUI());
      f.setSize(300, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class CloseTabIcon implements Icon {
  @Override public void paintIcon(Component c, Graphics g, int x, int y) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.translate(x, y);
    g2.setPaint(Color.BLACK);
    if (c instanceof AbstractButton) {
      ButtonModel m = ((AbstractButton) c).getModel();
      if (m.isRollover()) {
        g2.setPaint(Color.ORANGE);
      }
    }
    g2.drawLine(4,  4, 11, 11);
    g2.drawLine(4,  5, 10, 11);
    g2.drawLine(5,  4, 11, 10);
    g2.drawLine(11, 4,  4, 11);
    g2.drawLine(11, 5,  5, 11);
    g2.drawLine(10, 4,  4, 10);
    g2.dispose();
  }
  @Override public int getIconWidth() {
    return 16;
  }
  @Override public int getIconHeight() {
    return 16;
  }
}

class CloseableTabbedPaneLayerUI extends LayerUI<JTabbedPane> {
  private final JComponent rubberStamp = new JPanel();
  private final Point pt = new Point();
  private final JButton button = new JButton(new CloseTabIcon()) {
    @Override public void updateUI() {
      super.updateUI();
      setBorder(BorderFactory.createEmptyBorder());
      setFocusPainted(false);
      setBorderPainted(false);
      setContentAreaFilled(false);
      setRolloverEnabled(false);
    }
  };
  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (c instanceof JLayer) {
      JLayer jlayer = (JLayer) c;
      JTabbedPane tabPane = (JTabbedPane) jlayer.getView();
      Dimension d = button.getPreferredSize();
      for (int i = 0; i < tabPane.getTabCount(); i++) {
        Rectangle rect = tabPane.getBoundsAt(i);
        int x = rect.x + rect.width - d.width - 2;
        int y = rect.y + (rect.height - d.height) / 2;
        Rectangle r = new Rectangle(x, y, d.width, d.height);
        button.getModel().setRollover(r.contains(pt));
        SwingUtilities.paintComponent(g, button, rubberStamp, r);
      }
    }
  }
  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(
          AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }
  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }
  @Override protected void processMouseEvent(
      MouseEvent e, JLayer<? extends JTabbedPane> l) {
    if (e.getID() == MouseEvent.MOUSE_CLICKED) {
      pt.setLocation(e.getPoint());
      JTabbedPane tabbedPane = (JTabbedPane) l.getView();
      int index = tabbedPane.indexAtLocation(pt.x, pt.y);
      if (index >= 0) {
        Rectangle rect = tabbedPane.getBoundsAt(index);
        Dimension d = button.getPreferredSize();
        int x = rect.x + rect.width - d.width - 2;
        int y = rect.y + (rect.height - d.height) / 2;
        Rectangle r = new Rectangle(x, y, d.width, d.height);
        if (r.contains(pt)) {
          tabbedPane.removeTabAt(index);
        }
      }
    }
  }
  @Override protected void processMouseMotionEvent(
      MouseEvent e, JLayer<? extends JTabbedPane> l) {
    pt.setLocation(e.getPoint());
    JTabbedPane t = (JTabbedPane) l.getView();
    if (t.indexAtLocation(pt.x, pt.y) >= 0) {
      Point loc = e.getPoint();
      loc.translate(-16, -16);
      l.repaint(new Rectangle(loc, new Dimension(32, 32)));
    }
  }
}

关于java - JTabbedPane自定义组件布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38290568/

相关文章:

java - 有没有比在单个 JFrame 中保留多个 JTabbedPanes 更好的方法?

java - JTabbedPane 与 GridBagLayout

java - 我无法使用 spring 框架将 Hibernate Validator 集成到 javaweb 项目中

java - EventQueue.invokeLater 与 Thread.start

java - 将变量从一个类传递到 paintComponent 类

Java JTabbedPane 允许 Tab 仅在 true 时切换到

java - 使用 POST 将数据从 Java(Android) 发送到 PHP

OSX 应用程序包中的 Java swixml

java - Java 8 与 Java 9 中的 Stream.peek() 方法

java - 如何获得转换文本的边界框(java)?