设置行高会触发重绘,进而触发另一个对渲染器的调用。因此,重要的是只有在行高度与当前行高度不同时才设置行高度,以避免出现尾部循环。当您无条件调用setRowHeight()
时,即使具有固定值,也会发生这种情况。
我需要在JTable中显示多行内容。实际内容是在自定义模型中维护的对象的集合,该对象扩展了DefaultTableModel
并通过覆盖getValueAt()
即时生成单元格内容。
为了拥有多行内容,我实现了自定义TableCellRenderer
:
private class MultiLineCellRenderer extends JTextArea implements TableCellRenderer {
public MultiLineCellRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
setOpaque(true);
setBorder(new EmptyBorder(-1, 2, -1, 2));
setRows(1);
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
String text = value == null ? "" : value.toString();
if (!getText().equals(text)) {
setText(text);
int newHeight = table.getRowHeight() * getLineCount();
if (table.getRowHeight(row) != newHeight)
table.setRowHeight(row, newHeight);
}
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
return this;
}
}
现在,如果我在表中填充几百行(列数为2),我会看到AWT工作线程开始将一个CPU内核最大化。同时,内存消耗从〜100 MB上升到该数量的十倍甚至更多。即使应用程序实际上没有做任何事情(没有数据在后台加载,也没有用户交互),这种情况也会发生,并且仅当我清除表从中获取其内容的集合时才停止。
通过注释掉选定的代码部分,我将这些行确定为罪魁祸首:
int newHeight = table.getRowHeight() * getLineCount(); if (table.getRowHeight(row) != newHeight) table.setRowHeight(row, newHeight);
如果我注释掉本节,则所有表行都具有相同的高度(1行文本),但是内存消耗保持在〜100 MB左右。
如果我通过一次调用table.setRowHeight(row, 32)
(即使用固定值来替换这些行,则内存消耗将无限期地再次开始上升。
以下修改有效,但以所有具有相同高度的行为代价:
int newHeight = getRowHeight() * getLineCount(); if (table.getRowHeight() < newHeight) table.setRowHeight(newHeight);
底线:似乎在JTable中设置单个行高会造成大量内存泄漏。我是在做错什么,还是遇到了实际的错误?在后一种情况下,是否有任何已知的修复程序/解决方法?
我需要在JTable中显示多行内容。实际内容是在自定义模型中维护的对象的集合,该对象扩展了DefaultTableModel并通过...] >>
设置行高会触发重绘,进而触发另一个对渲染器的调用。因此,重要的是只有在行高度与当前行高度不同时才设置行高度,以避免出现尾部循环。当您无条件调用setRowHeight()
时,即使具有固定值,也会发生这种情况。
第二个问题是,每行包含两个单元格,它们可能具有不同的高度。上面的代码将设置行高以匹配当前正在渲染的单元格。当该行的另一个单元格被渲染并具有不同的高度时,该行的高度将再次更改。这将触发重绘,也将刷新该行的第一列。由于这将导致另一个高度变化,因此再次出现infine循环。
证明:以下代码解决了这个问题:
int newHeight = table.getRowHeight() * getLineCount(); if (table.getRowHeight(row) < newHeight) table.setRowHeight(row, newHeight);
现在,行高只会增加,而永远不会减少。副作用:如果单元格内容发生变化,并且现在占用的行数少于以前,则行高不会发生变化以反映这一点。
底线:使用多行单元格呈现JTable是不平凡的,因此SO有很多错误示例。我发现的唯一有效示例是在https://www.javaspecialists.eu/archive/Issue106.html(由于另一个SO帖子而找到)。
他们的解决方案是在渲染器内部存储像元高度(尽管这也可以在表格模型中完成,无论哪种方式最适合您的实现)。计算单元格的高度时,将其存储,然后获取该行中任何单元格的最大高度并使用该最大高度。另外,请确保仅在与当前行高度不同时设置行高。
这终于解决了内存泄漏/处理器消耗的问题,最后为我提供了一个如何正确计算单元格高度的实用示例。
设置行高会触发重绘,进而触发另一个对渲染器的调用。因此,重要的是只有在行高度与当前行高度不同时才设置行高度,以避免出现尾部循环。当您无条件调用setRowHeight()
时,即使具有固定值,也会发生这种情况。