在复制JTable的单元格时,如何修改HTML?
我有一个非常简单 JTable,类似于下面这个最小、可复现的示例
import module java.base;
import module java.desktop;
void main()
{
SwingUtilities
.invokeLater
(
() ->
{
JOptionPane
.showMessageDialog
(
null,
new JTable
(
new Object[][]
{
new Object[]{1, 2, 3},
new Object[]{4, 5, 6},
new Object[]{7777, 88888, 99999}
},
new Object[]{"abc", "xyz", "tuv"}
)
)
;
}
)
;
}
当用户选中表格的全部内容并复制后,粘贴的位置不同,渲染的结果也会不同。
- 如果粘贴到Notepad++,它呈现为以制表符分隔的字符串列表。
- 如果在浏览器中的Gmail粘贴,它会渲染为一个HTML表格,包含字面量的
<table>标签等。
那个HTML的效果很酷,但这是意料之中的——Swing的底层确实用了一点HTML,所以我也不感到惊讶。
但我想能够修改复制后的数据,比如给它加上边框。 在Swing中实现这一点的最简单的方法是什么?
显然,我可以在数据落入用户剪贴板后自行修改或生成HTML,但我会把那视为最后的手段。
最理想的情况是我可以使用类似 myTable.setBorder(BorderFactory.createLineBorder()) 的东西,让它为我把设置搞定。
显然,单元格是由诸如 ListCellRenderer 之类的东西处理的,所以我大概也能做到。
但我的目标是在单元格的HTML上找到最简单的修改方式。比如,为每个单元格创建边框。
解决方案
你看到的是JTable的默认TransferHandler的行为。如果你想改变这一行为,需要将你自定义的TransferHandler传递给JTable继承自的 setTransferHandler 方法。
改变JTable的外观(例如设置边框)将不起作用。剪贴板中的内容基于表格的数据,而不是它的外观。
幸运的是,编写一个自定义的TransferHandler相对来说比较直接:
import java.io.Serial;
import java.util.Formatter;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.IntStream;
import java.awt.EventQueue;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import javax.swing.TransferHandler;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.table.TableModel;
public class CustomHTMLTransferHandler
extends TransferHandler {
@Serial
private static final long serialVersionUID = 1;
@Override
public boolean canImport(TransferSupport support) {
return false;
}
@Override
public void exportToClipboard(JComponent c,
Clipboard clipboard,
int action) {
if (action != COPY && action != COPY_OR_MOVE) {
return;
}
clipboard.setContents(createTransferable(c), null);
}
@Override
public Transferable createTransferable(JComponent c) {
if (!(c instanceof JTable)) {
throw new IllegalArgumentException(
"This class can only handle export of data from a JTable.");
}
return new Transferable() {
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(DataFlavor.stringFlavor) ||
flavor.equals(DataFlavor.fragmentHtmlFlavor) ||
flavor.equals(DataFlavor.allHtmlFlavor);
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] {
DataFlavor.stringFlavor,
DataFlavor.fragmentHtmlFlavor,
DataFlavor.allHtmlFlavor
};
}
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException {
JTable table = (JTable) c;
TableModel model = table.getModel();
int columns = model.getColumnCount();
IntStream rows;
int[] selectedRows = table.getSelectedRows();
if (selectedRows.length > 0) {
Arrays.sort(selectedRows);
rows = Arrays.stream(selectedRows)
.map(table::convertRowIndexToModel);
} else {
rows = IntStream.range(0, model.getRowCount());
}
if (flavor.equals(DataFlavor.stringFlavor)) {
StringBuilder tsv = new StringBuilder();
rows.forEach(r -> {
for (int c = 0; c < columns; c++) {
if (c > 0) {
tsv.append('\t');
}
Object value = model.getValueAt(r, c);
String text = Objects.toString(value, "");
tsv.append(text.replace('\t', ' '));
}
tsv.append(System.lineSeparator());
});
return tsv.toString();
} else if (flavor.equals(DataFlavor.fragmentHtmlFlavor) ||
flavor.equals(DataFlavor.allHtmlFlavor)) {
Formatter html = new Formatter();
html.format("<table frame='border' rules='all'>\n");
rows.forEach(r -> {
html.format("<tr>");
for (int c = 0; c < columns; c++) {
html.format("<td>");
Object value = model.getValueAt(r, c);
String text = Objects.toString(value, "");
text.codePoints().forEach(p -> {
html.format(
p >= 32 && p < 127 && p != '<' && p != '&'
? "%c" : "&#%d;", p);
});
}
html.format("\n");
});
html.format("</table>");
return html.toString();
} else {
throw new UnsupportedFlavorException(flavor);
}
}
};
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JTable table = new JTable(
new Object[][] {
{1, 2, 3},
{4, 5, 6},
{7777, 88888, 99999}
},
new Object[] {"abc", "xyz", "tuv"}
);
table.setTransferHandler(new CustomHTMLTransferHandler());
JOptionPane.showMessageDialog(null, new JScrollPane(table));
});
}
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。