Столбец перемещен [закончено] событие в JTable

Как я должен обнаружить, что действие перемещения столбца закончено в JTable? Я добавил columnModeListener в мою модель столбца, но проблема в методе columnMoved вызывается каждый раз, когда столбец перемещается (на определенные пиксели). Я не хочу этого поведения. Я просто хочу определить, когда закончится перетаскивание столбца.

columnModel.addColumnModelListener(new TableColumnModelListener() {

            public void columnAdded(TableColumnModelEvent e) {
            }

            public void columnRemoved(TableColumnModelEvent e) {
            }

            public void columnMoved(TableColumnModelEvent e) {
                //this is called so many times
                //I don't want this, but something like column moved finished event
                System.out.println("Moved "+e.getFromIndex()+", "+e.getToIndex());
            }

            public void columnMarginChanged(ChangeEvent e) {
            }

            public void columnSelectionChanged(ListSelectionEvent e) {
            }
        });

Я надеюсь, что это понятно, что я ищу. Спасибо.

Ответ 1

Это то, что я закончил делать. Я знаю, что он грязный, но он подходит для того, что я ищу:

boolean dragComplete = false;
        apTable.getTableHeader().addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if (dragComplete) {
                    System.out.println("Drag completed");
                }
                dragComplete = false;
            }
        });
        columnModel.addColumnModelListener(new TableColumnModelListener() {

            public void columnAdded(TableColumnModelEvent e) {
            }

            public void columnRemoved(TableColumnModelEvent e) {
            }

            public void columnMoved(TableColumnModelEvent e) {
                dragComplete = true;
            }

            public void columnMarginChanged(ChangeEvent e) {
            }

            public void columnSelectionChanged(ListSelectionEvent e) {
            }
        });

Ответ 2

Здесь внутренний класс, который я использую, чтобы определить, когда порядок столбцов изменился. Обратите внимание, что пользователь может не отпустить мышь в этот момент, поэтому перетаскивание может продолжаться и дальше.

private class ColumnUpdateListener implements TableColumnModelListener {

   int lastFrom = 0;
   int lastTo = 0;

   private void verifyChange(int from, int to) {
      if (from != lastFrom || to != lastTo) {
         lastFrom = from;
         lastTo = to;

         ///////////////////////////////////////
         // Column order has changed!  Do something here
         ///////////////////////////////////////
      }
   }

   public void columnMoved(TableColumnModelEvent e) {
      verifyChange(e.getFromIndex(), e.getToIndex());
   }

   public void columnAdded(TableColumnModelEvent e) {
      verifyChange(e.getFromIndex(), e.getToIndex());
   }

   public void columnRemoved(TableColumnModelEvent e) {
      verifyChange(e.getFromIndex(), e.getToIndex());
   }

   public void columnMarginChanged(ChangeEvent e) {}
   public void columnSelectionChanged(ListSelectionEvent e) {}

}

Это сработало для меня.

Ответ 3

Это может быть лучший подход:

table.setTableHeader(new JTableHeader(table.getColumnModel()) {

      @Override
      public void setDraggedColumn(TableColumn column) {
        boolean finished = draggedColumn != null && column == null;
        super.setDraggedColumn(column);
        if (finished) {
          onColumnChange(table); // Handle the event here...
        }
      }
    });

Ответ 4

Это то, что работает для меня (оба перемещения столбцов и размеры полей):

Я расширяю таблицу и переопределяю columnMoved и columnMarginChanged методов следующим образом:

... сначала добавьте некоторые переменные для сохранения состояния

private int lastMovementDistance = 0;
private boolean bigMove = false;
private boolean resizeBegan = false;

...

@Override
public void columnMarginChanged(ChangeEvent e) {
   super.columnMarginChanged(e);
   if (isShowing()){
      resizeBegan = true;
   }
}

@Override
public void columnMoved(TableColumnModelEvent e) {
   super.columnMoved(e);

   //this will be set to 0 when the column is dragged
   //to where it should begin if released       
   lastMovementDistance = Math.abs(getTableHeader().getDraggedDistance());


   if (e.getFromIndex() != e.getToIndex()){
     //note, this does not guarantee that the columns will be eventually
     //swapped - the user may move the column back.
     //but it prevents us from reacting to movements where
     //the user hasn't even moved the column further then its nearby region.
     //Works for me, because I don't care if the columns stay the same
     //I only need the updates to be infrequent and don't want to miss
     //changes to the column order
 bigMove = true;
   }
}

