【问题标题】:JTable render stops rendering sometimes but resumes on window resizeJTable 渲染有时会停止渲染,但会在调整窗口大小时恢复
【发布时间】:2013-12-02 15:44:26
【问题描述】:

我的自定义 JTable 和自定义 TableRenderer 出现了一个神秘问题。 在 95%-99,99% 的情况下,它可以完美运行,但有时渲染器会停止工作,并将表格的一部分(位于 JScrollPane 内)留空。

问题案例如下所示:

在所有其他情况下,稍微调整窗口大小后,表格如下所示:

现在两列都有一个与之关联的 TextAreaCellRenderer,其工作原理如下:

public class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {

    private final Color evenColor = new Color(252, 248, 202);


    public TextAreaCellRenderer() {
        super();
        setLineWrap(true);
        setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    }


    @Override
    public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
        if (isSelected) {
            setForeground(table.getSelectionForeground());
            setBackground(table.getSelectionBackground());
        } else {
            setForeground(table.getForeground());
            setBackground(table.getBackground());
            setBackground((row % 2 == 0) ? evenColor : getBackground());
        }
        setWrapStyleWord(true);
        setFont(table.getFont());
        setText((value == null) ? "" : value.toString());
        return this;
    }
}

我还必须重写 JTable 的 doLayout 方法,以便能够根据内容计算单元格的高度。自定义表格如下所示:

public class MediaMetaDataTable extends JTable {

    @Override
    public void doLayout() {
        TableColumn col = getColumnModel().getColumn(1);
        for (int row = 0; row < getRowCount(); row++) {
            Component c = prepareRenderer(col.getCellRenderer(), row, 1);
            if (c instanceof JTextArea) {
                JTextArea a = (JTextArea) c;
                int h = getPreferredHeight(a) + getIntercellSpacing().height;
                if (getRowHeight(row) != h) {
                    setRowHeight(row, h);
                }
            }
        }
        super.doLayout();
    }


    private int getPreferredHeight(final JTextComponent c) {
        Insets insets = c.getInsets();
        View view = c.getUI().getRootView(c).getView(0);
        int preferredHeight = (int) view.getPreferredSpan(View.Y_AXIS);
        return preferredHeight + insets.top + insets.bottom;
    }
}

该表使用以下参数实例化一次:

metaTable = new MediaMetaDataTable();
metaTable.setModel(new MediaMetaDataTableModel());
metaTable.setEnabled(false);
metaTable.setShowGrid(false);
metaTable.setTableHeader(null);
metaTable.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());
metaTable.getColumnModel().getColumn(1).setCellRenderer(new TextAreaCellRenderer());
metaTable.setPreferredScrollableViewportSize(new Dimension(-1, -1));
metaTable.setShowHorizontalLines(false);
metaTable.setShowVerticalLines(false);

每次要显示的数据发生变化时,我都会通过替换底层模型数据来更新表:

List<MediaMetaData> metaInformation = mediaSearchHit.getMetaInformation();
        if (metaInformation != null) {
            ((MediaMetaDataTableModel) metaTable.getModel()).replaceMetaInfos(metaInformation);
        }

更新模型本身会触发表数据更改事件:

public class MediaMetaDataTableModel extends AbstractTableModel {

    private List<MediaMetaData> metaInfos = new LinkedList<MediaMetaData>();

    public static final int COL_INDEX_NAME = 0;
    public static final int COL_INDEX_VALUE = 1;


    public void replaceMetaInfos(final List<MediaMetaData> metaInfos) {
        this.metaInfos = null;
        this.metaInfos = metaInfos;
        fireTableDataChanged();
    }
...

现在有人知道,是什么导致了所描述的渲染问题?

感谢您的任何建议。

【问题讨论】:

