Java JTable检测用户调整列大小

12

我有一个使用TableColumnModelListener()来检测列是否已经被调整大小的JTable,并且我有一些想要在columnMarginChanged()方法中执行的代码。

如何确定该列是由用户调整大小还是由其他代码引起的?

我认为我必须从ChangeEvent.getSource()开始,但我不知道接下来该如何操作。

谢谢。


除非您更改它,否则源始终是表的“DefaultTableColumnModel”通过“fireColumnMarginChanged()”。您试图解决什么问题? - trashgod
没有办法。重复@trashgod的问题:为什么? - kleopatra
如果是您的代码更改了列大小,您可以在更改之前始终停用或删除监听器,进行更改,然后再次启用或添加监听器。 - Robin
4个回答

14

我可以给你一个可能的方案。我曾尝试解决同样的问题,因为我想将关于列宽的信息序列化到磁盘上,以便下次在我的应用程序中打开表格时,可以恢复适当的列宽。具体步骤如下:

第一步 - 重写JTable并添加一个布尔属性

class MyTable extends JTable {

    private boolean isColumnWidthChanged;
    public boolean getColumnWidthChanged() {
        return isColumnWidthChanged;
    }

    public void setColumnWidthChanged(boolean widthChanged) {
        isColumnWidthChanged = widthChanged;
    }

}

步骤2 - 将TableColumnModelListener()添加到表格中

private class TableColumnWidthListener implements TableColumnModelListener
{
    @Override
    public void columnMarginChanged(ChangeEvent e)
    {
        /* columnMarginChanged is called continuously as the column width is changed
           by dragging. Therefore, execute code below ONLY if we are not already
           aware of the column width having changed */
        if(!tableObj.hasColumnWidthChanged())
        {
            /* the condition  below will NOT be true if
               the column width is being changed by code. */
            if(tableObj.getTableHeader.getResizingColumn() != null)
            {
                // User must have dragged column and changed width
                tableObj.setColumnWidthChanged(true);
            }
        }
    }

    @Override
    public void columnMoved(TableColumnModelEvent e) { }

    @Override
    public void columnAdded(TableColumnModelEvent e) { }

    @Override
    public void columnRemoved(TableColumnModelEvent e) { }

    @Override
    public void columnSelectionChanged(ListSelectionEvent e) { }
}

第三步 - 在表头中添加鼠标监听器

private class TableHeaderMouseListener extends MouseAdapter
{
    @Override
    public void mouseReleased(MouseEvent e)
    {
        /* On mouse release, check if column width has changed */
        if(tableObj.getColumnWidthChanged())
        {
            // Do whatever you need to do here

            // Reset the flag on the table.
            tableObj.setColumnWidthChanged(false);
        }
    }
}

注意:在我的应用程序中,TableHeaderMouseListener和TableColumnWidthListener类是我的主应用程序类的私有内部类。我的主应用程序类持有对被观察的表的引用。因此,这些内部类可以访问表实例。显然,根据您的设置,您需要采取适当的措施将表实例提供给这些其他类。希望这能有所帮助!


(不太明白您的要求-为什么不直接始终保存宽度?)但是关于标记:不要使用光标,因为它非常脆弱,正如您已经注意到的一样 :-) 使用标题的resizingColumn而不是光标:只有在用户进行调整大小时才会!= null。这就是JXTableHeader支持打包并修复触发排序的恼人核心错误的方式... - kleopatra
1
正如您所指出的,一种方法是在应用程序关闭时(可能通过WindowListener的windowClosing方法)简单地序列化所有列宽。这可能是发布此问题的人的正确方法。我必须逐步序列化列宽的原因是每次创建/销毁行/列时都会从头开始销毁并重新创建我的JTable。(这听起来可能很奇怪,但我不能在公共论坛上透露原因。)因此,获取回原始宽度的唯一方法是逐步序列化它们。 - eternaln00b
也许我可以提供更多的细节。 :) 你看,我的 JTable 是一个更复杂后端的输入表单。当用户在我的应用程序中对 JTable 进行更改时,某些东西会在后台自动构建给用户。因此,我的 JTable 本质上是一个向导。为了确保表格完全反映了在后台自动构建的内容,我必须解析自动构建的对象并重新创建表格。如果我不这样做,由于编程错误的危险,表格和自动构建的对象可能开始分歧,就不再有一个真相了。 - eternaln00b

