【问题标题】:Java JTable detect Column re-sized by userJava JTable检测用户重新调整大小的列
【发布时间】:2012-02-03 21:05:47
【问题描述】:

我有一个 JTable,它使用 TableColumnModelListener() 来检测列何时重新调整大小,并且我有一些代码要在 columnMarginChanged() 方法中执行。

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

我想我必须从ChangeEvent.getSource() 开始,但我不知道从那里去哪里。

谢谢。

【问题讨论】:

  • 除非您更改它,否则源将始终是表的DefaultTableColumnModel 通过fireColumnMarginChanged()。你想解决什么问题?
  • 没办法。重复@trashgod 的问题:为什么?
  • 如果是您的代码更改了列大小,您始终可以在更改之前停用或删除侦听器,进行更改然后响应或再次添加侦听器。

标签: java swing jtable listener


【解决方案1】:

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

【讨论】:

    【解决方案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 持久化。很棒。
    【解决方案3】:

    通常,您希望在完成列拖动或完成列调整大小时收到通知。例如

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

    【讨论】:

      【解决方案4】:

      我可以给你一种可能的方法。我试图解决同样的问题,因为我想将有关列宽的信息序列化到磁盘,以便下次在我的应用程序中打开表时,我可以适当地恢复列宽。如下:

      第 1 步 - 覆盖您的 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) { }
      }
      

      第 3 步 - 向表头添加鼠标侦听器

      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 支持打包和修复令人讨厌的核心错误的方式,即使在调整大小时也会触发排序......
      • 正如您所指出的,一种方法是在应用程序关闭时简单地序列化所有列宽(可能通过 WindowListener 的 windowClosing 方法)。对于发布此问题的人来说,这可能是正确的方法。我必须增量序列化列宽的原因是因为每次创建/销毁行/列时,我的 JTable 都会被销毁并从头重新创建。 (这可能听起来很奇怪,但有一个原因我不能在公共论坛上透露。)所以恢复原始宽度的唯一方法是逐步序列化它们。
      • 也许我可以提供更多细节。 :) 你看,我的 JTable 是一个更复杂的后端的输入表单。当用户在我的应用程序中对 JTable 进行更改时,某些内容会在后台为用户自动构建。所以我的 JTable 本质上是一个向导。为了确保表格准确反映后台自动构建的内容,我必须解析自动构建的对象并重新创建表格。如果我不这样做,就有危险(由于编程错误)表格和自动构造的对象开始发散并且不再有一个事实。
      猜你喜欢
      • 2011-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-01
      • 2019-02-21
      • 1970-01-01
      • 1970-01-01
      • 2011-10-13
      相关资源
      最近更新 更多