  • 如需尽快获得更好的帮助,请发帖SSCCE
  • 有时建议同步不正确。
  • 现在有人知道,是什么导致了所描述的渲染问题? == SSCCE,简短,可运行,可与 JTable 的硬编码值编译,顺便说一句,昨天被问及回答了类似的问题
  • 并创建一个局部变量而不是extends JTextArea,doLayout很熟悉,重要错误的原因是setPreferredScrollableViewportSize(new Dimension(-1, -1));,开始搜索如何编写AbstractTableModel if rest 与您在此处显示的代码片段相同

标签: java swing jtable rendering


【解决方案1】:

我还必须覆盖 JTabledoLayout 方法才能能够 根据内容计算单元格的高度。

要实现这个目标,无需重写doLayout() 方法。我认为最简单的方法是将用于呈现单元格内容的文本区域添加到带有BorderLayoutJPanel 中,并根据面板的首选大小设置行高。这样布局管理器将为您完成任务,并且所有单元格的内容都将可见:

@Override
public Component getTableCellRendererComponent(...) {
    ...
    JPanel contentPane = new JPanel(new BorderLayout());            
    contentPane.add(this);
    table.setRowHeight(row, contentPane.getPreferredSize().height); // sets row's height
    return contentPane;
}

正如@mKorbel 指出的那样,没有必要让渲染器从JTextArea 扩展:单个变量就可以工作。牢记这一点,请根据您的工作查看此实现:

class TextAreaRenderer implements TableCellRenderer {

    private JTextArea renderer;
    private final Color evenColor = new Color(252, 248, 202);

    public TextAreaRenderer() {
        renderer = new JTextArea();            
        renderer.setLineWrap(true);
        renderer.setWrapStyleWord(true);
        renderer.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        if (isSelected) {
            renderer.setForeground(table.getSelectionForeground());
            renderer.setBackground(table.getSelectionBackground());
        } else {
            renderer.setForeground(table.getForeground());
            renderer.setBackground((row % 2 == 0) ? evenColor : table.getBackground());
        }            
        renderer.setFont(table.getFont());
        renderer.setText((value == null) ? "" : value.toString());
        JPanel contentPane = new JPanel(new BorderLayout());            
        contentPane.add(renderer);
        table.setRowHeight(row, contentPane.getPreferredSize().height); // sets row's height
        return contentPane;
    }

}

截图

【讨论】:

  • 感谢您抽出宝贵时间购买 Delcio!你的解决方案很好。唯一的问题是,在快速迭代结果集时有时会产生效果,但它不会持续很长时间,需要进行更多的刷新才能解决问题。原因可能与幕后的某种事件处理有关,它会更新结果。这也是为什么在这种情况下提供 SSCCE 会非常困难的原因。无论如何,您的解决方案是一个很大的帮助!谢了!
  • @Macilias 不客气!我很高兴它对您有所帮助:) 正如您所说,这种行为的原因可能与事件处理有关。只需确保在 Event Dispatching Thread 中将行添加到模型中,例如使用 SwingWorker,例如 here。该示例是关于向 JComboBox 添加项目,但同样适用于 JTable(或任何摆动组件)。
【解决方案2】:

如果我不得不猜测,我会说这可能是一个并发问题。您是否在 GUI 线程中做所有事情?如果是,则不会是并发问题。否则尝试在初始调试步骤中使用 Thread.InvokeLater() 调用所有内容,如果经过长时间测试后不再遇到错误,则知道问题的原因。

然后,您将在第二步中准确检查需要使用invokelater() 进行调用的位置以及不需要调用的位置(因为您不应该一直这样做,因为这会导致性能非常差。

正如我所说,只是一个疯狂的猜测......你可能只是另一个错误。你用的是Java7吗? Swing 中有数百万个带有 java 7 代码的错误(只是所有来自 oracle 的代码)。

【讨论】:

    猜你喜欢
    • 2012-12-20
    • 2010-12-05
    • 2016-11-27
    • 1970-01-01
    • 2017-11-21
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    相关资源
    最近更新 更多