JTabbedPane:选项卡位置设置为LEFT但图标未对齐

我有一个JTabbedPane ,标签位置设置为LEFT。 问题是每个标签中的图标不是彼此垂直对齐的。

考虑这张图:

在此处输入图像描述

正如您所看到的,“Editar Protocolo”(第二个选项卡)的图标与“Distribuir Protocolo”(第一个选项卡)的图标并不完全对齐,而其他选项卡也会出现这种情况。 我希望所有图标都垂直对齐到左边。

这是我用来设置标签组件的代码:

 ... jtabbedPane.setTabComponentAt(1, configurarJtabbedPane("Editar Protocolo", iconEditarProtocolo)); ... public JLabel configurarJtabbedPane(String title, ImageIcon icon) { JLabel l = new JLabel(title); l.setIcon(icon); l.setIconTextGap(5); l.setHorizontalTextPosition(SwingConstants.RIGHT); return l; } 

代码从标签左侧的 Q&A: JTabbedPane:图标中提取。

我想要的是:LEFT中的图标ALL,而不是基于文本大小[…]

选项卡的内容以典型实现为中心,这是有道理的,因为在有效呈现选项卡之前,需要适合此内容的区域是不可预测的。 由于该区域取决于内容,并且不同的选项卡可能具有不同的标题长度,因此必须有关于如何呈现这些选项卡的策略。 标准是将选项卡内容居中并使选项卡区域适合此内容。 当我们有一个默认的选项卡式窗格,顶部有选项卡时,我们不关心图标/文本对齐方式:

top_tabbed_pa​​ne

唯一的问题可能是标签长度不同,但谁在乎呢? 毕竟,图标和文本是可见的,标签窗格看起来很好。 但是,当您将标签位置设置为LEFTRIGHT时,事物会有所不同,并且看起来没什么吸引力:

left_tabbed_pa​​ne

显然,这种默认行为是一个长期存在的问题,这里有一个非常有趣的讨论。 有些SO成员参与其中:@camickr,@ jackopatra,@ splungebob。 正如该文章中所讨论的,一个简单的解决方案是不可能的,并且提出了几种解决方法:基本上是自定义UI实现或使用面板作为渲染器并根据文本长度播放首选宽度/高度。 两种选择都涉及很多工作。

为了避免处理UI委托并利用setTabComponentAt(...)方法,我前段时间开始使用标签窗格扩展,我想在这里分享。 该方法基于渲染器的Swing概念:一个必须生成组件以呈现另一个组件的部分的类,目标是提供一种灵活的机制来添加自定义选项卡组件。

我在下面使用我的自定义选项卡式窗格包含了一个示例,这里是提供上述机制所需的所有接口/类的概述。

ITabRenderer界面

第一步是定义一个iterface来提供一个合同来呈现一个标签组件。

AbstractTabRenderer类

一个抽象类,提供基本方法来帮助getTabRendererComponent(...)方法实现。 这个抽象类有三个主要属性:

  • prototypeText :用于定义原型文本以生成默认渲染器组件。
  • prototypeIcon :用于定义原型图标以生成默认渲染器。
  • horizontalTextAlignment :选项卡的文本水平对齐方式。

请注意,此类是抽象的,因为它不实现getTabRendererComponent(...)方法。

DefaultTabRenderer类

通过扩展AbstractTabRenderer类的具体实现。 请注意,如果您想要包含一个关闭按钮,如教程演示中所示,那么这个类中的一些工作就足够了。 事实上,我已经这样做了,但我不会把这部分包括在内,而不是扩展这个(已经很大的)post。

JXTabbedPane

最后是选项卡式窗格的扩展,其中包括选项卡式渲染器支持并覆盖addTab(...)方法。

我使用这些PLAF运行了这个带有正面结果的例子:

  • WindowsLookAndFeel
  • WindowsClassicLookAndFeel
  • NimbusLookAndFeel
  • MetalLookAndFeel
  • SeaglassLookAndFeel

