调整JTable列宽适配内容却显示省略号,如何精准实现?
问题原因与解决方案
你遇到的不是Swing Bug,而是计算列宽时忽略了单元格/表头的内边距(Insets)。FontMetrics只计算纯文本的宽度,但Swing单元格默认带有左右内边距,你设置的列宽刚好等于文本宽度,加上内边距后实际可用空间不足,导致文本被截断显示省略号。
解决思路
计算列宽时,需要额外加上单元格和表头渲染器的左右内边距,确保列宽能容纳文本+内边距的总宽度;同时修正原代码中的布局错误,保证初始化状态下列宽正确。
修改后的完整代码
import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Insets; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.util.Vector; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; public class Scrollable_Table_Example extends JFrame { private static final long serialVersionUID = 1L; private JPanel _panel; private JTextArea _text; private JTable _table; private DefaultTableModel _model; private JScrollPane _scroll; public static void main(String[] args) { Scrollable_Table_Example app = new Scrollable_Table_Example(); app.setVisible(true); } public Scrollable_Table_Example() { _panel = new JPanel(new BorderLayout()); setPreferredSize(new Dimension(300, 500)); _text = new JTextArea(300, 300); _text.setEditable(false); JScrollPane areaScrollPane = new JScrollPane(_text); areaScrollPane.setPreferredSize(new Dimension(250, 250)); areaScrollPane.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); areaScrollPane.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); areaScrollPane.setViewportView(_text); // 修正原add方法的错误,改用setViewportView设置滚动面板内容 _panel.add(areaScrollPane, BorderLayout.NORTH); _model = new DefaultTableModel(); _model.addColumn("Type"); _model.addColumn("Schema"); _model.addColumn("Module Name"); _model.addColumn("Path"); _table = new JTable(); _table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF ); _table.getTableHeader().setResizingAllowed(false); _table.getTableHeader().setReorderingAllowed(false); _table.setModel(_model); addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { resizeColumnWidth(_table); } }); for(int i = 0; i < 20; i++) { Vector<String> r = new Vector<String>(); r.addElement("TABLE"); r.addElement("dbo"); r.addElement("Policy"); r.addElement("OBJECTS -> INSURANCE -> Policy"); _model.addRow(r); } _scroll = new JScrollPane(_table); _panel.add(_scroll, BorderLayout.CENTER); add(_panel); pack(); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setLocationRelativeTo(null); resizeColumnWidth(_table); // 初始化时调用一次,确保初始列宽正确 } public static void resizeColumnWidth(JTable table) { final TableColumnModel columnModel = table.getColumnModel(); FontMetrics fontMetrics = table.getFontMetrics(table.getFont()); for (int column = 0; column < table.getColumnCount(); column++) { String headerValue = columnModel.getColumn(column).getHeaderValue().toString(); // 获取表头渲染器的内边距,计算表头总宽度 TableCellRenderer headerRenderer = table.getTableHeader().getDefaultRenderer(); Component headerComp = headerRenderer.getTableCellRendererComponent(table, headerValue, false, false, 0, column); Insets headerInsets = headerComp.getInsets(); int headerWidth = fontMetrics.stringWidth(headerValue) + headerInsets.left + headerInsets.right; // 获取单元格渲染器的内边距,计算内容所需额外宽度 TableCellRenderer cellRenderer = table.getDefaultRenderer(table.getColumnClass(column)); Component cellComp = cellRenderer.getTableCellRendererComponent(table, "", false, false, 0, column); Insets cellInsets = cellComp.getInsets(); int cellExtraWidth = cellInsets.left + cellInsets.right; int width = headerWidth; // 计算每一行内容的总宽度,取最大值 for (int row = 0; row < table.getRowCount(); row++) { String columnValue = table.getModel().getValueAt(row, column).toString(); int columnValueWidth = fontMetrics.stringWidth(columnValue) + cellExtraWidth; width = Math.max(width, columnValueWidth); } // 加2像素缓冲避免极端情况截断 columnModel.getColumn(column).setPreferredWidth(width + 2); } int parentWidth = table.getParent().getSize().width; int tableWidth = table.getPreferredSize().width; if(parentWidth > tableWidth) { int diff = parentWidth - tableWidth; int newWidth = columnModel.getColumn(3).getPreferredWidth() + diff; columnModel.getColumn(3).setPreferredWidth(newWidth); } } }
关键修正说明
- 内边距计算:分别获取表头和单元格渲染器的内边距,将其加到文本宽度上,确保列宽足够容纳所有内容。
- 滚动面板内容设置:原代码用
add方法给滚动面板加内容是错误的,改用setViewportView符合Swing规范。 - 初始化调用:窗口初始化完成后调用一次列宽调整,避免初始状态下列宽不正确。
内容的提问来源于stack exchange,提问作者Newbie Guest
相关产品推荐
相关产品推荐

