Как предотвратить переупорядочение одного столбца в JTable? - PullRequest
14 голосов
/ 20 июля 2009

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

table.getTableHeader().setReorderingAllowed(true);

Теперь можно изменить порядок столбцов, включая первый столбец, который мне не нужен. Есть ли способ заблокировать первый столбец?

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

Ответы [ 8 ]

7 голосов
/ 08 февраля 2010

Это решение, которое я использовал для предотвращения повторного заказа 1-го столбца

private int columnValue = -1; 
private int columnNewValue = -1; 


tblResults.getColumnModel().addColumnModelListener(new TableColumnModelListener() 
{ 
    public void columnAdded(TableColumnModelEvent e) {} 

    public void columnMarginChanged(ChangeEvent e) {} 

    public void columnMoved(TableColumnModelEvent e) 
    { 
        if (columnValue == -1) 
            columnValue = e.getFromIndex(); 

        columnNewValue = e.getToIndex(); 
    } 

    public void columnRemoved(TableColumnModelEvent e) {} 

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

tblResults.getTableHeader().addMouseListener(new MouseAdapter() 
{ 
    @Override 
    public void mouseReleased(MouseEvent e) 
    { 
        if (columnValue != -1 && (columnValue == 0 || columnNewValue == 0)) 
        tblResults.moveColumn(columnNewValue, columnValue); 

        columnValue = -1; 
        columnNewValue = -1; 
    } 
}); 

Приветствия

5 голосов
/ 23 января 2013

Спустя почти 4 года нигде не видно оптимального решения.

Еще один неоптимальный подход для предотвращения перетаскивания первого столбца (и других столбцов над первым) - это перехватывать mouseEvents до , установленный uidelegate mouseInputListener может обрабатывать их ( аналогично недавний QA ).

Соавторы

  • пользовательский MouseMotionListener, который делегирует все события первоначально установленному, кроме перетаскиваемого, если он приведет к другому столбцу выше первого
  • заменить оригинал на пользовательский
  • обновлять замену всякий раз, когда изменяется LAF (потому что оригинал контролируется пользовательским интерфейсом). Для этого необходимо создать подкласс JTableHeader и выполнить подключение в updateUI

Пользовательский MouseInputListener:

/**
 * A delegating MouseInputListener to be installed instead of
 * the one registered by the ui-delegate.
 * 
 * It's implemented to prevent dragging the first column or any other
 * column over the first.
 */
public static class DragHook implements MouseInputListener {

    private JTableHeader header;
    private MouseListener mouseDelegate;
    private MouseMotionListener mouseMotionDelegate;
    private int maxX;

    public DragHook(JTableHeader header) {
        this.header = header;
        installHook();
    }

    /**
     * Implemented to do some tweaks/bookkeeping before/after
     * passing the event to the original
     * 
     * - temporarily disallow reordering if hit on first column
     * - calculate the max mouseX that's allowable in dragging to the left
     * 
     */
    @Override
    public void mousePressed(MouseEvent e) {
        int index = header.columnAtPoint(e.getPoint());
        boolean reorderingAllowed = header.getReorderingAllowed();
        if (index == 0) {
            // temporarily disable re-ordering 
            header.setReorderingAllowed(false);
        }
        mouseDelegate.mousePressed(e);
        header.setReorderingAllowed(reorderingAllowed);
        if (header.getDraggedColumn() != null) {
            Rectangle r = header.getHeaderRect(index);
            maxX = header.getColumnModel().getColumn(0).getWidth() 
                    + e.getX() - r.x -1; 
        }
    }

    /**
     * Implemented to pass the event to the original only if the
     * mouseX doesn't lead to dragging the column over the first.
     */
    @Override
    public void mouseDragged(MouseEvent e) {
        TableColumn dragged = header.getDraggedColumn();
        int index = getViewIndexForColumn(header.getColumnModel(), dragged);
        // dragged column is at second position, allow only drags to the right
        if (index == 1) {
            if (e.getX() < maxX) return;
        }
        mouseMotionDelegate.mouseDragged(e);
    }

    //-------- delegating-only methods

    @Override
    public void mouseReleased(MouseEvent e) {
        mouseDelegate.mouseReleased(e);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        mouseDelegate.mouseClicked(e);
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        mouseDelegate.mouseEntered(e);
    }

    @Override
    public void mouseExited(MouseEvent e) {
        mouseDelegate.mouseExited(e);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        mouseMotionDelegate.mouseMoved(e);
    }

    //------------ un-/install listeners

    protected void installHook() {
        installMouseHook();
        installMouseMotionHook();
    }

    protected void installMouseMotionHook() {
        MouseMotionListener[] listeners = header.getMouseMotionListeners();
        for (int i = 0; i < listeners.length; i++) {
            MouseMotionListener l = listeners[i];
            if (l.getClass().getName().contains("TableHeaderUI")) {
                this.mouseMotionDelegate = l;
                listeners[i] = this;
            }
            header.removeMouseMotionListener(l);
        }
        for (MouseMotionListener l : listeners) {
            header.addMouseMotionListener(l);
        }
    }

    protected void installMouseHook() {
        MouseListener[] listeners = header.getMouseListeners();
        for (int i = 0; i < listeners.length; i++) {
            MouseListener l = listeners[i];
            if (l.getClass().getName().contains("TableHeaderUI")) {
                this.mouseDelegate = l;
                listeners[i] = this;
            }
            header.removeMouseListener(l);
        }
        for (MouseListener l : listeners) {
            header.addMouseListener(l);
        }
    }

    public void uninstallHook() {
        uninstallMouseHook();
        uninstallMouseMotionHook();
    }

    protected void uninstallMouseMotionHook() {
        MouseMotionListener[] listeners = header.getMouseMotionListeners();
        for (int i = 0; i < listeners.length; i++) {
            MouseMotionListener l = listeners[i];
            if (l == this) {
                listeners[i] = mouseMotionDelegate;
            }
            header.removeMouseMotionListener(l);
        }
        for (MouseMotionListener l : listeners) {
            header.addMouseMotionListener(l);
        }
    }

    protected void uninstallMouseHook() {
        MouseListener[] listeners = header.getMouseListeners();
        for (int i = 0; i < listeners.length; i++) {
            MouseListener l = listeners[i];
            if (l == this) {
                listeners[i] = mouseDelegate;
            }
            header.removeMouseListener(l);
        }
        for (MouseListener l : listeners) {
            header.addMouseListener(l);
        }
    }

}

Использование, которое переживает переключение LAF, f.i.:

JTable table = new JTable(new AncientSwingTeam()) {

    @Override
    protected JTableHeader createDefaultTableHeader() {
        JTableHeader header = new JTableHeader(getColumnModel()) {
            DragHook hook;

            @Override
            public void updateUI() {
                if (hook != null) {
                    hook.uninstallHook();
                    hook = null;
                }
                super.updateUI();
                hook = new DragHook(this);
            }

         };
        return header;
    }

};
3 голосов
/ 20 июля 2009

Я думаю, что вам нужно переопределить метод columnMoved() в TableColumnModelListener. класс TableColumnModelEvent имеет метод getFromIndex(), на который вы сможете посмотреть, чтобы определить, является ли ваш фиксированный столбец, и тогда вы сможете отменить событие.

Надеюсь, это поможет. A

2 голосов
/ 21 июля 2009

Сначала вам нужно определить лучший и более простой способ. Что вам не нравится в подходе с двумя столами?

Вы не можете использовать TableColumnModelListener, потому что событие вызывается «после» того, как столбец уже был перемещен.

Код для перетаскивания столбца находится в BasicTableHeaderUI. Таким образом, вы можете попробовать переопределить код там, но тогда вам нужно будет сделать это для всех LAF.

Приведенный выше код вызывает JTableHeader.getReorderingAllowed () для события mousePressed, чтобы определить, разрешено ли изменение порядка столбцов. Я думаю, вы могли бы переопределить этот метод в JTableHeader и, возможно, использовать класс MouseInfo, чтобы получить текущее местоположение мыши, чтобы определить, было ли оно над первым столбцом, а затем вернуть false. Но теперь вам также необходимо создать пользовательский JTable, использующий пользовательский заголовок таблицы.

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

Таблица фиксированных столбцов - моя версия того, как это будет реализовано с двумя таблицами. Это лучше? Я не знаю, но это просто, поскольку его можно использовать только одной строкой кода.

1 голос
/ 12 августа 2009

У меня была такая же проблема, и я искал об этом. Пока я нашел два способа сделать это.

  • Метод «если я сам переписывал» : изменение базовых классов из Java.

TableColumn потребуется новое свойство, например, «resizingAllowed», для него потребуется «reorderingAllowed». Исходя из этого, модификации происходят в BasicTableHeaderUI:

Уже есть:

private static boolean canResize(TableColumn column,
                                 JTableHeader header) {
    return (column != null) && header.getResizingAllowed()
                            && column.getResizable();
}

Это тоже нужно:

private static boolean canMove(TableColumn column,
                               JTableHeader header) {
    return (column != null) && header.getReorderingAllowed()
                                && column.getReorderable();
}

(Обратите внимание, что если вы не хотите, чтобы первый столбец не двигался, вы можете обойтись без изменения TableColumns:

private static boolean canMove(TableColumn column,
                                 JTableHeader header) {
    return (column != null) && header.getReorderingAllowed()
                            && header.getColumnModel().getColumnIndex(column.getIdentifier()) != 0;
}
* * Тысяча двадцать-одина)

После, два места для изменения в MouseInputListener:

  • в mousePressed, вызывая canMove() вместо header.getReorderingAllowed(). Это гарантирует, что столбец, который не должен быть перемещен, не будет.
  • Но этого недостаточно, мы должны предотвратить перемещение неподвижных столбцов при перетаскивании другого. Вам также нужно изменить mouseDragged, когда он получает "newColumnIndex":

    if (0

Вам необходимо добавить условие, если этот новый индекс можно переместить, например, с помощью метода canMove (). Таким образом, когда вы будете перетаскивать столбец в этот неподвижный, вы все равно будете его перетаскивать, но он не поменяет их местами.

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


  • Метод «Давайте попробуем заблокировать нормальное поведение тем, что у нас есть на самом деле» : не изменяя пользовательский интерфейс, этот метод фокусируется на JTableHeader для блокировки команд, выполняемых пользовательским интерфейсом.

Сначала, чтобы заблокировать перетаскивание первого столбца, нам нужен подкласс из JTableHeader с этим переопределенным методом:

@Override
public void setDraggedColumn(TableColumn pAColumn)
{
    int lIndex  = -1;
    if (pAColumn != null)
        lIndex = getColumnModel().getColumnIndex(pAColumn.getIdentifier());
    if (lIndex != 0)
        super.setDraggedColumn(pAColumn);
}

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

Пока у меня нет правильного метода для этого. Я попытался создать подкласс TableColumnModel и переопределить метод moveColumn():

@Override
public void moveColumn(int pColumnIndex, int pNewIndex)
{
    //Move only if the first column is not concerned
    if (pColumnIndex =! 0 && pNewIndex != 0)
        super.moveColumn(pColumnIndex, pNewIndex);
}

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

Так что я все еще ищу и задаюсь вопросом, есть ли у кого-то предложения относительно этой части.

0 голосов
/ 17 февраля 2015

Я просто верну колонку после завершения перемещения. Так что-то типа.

@Override
public void moveColumn(int from, int to) {
      super.moveColumn(from, to);
      if (from == 0 || to == 0) {
           super.moveColumn(to, from);
      }
}
0 голосов
/ 23 мая 2014

Сначала я воспользовался последним предложением Gnoupi, состоящим в создании подкласса TableColumnModel и переопределении moveColumn, но все еще были некоторые раздражающие переходы.

Это «моё» полностью работающее и протестированное решение без каких-либо неприятных скачков, оно в основном опирается на предложения СтаниславКо и Клеопатры. Я добавил более сложный механизм для отмены нежелательного движения при отпускании кнопки мыши:

table.getTableHeader().setUI(new WindowsTableHeaderUI() {
        @Override
        protected MouseInputListener createMouseInputListener() {
            return new BasicTableHeaderUI.MouseInputHandler() {

                @Override
                public void mouseDragged(MouseEvent e) {
                    if (header.isEnabled() && header.getReorderingAllowed() && header.getDraggedColumn() != null && header.getDraggedColumn().getModelIndex() == frozenColumnModelIndex) {
                        header.setDraggedDistance(0);
                        header.setDraggedColumn(null);
                        return;
                    }
                    super.mouseDragged(e);
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    if (header.isEnabled() && header.getReorderingAllowed() && header.getDraggedColumn() != null &&
                        0 <= illegalTableColumnMoveFromIndex && illegalTableColumnMoveFromIndex < header.getTable().getColumnModel().getColumnCount()) {
                        header.setDraggedDistance(0);
                        header.setDraggedColumn(null);
                        header.getTable().getColumnModel().moveColumn(illegalTableColumnMoveToIndex, illegalTableColumnMoveFromIndex);
                        illegalTableColumnMoveFromIndex = -1;
                        illegalTableColumnMoveToIndex = -1;
                        return;
                    }
                    super.mouseReleased(e);
                }
            };
        }
    });
    table.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
        @Override
        public void columnAdded(TableColumnModelEvent e) {
        }

        @Override
        public void columnRemoved(TableColumnModelEvent e) {
        }

        @Override
        public void columnMoved(TableColumnModelEvent e) {
            if (e.getFromIndex() != e.getToIndex() && table.getColumnModel().getColumn(e.getFromIndex()).getModelIndex() == frozenColumnModelIndex) {
                illegalTableColumnMoveFromIndex = e.getFromIndex();
                illegalTableColumnMoveToIndex = e.getToIndex();
            } else {
                illegalTableColumnMoveFromIndex = -1;
                illegalTableColumnMoveToIndex = -1;
            }
        }

        @Override
        public void columnMarginChanged(ChangeEvent e) {
        }

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

Обратите внимание, что последний действительный ход принимается вместо полного возврата перетаскивания столбца.

frozenColumnModelIndex - это индекс «замороженного» столбца в модели таблицы.

invalidTableColumnMoveFromIndex - это индекс столбца, из которого он был перемещен при обнаружении последнего незаконного перемещения.

invalidTableColumnMoveToIndex - это индекс столбца, куда он был перемещен при обнаружении последнего незаконного перемещения.

Код внутри mouseDragged достаточен для предотвращения перетаскивания замороженного столбца, а остальное позволяет предотвратить перетаскивание другого столбца в замороженный столбец.

Он работает как в Microsoft Windows, так как я расширяю WindowsTableHeaderUI, но вместо этого использую API отражения, чтобы установить прослушиватель ввода мыши для пользовательского интерфейса заголовка таблицы, вызвать uninstallerListeners () и, наконец, вызвать header.addMouseListener (mouseInputListener) и header.addMouseMotionListener ( mouseInputListener) для межплатформенного управления моим решением без каких-либо предположений относительно имени класса для каждого пользовательского интерфейса заголовка таблицы.

Я признаю, что это может быть немного менее надежным, чем решение Клеопатры. Я благодарю вас всех за вашу помощь, я очень благодарен и очень рад видеть, что совместная работа просто работает :)

0 голосов
/ 25 октября 2009

Я использовал метод «Давайте попробуем заблокировать нормальное поведение с помощью того, что у нас есть». Gnoupi сказал, что он не решил вторую часть проблемы. Вот решение для Windows XP L & F:

  1. скопируйте класс XPStyle себе.
  2. продлить WindowsTableHeaderUI. Взгляните на исходный код .
  3. используйте это: getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());

Спасибо Gnoupi за усилия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...