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

时间:2014-10-10 22:18:13

标签: java swing icons alignment jtabbedpane

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

考虑这张照片:

enter image description here

正如您所看到的,“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: icon on left side of tabs

4 个答案:

答案 0 :(得分:11)

  

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

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

top_tabbed_pane

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

left_tabbed_pane

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

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

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

ITabRenderer界面

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

AbstractTabRenderer类

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

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

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

DefaultTabRenderer类

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

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

enter image description here

<强> NimbusLookAndFeel

enter image description here

<强> SeaglassLookAndFeel

enter image description here

<强> WindowsLookAndFeel

enter image description here

答案 1 :(得分:2)

从你的片段中不清楚对齐是否出错。 TabComponentsDemocomplete example,说明了如何使用自定义组件创建标签。在ButtonTabComponent中,请注意如何为组件提供FlowLayout FlowLayout.LEFT对齐方式。您可以将其与当前的方法进行比较。

答案 2 :(得分:1)

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

final String PRE_HTML = "<html><p style=\"text-align: left; width: 230px\">"; 
final String POST_HTML = "</p></html>"; 

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

答案 3 :(得分:1)

我所做的是为标签添加一个组件(在本例中为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));

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

sample

相关问题