Swing JList具有多行文本和动态高度 [英] Swing JList with multiline text and dynamic height

查看:212
本文介绍了Swing JList具有多行文本和动态高度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I already read/tried these posts but that didn't help:

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

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

What I need is a ListCellRenderer which returns a panel with an icon on the left and a text of dynamic length on the right (like in any forum: on the left a user avatar, on the right the post text). The texts are NOT known to me, so I can't set a fixed cell height. Further, the text length differs from list cell to list cell. So every list cell needs its own height depending on the length of the text. Actually a really common layout ... but not for Swing. The cell height just doesn't expand according to the text length.

我已经阅读了几乎所有关于 JList ,但找不到解决方案。所以我决定给一个小型的SSCCE。请告诉我如何实现我所描述的内容,或者如果您认为这很容易,请修改我的代码。

I already read almost any post out there about dynamic cell heights and multiline texts in JList, but couldn't find a solution. So I decided to give a small SSCCE. Please give me a hint on how to achieve what I described or please fix my code if you think it's easy.

谢谢

这是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;

        }
    }

}


推荐答案

编辑1 :oops - 看到@Andrew的屏幕截图,意识到这没有按预期工作,文本实际上比显示的更长(忽略了)内部注释PENDING:不适用于JScrollPane中的JList;-)如果我不能尽快使用它,请稍微删除并删除此答案。

Edit 1: oops - seeing @Andrew's screenshot, realized that this isn't working as expected, the text is actually longer than shown with this (overlooked an internal comment "PENDING: not working for JList in JScrollPane" ;-) Will dig a bit and delete this answer if I can't make it work soon.

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

Edit 2: got it - the renderer implementation as shown below is okay, the culprit is the JList with its occasional less than optimal size caching. There are two parts of that


  • BasicListUI没有考虑到调整列表大小可能需要清除内部大小(实际行高)缓存,应用程序代码必须强制它这样做,fi在ComponentListener

  • 列表中trackViewportWidth的Scrollable实现包含阻碍该区域的逻辑(导致该区域的循环拉伸直到它是单行),子类返回true。

使用以下渲染器的代码:

Code that uses the renderer below:

    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作为渲染组件的渲染器实现)

First answer (a renderer implementation which uses JTextArea as rendering component)

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

TextArea is a bit tricky in sizing: it needs to get initialized to something reasonable:

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;

    }
}

这篇关于Swing JList具有多行文本和动态高度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