You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调整列宽时保持JScrollPane中的JTable位置固定

解决JTable列宽调整时保持视口显示区域固定的问题

这个问题的核心在于:当你加宽视口左侧的列时,表格的整体内容宽度会增加,所有右侧的列会向右偏移,导致JScrollPane的视口看起来“向左滚动”。直接调整滚动条数值或者视口位置没生效,是因为列宽变化后,表格的总宽度改变了,滚动条的maximum值也会更新,此时单纯的数值补偿不准确。

解决方案思路

我们需要在修改列宽之前记录视口在表格中的绝对位置,修改列宽后,根据列宽的变化量,将视口调整回对应的绝对位置,这样就能保证显示的区域完全不变。具体来说:

  1. 记录调整前列的原始宽度和视口左边缘在表格中的x坐标
  2. 计算列宽变化的差值(delta)
  3. 如果该列在视口左侧,加宽后表格内容会整体右移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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:42:49