4
通常情况下,您希望在完成列拖动或完成列调整大小时收到通知。例如:
interface SGridModel {
    public void columnMoved(int oldLocation, int newLocation);
    public void columnResized(int column, int newWidth);
}        

这个代码不太好看,但能实现你想要的功能:
class SColumnListener extends MouseAdapter implements TableColumnModelListener {

    private final Logger log = Logger.getLogger(getClass());
    private final SGridModel model;

    private int oldIndex = -1;
    private int newIndex = -1;
    private boolean dragging = false;

    private boolean resizing = false;
    private int resizingColumn = -1;
    private int oldWidth = -1;

    SColumnListener(SGridModel model){
        this.model = model;
    }

    @Override
    public void mousePressed(MouseEvent e) {
        // capture start of resize
        if(e.getSource() instanceof JTableHeader) {
            TableColumn tc = ((JTableHeader)e.getSource()).getResizingColumn();
            if(tc != null) {
                resizing = true;
                resizingColumn = tc.getModelIndex();
                oldWidth = tc.getPreferredWidth();
            } else {
                resizingColumn = -1;
                oldWidth = -1;
            }
        }   
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        // column moved
        if(dragging && oldIndex != newIndex) {
            model.columnMoved(oldIndex, newIndex);
            log.info("column moved: " +oldIndex+" -> "+newIndex);
        }
        dragging = false;
        oldIndex = -1;
        newIndex = -1;


        // column resized
        if(resizing) {
            if(e.getSource() instanceof JTableHeader) {
                TableColumn tc = ((JTableHeader)e.getSource()).getColumnModel().getColumn(resizingColumn);
                if(tc != null) {
                    int newWidth = tc.getPreferredWidth();
                    if(newWidth != oldWidth) {
                        model.columnResized(resizingColumn, newWidth);
                        log.info("column resized: " +resizingColumn+" -> "+newWidth);
                    }
                }
            }   
        }
        resizing = false;
        resizingColumn = -1;
        oldWidth = -1;
    }

    @Override
    public void columnAdded(TableColumnModelEvent e) {      
    }

    @Override
    public void columnRemoved(TableColumnModelEvent e) {        
    }

    @Override
    public void columnMoved(TableColumnModelEvent e) {
        // capture dragging
        dragging = true;
        if(oldIndex == -1){
            oldIndex = e.getFromIndex();
        }

        newIndex = e.getToIndex();  
    }

    @Override
    public void columnMarginChanged(ChangeEvent e) {
    }

    @Override
    public void columnSelectionChanged(ListSelectionEvent e) {
    }
}

将其添加到表格中如下:
table.getColumnModel().addColumnModelListener(cl);
table.getTableHeader().addMouseListener(cl);

2
一个更简单的解决方案可能只是监听鼠标释放事件(这在用户交互中只会发生一次),并检查列大小是否在此期间已更改?我使用以下代码来监听列重新排序和大小更改。
getTableHeader().addMouseListener( new MouseAdapter() {
    public void mouseReleased(MouseEvent arg0)
    {
      String colNamesAndSizes = "";
      for( int i=0;i<getColumnModel().getColumnCount();i++ ) {

        if( i>0 ) colNamesAndSizes += ",";
        TableColumn column = getColumnModel().getColumn(i);
        colNamesAndSizes += column.getHeaderValue();
        colNamesAndSizes += ":";
        colNamesAndSizes += column.getWidth();
      }
      // check if changed, if yes, persist...
   }});

使用Map<columnName, columnWidth>很容易进行持久化。非常好。 - Alex Byrth

0
您还可以在每个TableColumn上添加PropertyChangeListener来捕获widthpreferredWidth属性的更改。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接