调整列宽时保持JScrollPane中的JTable位置固定
解决JTable列宽调整时保持视口显示区域固定的问题
这个问题的核心在于:当你加宽视口左侧的列时,表格的整体内容宽度会增加,所有右侧的列会向右偏移,导致JScrollPane的视口看起来“向左滚动”。直接调整滚动条数值或者视口位置没生效,是因为列宽变化后,表格的总宽度改变了,滚动条的maximum值也会更新,此时单纯的数值补偿不准确。
解决方案思路
我们需要在修改列宽之前记录视口在表格中的绝对位置,修改列宽后,根据列宽的变化量,将视口调整回对应的绝对位置,这样就能保证显示的区域完全不变。具体来说:
- 记录调整前列的原始宽度和视口左边缘在表格中的x坐标
- 计算列宽变化的差值(delta)
- 如果该列在视口左侧,加宽后表格内容会整体右移delta,因此视口的x位置需要增加delta,才能保持显示原来的内容区域
修改后的代码实现
下面是基于你的代码调整后的完整版本:
package oracle.psr.ndr.guiclient.util.table; import java.awt.BorderLayout; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JViewport; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; public final class StableColumnResizing extends JFrame { private static final long serialVersionUID = 738732002168497075L; private final TableColumnModel m_model; private final JViewport m_viewport; // 添加视口引用 private int m_colIndex; public static void main(String args[]) { SwingUtilities.invokeLater(StableColumnResizing::create); } private static void create() { StableColumnResizing frame; Timer timer; frame = new StableColumnResizing(); frame.setVisible(true); SwingUtilities.invokeLater(frame::moveRight); timer = new Timer(1000, frame::adjust); timer.start(); } private StableColumnResizing() { JScrollPane pane; JTable table; Object rowData[][], columnNames[]; columnNames = new Object[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; rowData = new Object[][]{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}}; table = new JTable(rowData, columnNames); m_model = table.getColumnModel(); pane = new JScrollPane(); m_viewport = pane.getViewport(); // 初始化视口引用 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); pane.setViewportView(table); add(pane, BorderLayout.CENTER); setSize(1024, 768); setLocationByPlatform(false); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } private void moveRight() { // 直接通过视口设置位置,比操作滚动条更直观 m_viewport.setViewPosition(new Point(m_model.getTotalColumnWidth() / 3, 0)); } private void adjust(@SuppressWarnings("unused") ActionEvent event) { TableColumn column; int count; count = m_model.getColumnCount(); if (m_colIndex >= count) return; column = m_model.getColumn(m_colIndex); // 1. 记录调整前的关键状态 Rectangle viewRect = m_viewport.getViewRect(); int originalViewportX = viewRect.x; int originalColumnWidth = column.getPreferredWidth(); int newColumnWidth = 200; int delta = newColumnWidth - originalColumnWidth; // 2. 修改列宽 column.setPreferredWidth(newColumnWidth); // 3. 调整视口位置:只有当该列在视口左侧时才需要补偿 if (originalViewportX > column.getWidth()) { m_viewport.setViewPosition(new Point(originalViewportX + delta, viewRect.y)); } m_colIndex++; } }
关键细节解释
- 使用
JViewport而非直接操作滚动条:视口的getViewRect()方法能直接获取视口在表格中的绝对坐标,比滚动条的value更可靠,因为滚动条的数值是基于自身范围的,而列宽变化会改变这个范围。 - 判断列是否在视口左侧:通过
originalViewportX > column.getWidth()判断,确保只有当被加宽的列在视口之外的左侧时,才调整视口位置——如果列在视口内部,加宽它只会让列本身变宽,不会导致整个视口偏移,不需要调整。 - delta计算:列宽增加的差值就是表格内容右移的距离,因此视口需要同步右移相同的距离,才能保持显示的内容区域不变。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

