带有多行文本和动态高度的 Swing JList

Posted

技术标签:

【中文标题】带有多行文本和动态高度的 Swing JList【英文标题】:Swing JList with multiline text and dynamic height 【发布时间】:2011-11-10 12:01:52 【问题描述】:

我已经阅读/尝试过这些帖子,但没有帮助:

Display multiple lines within a Jlist cell How to get multiline for a Jlist text? Problem displaying components of JList

我需要的是一个ListCellRenderer,它返回一个面板,左边是一个图标,右边是一个动态长度的文本(就像在任何论坛中一样:左边是用户头像,右边是帖子文本) .我知道这些文本,因此我无法设置固定的单元格高度。此外,文本长度因列表单元格而异。因此,每个列表单元格都需要自己的高度,具体取决于文本的长度。实际上是一个非常常见的布局......但不适用于 Swing。单元格高度不会根据文本长度扩展。

我已经在JList 中阅读了几乎所有关于动态单元格高度和多行文本的帖子,但找不到解决方案。所以我决定给一个小的SSCCE。请给我一个关于如何实现我所描述的内容的提示,或者如果您认为这很容易,请修复我的代码。

谢谢

这里是 SSCCE:

public class MultiLineList extends JFrame


    private static final long serialVersionUID = 1L;

    public static void main(final String[] args)
    
        new MultiLineList();
    

    private MultiLineList()
    
        setTitle("MultiLineList");
        setSize(800, 450);
        setResizable(true);
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        this.getContentPane().setLayout(new BorderLayout());

        final DefaultListModel model = new DefaultListModel();
        model.addElement("This is a short text");
        model.addElement("This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. ");
        model.addElement("This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. ");

        final JList list = new JList(model);
        list.setCellRenderer(new MyCellRenderer());

        this.add(list);

        this.getContentPane().invalidate();
        this.getContentPane().validate();

    

    public class MyCellRenderer extends DefaultListCellRenderer
    
        @Override
        public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean hasFocus)
        

            final String text = (String) value;

            //create panel
            final JPanel p = new JPanel();
            p.setLayout(new BorderLayout());

            //icon
            final JPanel IconPanel = new JPanel(new BorderLayout());
            final JLabel l = new JLabel("icon"); //<-- this will be an icon instead of a text
            IconPanel.add(l, BorderLayout.NORTH);
            p.add(IconPanel, BorderLayout.WEST);

            //text
            final JTextArea ta = new JTextArea();
            ta.setText(text);
            ta.setLineWrap(true);
            ta.setWrapStyleWord(true);
            p.add(ta, BorderLayout.CENTER);

            return p;

        
    


【问题讨论】:

与手头的问题无关:a) 永远不会在 getXXRendererComponent 中创建 组件 b) 如果新组件没有,则不要扩展 类满足 is-a 要求 请注意,SSCCE 应包括导入。并非所有人都使用可以解决它们的自动 IDE! 【参考方案1】:

