JTabbedPane的:标签位置设置为离开,但图标不对齐设置为、图标、位置、标签

2023-09-11 07:41:32 作者:挽留与走

我有一个 JTabbedPane的与标签位置设置为左边。的问题是,在每个标签的图标时不会纵向彼此对齐。

考虑一下这样的画面:

正如你所看到的Editar Protocolo(第二个选项卡)的不完全与Distribuir P​​rotocolo(第一个选项卡)的图标排列图标,这也发生在其他选项卡。我想所有图标垂直靠左对齐。

这是在code我使用设置选项卡组件:

  ...
jtabbedPane.setTabComponentAt(1,configurarJtabbedPane(Editar Protocolo,iconEditarProtocolo));
...

公众的JLabel configurarJtabbedPane(字符串名称,ImageIcon的图标){
    JLabel的L =新的JLabel(职称);
    l.setIcon(图标);
    l.setIconTextGap(5);
    l.setHorizo​​ntalTextPosition(SwingConstants.RIGHT);
    返回L;
}
 

在code从这个Q&功放提取; A:JTabbedPane:在标签左侧的图标。

解决方案   

我要什么:ALL左侧的图标,不是基于文本大小   [...] 的

该选项卡的内容是由典型的实现为中心的,这是有道理的,因为要符合这个内容所需的面积是联合国predictable直到卡被有效呈现。由于该地区依赖于内容和不同的标签将可能有不同的标题的长度,则必须有一个关于如何呈现这些选项卡的政策。该标准是居中的选项卡的内容,并符合标签区域此内容。当我们与放置在顶部的标签默认标签面板,我们不会在意图标/文本对齐方式:

唯一要担心的可能是具有不同长度的标签,但谁在乎呢?毕竟,图标和文字是可见的,标签式面板看上去不够好。但是,当您设置的选项卡放置到的左或右的东西是不同的,它看起来没有吸引力:

显然,这个默认的行为是一个长期存在的问题,有一个非常有趣的讨论这里。有些组织成员参与有:@camickr,@kleopatra,@splungebob。由于在该职位所讨论的,一个简单的解决方案是不可能的,和​​几个解决方法,提出:基本的自定义UI实现或使用板作为渲染器和preferred宽度/高度基于文本长度播放。这两个方案涉及相当多的工作。

如何在 Windows 11 上将任务栏图标移到左侧

为了避免处理UI代表和利用 setTabComponentAt的(...)的方法,我已经开始一段时间前一个标签面板扩展,我倒要在这里分享。该方法是基于渲染的摇摆概念:即必须产生一个分量呈现另一个组件的部分类,我们的目标是提供一种灵活的机制来添加自定义选项卡组件

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

ITabRenderer接口

第一步骤是定义一iterface提供一个合同来呈现一个选项卡组件。

AbstractTabRenderer类

这是抽象类,提供基础的方法在 getTabRendererComponent(...)方法实现帮助。这个抽象类主要有三个特性:

prototypeText :用来定义一个原型的文本生成一个默认的渲染器​​组件 prototypeIcon :用于定义一个原型图标生成默认渲染器 horizo​​ntalTextAlignment :标签的文字水平对齐方式

请注意这个类是抽象的,因为它没有实现 getTabRendererComponent(...)方法。

DefaultTabRenderer类

一个具体实施延长 AbstractTabRenderer 类。请注意,如果要包括如图教程演示关闭按钮,然后在这个类中的一些工作就足够了。作为事实上,我已经做到了,但我不会包括部分不延伸这个(已大)后。

JXTabbedPane

最后的选项卡面板的扩展,其中包括标签渲染器的支持和覆盖 addTab(...)的方法。

示例

我遇到这个例子中使用这些PLAFs积极的成果:

WindowsLookAndFeel WindowsClassicLookAndFeel NimbusLookAndFeel MetalLookAndFeel SeaglassLookAndFeel