... то в конструкторе моей таблицы я делаю это:

 public MyTable(){

   ...

 getTableHeader().addMouseListener(new MouseAdapter(){

     public void mouseReleased(MouseEvent evt) {  
      if (bigMove && lastMovementDistance == 0 ){
         //react! the tables have possibly switched!
      } 

          else if (resizeBegan){
    //react! columns resized
      }

      resizeBegan = false;
      bigMove = false;
 } 
 });
  ...
}

Это своего рода хак, но он работает для меня.

Ответ 5

Хороший ответ на свой вопрос ashokgelal. Думаю, небольшое улучшение. Ваш код также запускается одним нажатием на заголовок. Используя еще один флаг, вы можете предотвратить триггер "dragComplete", когда столбец не изменился. Измененный код:

    boolean mDraggingColumn = false;
    boolean mColumnCHangedIndex = false;

    tblObjects.getTableHeader().addMouseListener(new MouseAdapter() {
        @Override
        public void mouseReleased(MouseEvent e) {
            if (mDraggingColumn && mColumnCHangedIndex) {
                System.out.println("Column changed");
            }
            mDraggingColumn = false;
            mColumnCHangedIndex = false;
        }
    });
    tblObjects.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
        @Override
        public void columnAdded(TableColumnModelEvent e) {}
        @Override
        public void columnRemoved(TableColumnModelEvent e) {}
        @Override
        public void columnMoved(TableColumnModelEvent e) {
            mDraggingColumn = true;
            if (e.getFromIndex() != e.getToIndex()) {
                mColumnCHangedIndex = true;
            }
        }
        @Override
        public void columnMarginChanged(ChangeEvent e) {}
        @Override
        public void columnSelectionChanged(ListSelectionEvent e) {}
    });

Ответ 6

Если я правильно вас понимаю, возможно, вы хотите посмотреть на слушателей мыши. Может быть событие MOUSE_RELEASED?

Ответ 7

Все ответы терпят неудачу в одном случае: если таблица находится в макете, заполняющей все окно, то изменение размера окна приведет к изменению размера таблицы и, следовательно, ее столбцов. Наблюдая за событиями мыши в заголовках столбцов, мы не можем получить событие, когда пользователь изменит размер окна.

Я посмотрел исходный код JTable & friends, и метод columnMarginChanged() всегда вызывается в суб-суб-суб-функции, вызываемой JTable.doLayout().

Тогда мое решение - смотреть вызовы doLayout(), которые запускают хотя бы один столбецMarginChanged().

Фактически, для каждого столбца вызывается columnMarginChanged().

Вот мое решение:

private class ResizableJTable extends JTable {

    private TableColumnModelListener columnModelListener;

    private boolean columnsWereResized;

    @Override
    public void setColumnModel(TableColumnModel columnModel) {
        if (getColumnModel() != null) {
            getColumnModel().removeColumnModelListener(columnModelListener);
            columnModelListener = null;
        }

        if (columnModel != null) {
            columnModelListener = new TableColumnModelListener() {

                public void columnSelectionChanged(ListSelectionEvent e) {
                    // Nothing to do
                }

                public void columnRemoved(TableColumnModelEvent e) {
                    // Nothing to do
                }

                public void columnMoved(TableColumnModelEvent e) {
                    // Nothing to do
                }

                public void columnMarginChanged(ChangeEvent e) {
                    columnsWereResized = true;
                }

                public void columnAdded(TableColumnModelEvent e) {
                    // Nothing to do
                }
            };

            columnModel.addColumnModelListener(columnModelListener);
        }

        super.setColumnModel(columnModel);
    }

    @Override
    public void doLayout() {
        columnsWereResized = false;
        super.doLayout();
        if (columnsWereResized) {
            onColumnsResized();
        }
    }

    /**
     * Sub-classes can override this method to
     * get the columns-were-resized event.
     * By default this method must be empty,
     * but here we added debug code.
     */
    protected void onColumnsResized() {
        int[] columnSizes = getColumnSizes();
        String sizes = "";
        for (int i : columnSizes) {
            sizes += i + " ";
        }
        System.out.println("COLUMNS RESIZED: [ " + sizes + "]");
    }

    protected int[] getColumnSizes() {
        TableColumnModel columnModel = getTableHeader().getColumnModel();
        int columnCount = columnModel.getColumnCount();
        int[] columnSizes = new int[columnCount];

        for(int i = 0; i < columnCount; i++) {
            TableColumn column = columnModel.getColumn(i);
            columnSizes[i] = column.getWidth();
        }

        return columnSizes;
    }
}