编辑 1:哎呀 - 看到 @Andrew 的屏幕截图,意识到这没有按预期工作,文本实际上比显示的要长(忽略了内部评论“PENDING: not working for JScrollPane 中的 JList" ;-) 如果我不能让它尽快工作,我会挖掘一下并删除这个答案。

编辑 2:明白了 - 如下所示的渲染器实现还可以,罪魁祸首是 JList,它的缓存偶尔小于最佳大小。有两个部分

BasicListUI 没有考虑到调整列表大小可能需要清除内部大小(实际上是行高)缓存,应用程序代码必须强制它这样做,f.i.在 ComponentListener 中 tracksViewportWidth 的列表的 Scrollable 实现包含阻碍逻辑(导致循环拉伸该区域直到它成为单行),子类返回 true。

下面使用渲染器的代码:

    final JList list = new JList(model) 

        /** 
         * @inherited <p>
         */
        @Override
        public boolean getScrollableTracksViewportWidth() 
            return true;
        


    ;
    list.setCellRenderer(new MyCellRenderer());

    ComponentListener l = new ComponentAdapter() 

        @Override
        public void componentResized(ComponentEvent e) 
            // next line possible if list is of type JXList
            // list.invalidateCellSizeCache();
            // for core: force cache invalidation by temporarily setting fixed height
            list.setFixedCellHeight(10);
            list.setFixedCellHeight(-1);
        

    ;

    list.addComponentListener(l);
    add(new JScrollPane(list));

第一个答案(使用JTextArea作为渲染组件的渲染器实现)

TextArea 在调整大小方面有点棘手:它需要初始化为合理的值:

public class MyCellRenderer implements ListCellRenderer 

    private JPanel p;
    private JPanel iconPanel;
    private JLabel l;
    private JTextArea ta;

    public MyCellRenderer() 
        p = new JPanel();
        p.setLayout(new BorderLayout());

        // icon
        iconPanel = new JPanel(new BorderLayout());
        l = new JLabel("icon"); // <-- this will be an icon instead of a
                                // text
        iconPanel.add(l, BorderLayout.NORTH);
        p.add(iconPanel, BorderLayout.WEST);

        // text
        ta = new JTextArea();
        ta.setLineWrap(true);
        ta.setWrapStyleWord(true);
        p.add(ta, BorderLayout.CENTER);
    

    @Override
    public Component getListCellRendererComponent(final JList list,
            final Object value, final int index, final boolean isSelected,
            final boolean hasFocus) 

        ta.setText((String) value);
        int width = list.getWidth();
        // this is just to lure the ta's internal sizing mechanism into action
        if (width > 0)
            ta.setSize(width, Short.MAX_VALUE);
        return p;

    

【讨论】:

这实际上很完美。我刚刚将 int width=list.getWidth() 替换为 int width = list.getWidth() - l.getIcon().getIconWidth();否则,将计算 textarea 的高度,就好像列表的总宽度可供它使用一样。太感谢了。我今天也学到了很多东西。 @haferblues - 很好的调整到其余宽度,谢谢:-) @kleopatra - 很好的修复!我把头发扯掉了! @kleopatra - 我又扯头发了。有时,这会切断最后一行文本。我不明白为什么。然后在其他时候,它不会。【参考方案2】:

import java.awt.*;
import javax.swing.*;

public class MultiLineList

    private static final long serialVersionUID = 1L;

    public static void main(final String[] args)
    
        SwingUtilities.invokeLater(new Runnable() 
            public void run() 
                new MultiLineList();
            
        );
    

    private MultiLineList()
    
        JFrame f = new JFrame("MultiLineList");
        f.setResizable(true);
        f.setVisible(true);
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());

        final DefaultListModel model = new DefaultListModel();
        model.addElement("This is a short text");
        model.addElement("This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. ");
        model.addElement("This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. ");

        final JList list = new JList(model);
        list.setCellRenderer(new MyCellRenderer());

        f.add(list);

        f.pack();
    

    public class MyCellRenderer extends DefaultListCellRenderer
    
        final JPanel p = new JPanel(new BorderLayout());
        final JPanel IconPanel = new JPanel(new BorderLayout());
        final JLabel l = new JLabel("icon"); //<-- this will be an icon instead of a text
        final JLabel lt = new JLabel();
        String pre = "<html><body style='width: 200px;'>";

        MyCellRenderer() 
            //icon
            IconPanel.add(l, BorderLayout.NORTH);
            p.add(IconPanel, BorderLayout.WEST);

            p.add(lt, BorderLayout.CENTER);
            //text
        

        @Override
        public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean hasFocus)
        
            final String text = (String) value;
            lt.setText(pre + text);

            return p;
        
    

【讨论】:

该死 .. 又被打败了,一个 with 截图 :-) 您太忙于给出关于不在渲染器中创建组件的好建议。 ;) ***.com/questions/6901153/… ,但都是 +1 调整窗口大小不会使文本展开。此解决方案需要更多工作。

以上是关于带有多行文本和动态高度的 Swing JList的主要内容,如果未能解决你的问题,请参考以下文章

java swing 中的列表框JList如何在程序中动态的添加和删除元素

如何在Java Swing中创建响应式JList

Swing JList SetCellRenderer 背景颜色不起作用

根据里面的多行文本视图动态设置 ListView 高度

Java-Swing:使用 HTML 设置选定的文本颜色

uitableviewcell 具有动态高度或减小字体大小以将多行放入一个单元格