Additionaly如果你的左的切换标签放置到最常见的(默认)或者底的那么所有的标签仍然有同样的宽度,解决了在这个答案的第二段的关注说明。

 进口java.awt.Component中;
进口java.awt.Dimension中;
进口java.awt.GridBagConstraints中;
进口java.awt.GridBagLayout中;
进口java.awt.Insets中;
进口java.beans.PropertyChangeEvent中;
进口的java.beans.PropertyChangeListener;
进口java.beans.PropertyChangeSupport;
进口javax.swing.Icon;
进口javax.swing.JFrame中;
进口javax.swing.JLabel中;
进口javax.swing.JPanel中;
进口javax.swing.JTabbedPane中;
进口javax.swing.SwingConstants中;
进口javax.swing.SwingUtilities中;
进口javax.swing.UIManager中;

公共类演示{

    私人无效createAndShowGUI(){

        JXTabbedPane选项卡窗格=新JXTabbedPane(JTabbedPane.LEFT);
        AbstractTabRenderer渲染器=(AbstractTabRenderer)tabbedPane.getTabRenderer();
        renderer.setPrototypeText(这段文字是一个原型);
        renderer.setHorizo​​ntalTextAlignment(SwingConstants.LEADING);

        tabbedPane.addTab(短,UIManager.getIcon(OptionPane.informationIcon),createEmptyPanel(),信息工具提示);
        tabbedPane.addTab(龙文,UIManager.getIcon(OptionPane.warningIcon),createEmptyPanel(),警告工具提示);
        tabbedPane.addTab(这是一个很长的文本,UIManager.getIcon(OptionPane.errorIcon),createEmptyPanel(),错误的工具提示);

        JFrame的框架=新的JFrame(演示);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(选项卡窗格);
        frame.pack();
        frame.setLocationByPlatform(真正的);
        frame.setVisible(真正的);

    }

    私人的JPanel createEmptyPanel(){
        JPanel的dummyPanel =新的JPanel(){

            @覆盖
            公共维度的get preferredSize(){
                回报是preferredSizeSet​​()?
                            super.get preferredSize():新的尺寸(400,300);
            }

        };
        返回dummyPanel;
    }

    公共静态无效的主要(字串[] args){
        SwingUtilities.invokeLater(新的Runnable(){
            @覆盖
            公共无效的run(){
                新的演示()createAndShowGUI()。
            }
        });
    }

    类JXTabbedPane JTabbedPane的扩展{

        私人ITabRenderer tabRenderer =新DefaultTabRenderer();

        公共JXTabbedPane(){
            超();
        }

        公共JXTabbedPane(INT tabPlacement){
            超(tabPlacement);
        }

        公共JXTabbedPane(INT tabPlacement,诠释tabLayoutPolicy){
            超(tabPlacement,tabLayoutPolicy);
        }

        公共ITabRenderer getTabRenderer(){
            返回tabRenderer;
        }

        公共无效setTabRenderer(ITabRenderer tabRenderer){
            this.tabRenderer = tabRenderer;
        }

        @覆盖
        公共无效addTab(字符串标题,色差分量){
            this.addTab(标题,零,部件,NULL);
        }

        @覆盖
        公共无效addTab(字符串名称,图标图标,色差分量){
            this.addTab(标题,图标,成分,NULL);
        }

        @覆盖
        公共无效addTab(字符串名称,图标图标,色差分量,串尖){
            super.addTab(标题,图标,成分,尖);
            INT的tabIndex = getTabCount() -  1;
            组件选项卡= tabRenderer.getTabRendererComponent(这一点,标题,图标,的tabIndex);
            super.setTabComponentAt(的tabIndex,标签);
        }
    }

    接口ITabRenderer {

        公共组件getTabRendererComponent(JTabbedPane的选项卡窗格,字符串文字,图标图标,INT的tabIndex);

    }

    抽象类AbstractTabRenderer实现ITabRenderer {

        私人字符串prototypeText =;
        私人图标prototypeIcon = UIManager.getIcon(OptionPane.informationIcon);
        私人诠释horizo​​ntalTextAlignment = SwingConstants.CENTER;
        私人最终的PropertyChangeSupport的PropertyChangeSupport =新的PropertyChangeSupport(本);

        公共AbstractTabRenderer(){
            超();
        }

        公共无效setPrototypeText(字符串文本){
            字符串oldText = this.prototypeText;
            this.prototypeText =文本;
            中的firePropertyChange(prototypeText,oldText,文本);
        }

        公共字符串getPrototypeText(){
            返回prototypeText;
        }

        公共图标getPrototypeIcon(){
            返回prototypeIcon;
        }

        公共无效setPrototypeIcon(ICON图标){
            图标oldIcon = this.prototypeIcon;
            this.prototypeIcon =图标;
            中的firePropertyChange(prototypeIcon,oldIcon,图标);
        }

        公众诠释getHorizo​​ntalTextAlignment(){
            返回horizo​​ntalTextAlignment;
        }

        公共无效包含setVerticalTextAlignment(INT horizo​​ntalTextAlignment){
            this.horizo​​ntalTextAlignment = horizo​​ntalTextAlignment;
        }

        公众的PropertyChangeListener [] getPropertyChangeListeners(){
            返回propertyChangeSupport.getPropertyChangeListeners();
        }

        公众的PropertyChangeListener [] getPropertyChangeListeners(字符串propertyName的){
            返回propertyChangeSupport.getPropertyChangeListeners(propertyName的);
        }

        公共无效addPropertyChangeListener(的PropertyChangeListener监听器){
            propertyChangeSupport.addPropertyChangeListener(听众);
        }

        公共无效addPropertyChangeListener(字符串propertyName的,听者的PropertyChangeListener){
            propertyChangeSupport.addPropertyChangeListener(propertyName的,监听器);
        }

        保护无效的firePropertyChange(字符串propertyName的,对象的属性oldValue,对象为newValue){
            的PropertyChangeListener []听众= getPropertyChangeListeners();
            的for(int i = listeners.length  -  1; I> = 0;我 - ){
                听众[I] .propertyChange(新的PropertyChangeEvent(这一点,propertyName的,属性oldValue,newValue)以);
            }
        }
    }

    类DefaultTabRenderer扩展AbstractTabRenderer实现的PropertyChangeListener {

        私有组件prototypeComponent;

        公共DefaultTabRenderer(){
            超();
            prototypeComponent = generateRendererComponent(getPrototypeText(),getPrototypeIcon(),getHorizo​​ntalTextAlignment());
            addPropertyChangeListener(本);
        }

        私有组件generateRendererComponent(字符串文本,图标图标,诠释horizo​​ntalTabTextAlignmen){
            JPanel的rendererComponent =新的JPanel(新的GridBagLayout());
            rendererComponent.setOpaque(假);

            GridBagConstraints的C =新的GridBagConstraints();
            c.insets =新插图(2,4,2,4);
            c.fill = GridBagConstraints.HORIZONTAL;
            rendererComponent.add(新JLabel的(图标),C);

            c.gridx = 1;
            c.weightx = 1;
            rendererComponent.add(新的JLabel(文字,horizo​​ntalTabTextAlignmen),C);

            返回rendererComponent;
        }

        @覆盖
        公共组件getTabRendererComponent(JTabbedPane的选项卡窗格,字符串文字,图标图标,INT的tabIndex){
            组件rendererComponent = generateRendererComponent(文字,图标,getHorizo​​ntalTextAlignment());
            INT prototypeWidth = prototypeComponent.get preferredSize()宽。
            INT prototypeHeight = prototypeComponent.get preferredSize()的高度。
            rendererComponent.set preferredSize(新尺寸(prototypeWidth,prototypeHeight));
            返回rendererComponent;
        }

        @覆盖
        公共无效的propertyChange(PropertyChangeEvent中EVT){
            字符串参数propertyName = evt.getPropertyName();
            如果(prototypeText.equals(propertyName的)||prototypeIcon.equals(propertyName的)){
                this.prototypeComponent = generateRendererComponent(getPrototypeText(),getPrototypeIcon(),getHorizo​​ntalTextAlignment());
            }
        }
    }
}
 

