调整列大小时使 JTable 在 JScrollPane 中保持静止

Keep JTable Stationary in JScrollPane When Resizing Columns

我在 JScrollPane 里面有一个 JTable。用户向右滚动。然后,我将用户正在查看的位置左侧的一列放大 x 像素。这会导致 table 向左滚动。如何调整 JScrollPaneJScrollBarJViewport 以保持 table 静止?换句话说,如何在屏幕上保持 table 的相同可见部分?

我尝试将水平 JScrollBar 的值调整 x。我还尝试通过 x 调整 JViewport 的视图位置。问题是 table 最终向右滚动。

这是显示问题的代码,我没有尝试保持 table 静止。

package oracle.psr.ndr.guiclient.util.table;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
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 JScrollBar       m_bar;
   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_bar   = pane.getHorizontalScrollBar();

      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_bar.setValue(m_bar.getMaximum() / 3);
   }

   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);

      column.setPreferredWidth(200);
      m_colIndex++;
   }
}

应他人的要求,我创建了一些简单的代码来说明问题。使用这个简单的代码,创建解决方案变得很容易。

该解决方案添加了一个新方法 getX() 并向 adjust() 添加了更多代码。 getX() 在进行任何列大小更改之前计算列的最左侧位置。此值存储在局部变量 x.

该解决方案还会在更改任何列大小之前捕获 JScrollBar.getValue()。此值存储在局部变量 value.

改变列大小后,再比较x < value。如果列的最左侧位置不在视图的左侧,则为真。如果为真,则将列宽的差异添加到 value 并调用 JScrollBar.setValue()。因此,离开视图左侧的列将导致滚动条移动到足以使可见列保持在视图中。右边的列将被简单地调整。

小心TableColumn.getMaxWidth()。设置列宽时,列宽将遵循这一点。处理 getMaxWidth() 的代码在下面,即使它对 UI 没有影响,因为原始 post 什么都不做,不调用 TableColumn.setMaxWidth().

private void adjust(@SuppressWarnings("unused") ActionEvent event)
{
   TableColumn column;
   int count, old, next, value, x, max;

   count = m_model.getColumnCount();

   if (m_colIndex >= count)
      return;

   x      = getX(m_colIndex);
   value  = m_bar.getValue();
   column = m_model.getColumn(m_colIndex);
   old    = column.getPreferredWidth();
   max    = column.getMaxWidth();
   next   = 200;

   column.setPreferredWidth(next);
   m_colIndex++;

   if (x < value)
      m_bar.setValue(value + Math.min(next, max) - old);
}

private int getX(int colIndex)
{
   TableColumn column;
   int result;

   result = 0;

   while (--colIndex >= 0)
   {
      column  = m_model.getColumn(colIndex);
      result += column.getPreferredWidth();
   }

   return(result);
}