1

我试图从其单元格渲染器 getListCellRendererComponent() 方法中获取 JList 的宽度。

我得到的奇怪行为是,只要它被放大,我得到正确的宽度,但对于小于原始尺寸的尺寸,我总是得到它第一次绘制时的宽度。

list.getWidth()、list.getSize()、list.getBounds() 和 list.getCellBounds() 也是如此。使用 parent.getxxxxxx() 获取包含 ScrollPanel 的列表的宽度按预期工作。

顺便说一下,这个 JList 上没有设置 min/max/preferredSize。

在这一点上我很茫然..

编辑:
所以看起来包含的 ScrollPanel 在调整大小时没有被重新验证。不应该自动调用 revalidate() 吗?请注意,这不是以编程方式完成的任何事情,而只是用户执行的鼠标框架调整大小。

编辑 2:
是的,JList 的几何结构有一些复杂的本质,归根结底与实际的屏幕真实状态完全无关。任何对后者感兴趣的人都可以访问包含 ScrollPane 的 ViewPort 并查询它(getWidth、getHaight、getSize、getBounds)。

编辑 3
这是根据要求提供的 SSCCE。虽然不是很短,因为我想保留 Netbeans 的布局代码,以防万一它与问题有关。有了这个,我得到一个 667 的 JList 宽度,这似乎与最长项目的长度有些相关(在 667 处,它开始在末尾显示省略号)。一旦视口宽度达到 667,jlists 宽度就开始跟踪视口的宽度。收缩时,此跟踪停止在 667 并保持在该位置。

package com.dafquest.operator.mvc;

import java.awt.Component;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JLabel;
import javax.swing.JList;

public class JListSSCCE extends javax.swing.JFrame {

    public JListSSCCE() {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setName("Form");

        jPanel1.setName("jPanel1");

        jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        jScrollPane1.setName("jScrollPane1");

        jList1.setModel(new javax.swing.AbstractListModel() {
            String[] strings = { "This is this Jlist's Item 1", "A shorter Item 2", "For some reason Item 3 seems to be longer", "Certainly issue whith item 3 must be a mirage, Item 4 *is* a really long item", "Item 5" };
            public int getSize() { return strings.length; }
            public Object getElementAt(int i) { return strings[i]; }
        });
        jList1.setCellRenderer(new MyCellRenderer());
        jList1.setName("jList1");
        jScrollPane1.setViewportView(jList1);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 330, Short.MAX_VALUE)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 110, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }                        

    public static void main(String args[]) {
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(JListSSCCE.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new JListSSCCE().setVisible(true);
            }
        });
    }

    private javax.swing.JList jList1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;

    private class MyCellRenderer extends DefaultListCellRenderer {

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

            JLabel comp = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            String widths = "(SPViewPort: " + list.getParent().getWidth()
                    + ", Jlist: " + list.getWidth()
                    + ", Cell: " + comp.getWidth()
                    + ", CellPref: " + comp.getPreferredSize().width + ") ";
            comp.setText(widths + comp.getText());

            // Lets see after adding text, might be that JList width is equal
            // tho longest item's preferred width?
            widths = "(SPViewPort: " + list.getParent().getWidth()
                    + ", Jlist: " + list.getWidth()
                    + ", Cell: " + comp.getWidth()
                    + ", CellPref: " + comp.getPreferredSize().width + ") ";
            System.out.println(widths);

            return comp;
        }
    }
}
4

1 回答 1

1

问题是您正在有效地更改列表的大小要求而没有通知它:-)

以下是您修改的渲染器以演示行为:

  • 最初,即在显示列表之前,它的 ui-delegate 测量每个项目的大小要求并缓存每个单元格的大小要求
  • 那时,视口和列表的大小都是0:添加的文本长度是somefixedChars + 2
  • 该列表是可滚动的,并将其 prefScrollableSize 报告为其首选项
  • scrollPane 总是以其 prefScrollableSize 调整列表的大小,如果它更大,则滚动
  • 显示后,列表/视口的大小确实 > 0 将最大长度项目的实际 prefSize 更改为somefixedChars + 2* digitsOfSize
  • ui 不知道该更改,因此它的 pref、prefScrollable 和它的实际都没有更新

解决方案取决于您的实际用例:您需要找到一种方法来强制 ui 使其大小缓存无效。SwingX 的 JXList有一个方法,比如:

xList.invalidateCellSizeCache();

对于核心,您可以通过重新/设置固定大小来破解:

list.setFixedCellWidth(somearbitraryValue);
list.setFixedCellWidth(-1);

要玩的渲染器:

private class MyCellRenderer extends DefaultListCellRenderer {

    private int zeroPref;

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

        JLabel comp = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        String widths = "(SPViewPort: " + list.getParent().getWidth()
                + ", Jlist: " + list.getWidth()
                + ", CellPref: " + comp.getPreferredSize().width + ") ";
        comp.setText(widths + comp.getText());
        if (list.getWidth() == 0) {
            // keep track of the pref for a zero sized list/viewport 
            zeroPref = Math.max(comp.getPreferredSize().width, zeroPref);
        }
        //list is sized to its initial pref
        widths = //"index: " + index 
                "initial: " + zeroPref
                + "(SPViewPort: " + list.getParent().getWidth()
                + ", Jlist: " + list.getWidth()
                + ", CellPref: " + comp.getPreferredSize().width + ") ";
        System.out.println(widths);

        return comp;
    }
}
于 2013-10-10T14:03:02.100 回答