另外,如果您将标签位置从LEFT切换到TOP (默认)或BOTTOM,则所有标签仍然具有相同的宽度,从而解决了本答案第二段所述的问题。

 import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import javax.swing.Icon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; public class Demo { private void createAndShowGUI() { JXTabbedPane tabbedPane = new JXTabbedPane(JTabbedPane.LEFT); AbstractTabRenderer renderer = (AbstractTabRenderer)tabbedPane.getTabRenderer(); renderer.setPrototypeText("This text is a prototype"); renderer.setHorizontalTextAlignment(SwingConstants.LEADING); tabbedPane.addTab("Short", UIManager.getIcon("OptionPane.informationIcon"), createEmptyPanel(), "Information tool tip"); tabbedPane.addTab("Long text", UIManager.getIcon("OptionPane.warningIcon"), createEmptyPanel(), "Warning tool tip"); tabbedPane.addTab("This is a really long text", UIManager.getIcon("OptionPane.errorIcon"), createEmptyPanel(), "Error tool tip"); JFrame frame = new JFrame("Demo"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.add(tabbedPane); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } private JPanel createEmptyPanel() { JPanel dummyPanel = new JPanel() { @Override public Dimension getPreferredSize() { return isPreferredSizeSet() ? super.getPreferredSize() : new Dimension(400, 300); } }; return dummyPanel; } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new Demo().createAndShowGUI(); } }); } class JXTabbedPane extends JTabbedPane { private ITabRenderer tabRenderer = new DefaultTabRenderer(); public JXTabbedPane() { super(); } public JXTabbedPane(int tabPlacement) { super(tabPlacement); } public JXTabbedPane(int tabPlacement, int tabLayoutPolicy) { super(tabPlacement, tabLayoutPolicy); } public ITabRenderer getTabRenderer() { return tabRenderer; } public void setTabRenderer(ITabRenderer tabRenderer) { this.tabRenderer = tabRenderer; } @Override public void addTab(String title, Component component) { this.addTab(title, null, component, null); } @Override public void addTab(String title, Icon icon, Component component) { this.addTab(title, icon, component, null); } @Override public void addTab(String title, Icon icon, Component component, String tip) { super.addTab(title, icon, component, tip); int tabIndex = getTabCount() - 1; Component tab = tabRenderer.getTabRendererComponent(this, title, icon, tabIndex); super.setTabComponentAt(tabIndex, tab); } } interface ITabRenderer { public Component getTabRendererComponent(JTabbedPane tabbedPane, String text, Icon icon, int tabIndex); } abstract class AbstractTabRenderer implements ITabRenderer { private String prototypeText = ""; private Icon prototypeIcon = UIManager.getIcon("OptionPane.informationIcon"); private int horizontalTextAlignment = SwingConstants.CENTER; private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); public AbstractTabRenderer() { super(); } public void setPrototypeText(String text) { String oldText = this.prototypeText; this.prototypeText = text; firePropertyChange("prototypeText", oldText, text); } public String getPrototypeText() { return prototypeText; } public Icon getPrototypeIcon() { return prototypeIcon; } public void setPrototypeIcon(Icon icon) { Icon oldIcon = this.prototypeIcon; this.prototypeIcon = icon; firePropertyChange("prototypeIcon", oldIcon, icon); } public int getHorizontalTextAlignment() { return horizontalTextAlignment; } public void setHorizontalTextAlignment(int horizontalTextAlignment) { this.horizontalTextAlignment = horizontalTextAlignment; } public PropertyChangeListener[] getPropertyChangeListeners() { return propertyChangeSupport.getPropertyChangeListeners(); } public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { return propertyChangeSupport.getPropertyChangeListeners(propertyName); } public void addPropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(listener); } public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(propertyName, listener); } protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { PropertyChangeListener[] listeners = getPropertyChangeListeners(); for (int i = listeners.length - 1; i >= 0; i--) { listeners[i].propertyChange(new PropertyChangeEvent(this, propertyName, oldValue, newValue)); } } } class DefaultTabRenderer extends AbstractTabRenderer implements PropertyChangeListener { private Component prototypeComponent; public DefaultTabRenderer() { super(); prototypeComponent = generateRendererComponent(getPrototypeText(), getPrototypeIcon(), getHorizontalTextAlignment()); addPropertyChangeListener(this); } private Component generateRendererComponent(String text, Icon icon, int horizontalTabTextAlignmen) { JPanel rendererComponent = new JPanel(new GridBagLayout()); rendererComponent.setOpaque(false); GridBagConstraints c = new GridBagConstraints(); c.insets = new Insets(2, 4, 2, 4); c.fill = GridBagConstraints.HORIZONTAL; rendererComponent.add(new JLabel(icon), c); c.gridx = 1; c.weightx = 1; rendererComponent.add(new JLabel(text, horizontalTabTextAlignmen), c); return rendererComponent; } @Override public Component getTabRendererComponent(JTabbedPane tabbedPane, String text, Icon icon, int tabIndex) { Component rendererComponent = generateRendererComponent(text, icon, getHorizontalTextAlignment()); int prototypeWidth = prototypeComponent.getPreferredSize().width; int prototypeHeight = prototypeComponent.getPreferredSize().height; rendererComponent.setPreferredSize(new Dimension(prototypeWidth, prototypeHeight)); return rendererComponent; } @Override public void propertyChange(PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); if ("prototypeText".equals(propertyName) || "prototypeIcon".equals(propertyName)) { this.prototypeComponent = generateRendererComponent(getPrototypeText(), getPrototypeIcon(), getHorizontalTextAlignment()); } } } } 

截图

MetalLookAndFeel

在此处输入图像描述

NimbusLookAndFeel

在此处输入图像描述

SeaglassLookAndFeel

在此处输入图像描述

WindowsLookAndFeel

在此处输入图像描述

从你的片段中不清楚对齐是否出错。 TabComponentsDemo是一个完整的示例 ,演示了如何使用自定义组件创建选项卡 。 在ButtonTabComponent ,请注意如何为组件提供具有FlowLayout.LEFT对齐的FlowLayout.LEFT 。 您可以将其与当前方法进行比较。

使用HTML格式有一个更简单的解决方案:

 final String PRE_HTML = "

"; final String POST_HTML = "

"; tabbedpane.setTitleAt(0, PRE_HTML + "your title" + POST_HTML); tabbedpane.setTitleAt(2, PRE_HTML + "your title 2" + POST_HTML);

我所做的是为选项卡添加一个组件(在这种情况下为JPanel)。 我需要在选项卡中添加一个复选框,而不是复选框,您可以手动添加图标。

这是我的代码:

 private JPanel makeTabPanel(JCheckBox checkBox) { String title = checkBox.getText(); checkBox.setText(""); JPanel pane = new JPanel(); pane.setLayout(new BoxLayout(pane, BoxLayout.LINE_AXIS)); pane.add(checkBox); pane.add(new JLabel(title, SwingUtilities.LEFT)); pane.setMinimumSize(new Dimension(150, 25)); pane.setPreferredSize(new Dimension(180, 25)); pane.setMaximumSize(new Dimension(220, 25)); return pane; } //then I call it with tabbed.setTabComponentAt(0, makeTabPanel(checkIncluderesume)); 

我知道在面板上添加尺寸并不是一个好习惯,但这是我能找到的最简单的方法。 结果是:

样品