java - JTextArea(换行)作为 TableCellRender 到多个列
问题描述
标题说明了一切。我有一个表格,在每个单元格中都有可能有长文本。我TableCellRenderer
应该使用什么来显示数据 - 每个单元格的文本以及正确的换行?我已经尝试了很多与渲染器大小混淆的变体,setRowHeight
但似乎没有什么是最佳的。我尝试的每一件事都有某种不稳定。
我尝试的第一件事是使用 aJTextArea
作为 myTableCellRenderer
以利用setLineWrap
. 这个答案准确地描述了我所做的。它完全按照我的意愿工作,但存在问题。此渲染器只能拥有一列。如果将渲染器添加到第二列,则具有较大“id”(表中的列索引)的列将“占主导地位”(并为表的行提供高度),而忽略列中文本较小的情况“ id" 需要显示更多行,即更大的高度。
检查这个 gif。这正是我想要实现的行为,具有“最高”文本的列在行高中占主导地位。它之所以有效,是因为该列具有最大的索引。(最后渲染)
你看?列的宽度减小了,所以文本需要更多的行才能完整地表示,并且第 1 列中的文本是可以的,因为它的所有文本都是可见的。
现在让我们看看第 1 列中的文本比最后一列需要更多行的情况。
很明显,我们丢失了第一列中的文本。最后一列(最后呈现)具有适当的高度,因此第 1 列不会“占主导地位”并用空间填充最后一列。
产生此行为的 SSCCE:
public class TableTest extends JTable {
private static final long serialVersionUID = 7180027425789244942L;
private static final String[] COLUMNS = { "SomeColumn", "OtherColumn", "OtherOtherColumn" };
public TableTest() {
super();
Object[][] data = new Object[5][3];
for (int i = 0; i < data.length; i++) {
data[i][0] = "Row: " + i + " - " + loremIpsum();
data[i][1] = "Row: " + i + " Maybe something small?";
data[i][2] = "Row: " + i + "___" + new StringBuilder(loremIpsum()).reverse().toString();
}
setModel(new DefaultTableModel(data, COLUMNS) {
@Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
});
setDefaultRenderer(String.class, new WordWrapCellRenderer());
getTableHeader().setReorderingAllowed(false);
}
public static class WordWrapCellRenderer extends JTextArea implements TableCellRenderer {
private WordWrapCellRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
}
@Override
public WordWrapCellRenderer getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setText(value.toString());
setSize(table.getColumnModel().getColumn(column).getWidth(), getPreferredSize().height);
if (table.getRowHeight(row) != getPreferredSize().height) {
table.setRowHeight(row, getPreferredSize().height);
}
return this;
}
}
private String loremIpsum() {
return "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
+ " Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"
+ " when an unknown printer took a galley of type and scrambled it to make a type specimen book."
+ " It has survived not only five centuries, but also the leap into electronic typesetting, "
+ "remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset"
+ " sheets containing Lorem Ipsum passages, and more recently with desktop publishing software"
+ " like Aldus PageMaker including versions of Lorem Ipsum";
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Test");
frame.setLayout(new BorderLayout());
TableTest table = new TableTest();
JScrollPane sp = new JScrollPane(table);
frame.add(sp);
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
解决此问题的第一个尝试是更改table.getRowHeight(row) != getPreferredSize().height
为table.getRowHeight(row) <= getPreferredSize().height
,以便仅在必须呈现“更高”组件时才更改行的高度。这也不起作用,因为在“高”渲染行高之后将不会恢复(包装)。相关gif图片:
看到这个之后,我尝试创建某种侦听器(ComponentListener#componentResized
-MouseListener#mouseReleased
到标题?),恢复每一行的高度,如下所示:
private void restoreRowHeight() {
if (getModel() == null) //causing NPE
return;
for (int row = 0; row < getRowCount(); row++) {
int heightOfTheTallestComponent = -1;
for (int column = 0; column < getColumnCount(); column++) {
Component c = prepareRenderer(getDefaultRenderer(String.class), row, column);
if (c.getPreferredSize().height > heightOfTheTallestComponent)
heightOfTheTallestComponent = c.getPreferredSize().height;
}
setRowHeight(row, heightOfTheTallestComponent);
}
}
我能想到的听众似乎都不合适。然而,即使我找到了合适的监听器来调用这个方法,也会发生一个小但非常烦人的小故障。(欢迎任何阻止它的替代方案)。
最后我抱了一些希望(我在 10 分钟后后悔了),也许 JTable 可以用文本正确呈现JLabel
s (当它有 HTML 文本时换行)并使用以下(扩展)<html>
JLabel
DefaultTableCellRenderer
int width = table.getColumnModel().getColumn(column).getWidth();
setText("<html><p style='width: " + width + "px'>" + String.valueOf(value));
但当然,没有机会。
我尝试的另一种方法是再次使用此答案中描述的 JLabel,但同样,如果需要恢复,则没有高度恢复。
是否有任何解决方案可以正确包装和显示所有列的文本并且不会导致故障?
解决方案
也许我在问题中提到的关于使用侦听器恢复额外行空间的解决方案一点也不差。即使它不是这个问题的 100% 最佳解决方案(这就是我不接受我的答案的原因),它是稳定的并且不会导致任何性能问题。另外,它很容易理解(这是一个优点,对吧?)。
问题是恢复额外的行高和包装单元格必须在正确的时间进行,也就是使用正确的侦听器。额外的空间可能由 2 个事件引起。
- 当用户手动更改列的宽度时。
- 当由于某种原因调整表格大小时。
为了涵盖第一个,我发现最好的侦听器是使用MouseListener
表的标题,更具体地说是捕获mouseReleased
事件,因为当鼠标单击标题时,列的大小调整结束。
关于第二个 aComponentListener#componentResized
足以覆盖它。请注意,即使表的数据发生更改,也会调用此侦听器,这就是dataChanged
不需要“”类型的侦听器的原因(可能覆盖模型的fireTableDataChanged
方法?)
这不是最好的,但它是一些东西。
预习:
代码:
public class TableTest extends JTable {
private static final String[] COLUMNS = { "SomeColumn", "OtherColumn", "OtherOtherColumn" };
public TableTest() {
super();
Object[][] data = new Object[5][3];
for (int i = 0; i < data.length; i++) {
data[i][0] = "Row: " + i + " - " + loremIpsum();
data[i][1] = "Row: " + i + " Maybe something small?";
data[i][2] = "Row: " + i + "___" + new StringBuilder(loremIpsum()).reverse().toString();
}
setModel(new DefaultTableModel(data, COLUMNS) {
@Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
});
setDefaultRenderer(String.class, new WordWrapCellRenderer());
getTableHeader().setReorderingAllowed(false);
getTableHeader().setReorderingAllowed(true);
getTableHeader().addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
restoreRowHeight();
}
});
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
restoreRowHeight();
}
});
}
private void restoreRowHeight() {
if (getModel() == null) // causing NPE
return;
for (int row = 0; row < getRowCount(); row++) {
int heightOfTheTallestComponent = -1;
for (int column = 0; column < getColumnCount(); column++) {
Component c = prepareRenderer(getDefaultRenderer(String.class), row, column);
if (c.getPreferredSize().height > heightOfTheTallestComponent)
heightOfTheTallestComponent = c.getPreferredSize().height;
}
setRowHeight(row, heightOfTheTallestComponent);
}
}
public static class WordWrapCellRenderer extends JTextArea implements TableCellRenderer {
private WordWrapCellRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
}
@Override
public WordWrapCellRenderer getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setText(value.toString());
setSize(table.getColumnModel().getColumn(column).getWidth(), getPreferredSize().height);
if (table.getRowHeight(row) < getPreferredSize().height) {
table.setRowHeight(row, getPreferredSize().height);
}
return this;
}
}
private String loremIpsum() {
return "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
+ " Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"
+ " when an unknown printer took a galley of type and scrambled it to make a type specimen book."
+ " It has survived not only five centuries, but also the leap into electronic typesetting, "
+ "remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset"
+ " sheets containing Lorem Ipsum passages, and more recently with desktop publishing software"
+ " like Aldus PageMaker including versions of Lorem Ipsum";
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Test");
frame.setLayout(new BorderLayout());
TableTest table = new TableTest();
JScrollPane sp = new JScrollPane(table);
frame.add(sp);
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
推荐阅读
- css - 如何应用条件CSS?
- javascript - 如何通过单击时钟示例更新输出值
- ubuntu - 创建 3 节点 Hadoop 集群
- apache - Apache Active MQ 使用远程机器上的 Java 程序连接异常
- python-3.x - 主线程不会等到所有 ThreadPoolExecutor 线程完成
- firebase - 角火2离子3
- django - 无法从 django-app 中的 AWS-S3 存储桶获取图像?
- html - 从 Web 下载 CHM 文件时出现问题
- r - ggplot,在套索中找不到对象“值”
- php - 路由无法从表单中获取 id(缺少 [Route: update_user] 所需的参数)