截图

MetalLookAndFeel

NimbusLookAndFeel

SeaglassLookAndFeel

WindowsLookAndFeel

I have a JTabbedPane with tab placement set to LEFT. The problem is that icons in each tab are not vertically aligned with one another.

Consider this picture:

As you can see the icon of "Editar Protocolo" (second tab) is not perfectly aligned with the icon of "Distribuir Protocolo" (first tab) and this also happen with the other tabs. I want all icons be vertically aligned to the left.

This is the code I'm using to set tab components:

...
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;
}

The code is extracted from this Q&A:JTabbedPane: icon on left side of tabs.

解决方案

What I want: the icons ALL in the LEFT, not based on the Text Size [...]

The tab's content is centered by typical implementations, and it makes sense because the area needed to fit this content is unpredictable until the tab is effectively rendered. Since the area depends on the content and different tabs will likely have different title lengths, then there has to be a policy about how to render those tabs. The criteria was to center tabs content and fit the tab area to this content. When we have a default tabbed pane with tabs placed at the top, we don't care much about icon/text alignment:

The only concern could be tabs having different length, but who cares? After all, icons and text are visible and tabbed pane looks good enough. However, when you set the tabs placement to LEFT or RIGHT things are different and it looks unappealing:

Apparently this default behavior is a long standing problem, and there's a really interesting discussion here. Some SO members are involved there: @camickr, @kleopatra, @splungebob. As discussed in that post, a simple solution is not possible, and several workarounds were proposed: basically a custom UI implementation or using panels as renderers and playing with preferred width/height based on text length. Both alternatives involve quite a lot of work.

In order to avoid dealing with UI delegates and taking advantage of setTabComponentAt(...) method, I've started some time ago a tabbed pane extension that I'd like to share here. The approach is based on Swing concept of renderer: a class that has to generate a component to render another component's part, and the goal is to provide a flexible mechanism to add custom tab components.

I have included an example below using my custom tabbed pane and here is an overview of all interfaces/classes needed to provide the aforementioned mechanism.

ITabRenderer interface

The first step is to define an iterface to offer a contract to render a tab component.

AbstractTabRenderer class

An abstract class to provide base methods to help in the getTabRendererComponent(...) method implementation. This abstract class has three main properties:

prototypeText: used to define a prototype text to generate a default renderer component. prototypeIcon: used to define a prototype icon to generate a default renderer. horizontalTextAlignment: tab's text horizontal alignment.

Note this class is abstract because it doesn't implement getTabRendererComponent(...) method.

DefaultTabRenderer class

A concrete implementation by extending AbstractTabRenderer class. Note that if you want to include a close button as shown in tutorial demo, then a little work in this class would be enough. As a matter of fact, I already did, but I won't include that part to not extend this (already large) post.

JXTabbedPane

Finally the tabbed pane's extension which includes tab renderer support and overrides addTab(...) methods.

Example

I have run this example with positive results using these PLAFs:

WindowsLookAndFeel WindowsClassicLookAndFeel NimbusLookAndFeel MetalLookAndFeel SeaglassLookAndFeel

Additionaly if you switch tab placement from LEFT to TOP (default) or BOTTOM then all tabs still having the same width, solving the concern described at the second paragraph of this answer.

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());
            }
        }
    }
}

Screenshots

MetalLookAndFeel

NimbusLookAndFeel

SeaglassLookAndFeel

WindowsLookAndFeel