Подтвердить что ты не робот

Как сохранить один столбец от переупорядочения в JTable?

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

table.getTableHeader().setReorderingAllowed(true);

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Это решение, которое я использовал, чтобы предотвратить переупорядочение 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; 
    } 
}); 

Приветствия,

Ответ 3

Почти 4 года спустя, по-прежнему нет оптимального решения в поле зрения.

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

Сотрудники

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

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

/**
 * A delegating MouseInputListener to be installed instead of
 * the one registered by the ui-delegate.
 * 
 * It 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 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;
    }

};

Ответ 4

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

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

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

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

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

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

Ответ 5

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

  • Метод "если я переписывал его самостоятельно" : изменение базовых классов из 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 < newColumnIndex && newColumnIndex < cm.getColumnCount())

Вам нужно добавить условие, если этот новый индекс можно перемещать, например, используя метод 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, у вас будет переход от перетащенного столбца в другое место.

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

Ответ 6

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

  • Скопируйте класс XPStyle для себя.
  • expand WindowsTableHeaderUI. Посмотрите исходный код.
  • используйте его: getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());

Благодаря Gnoupi для усилий.

Ответ 7

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

Это "мое" полностью работающее и проверенное решение без неприятного прыжка, оно в основном опирается на предложения StanislavKo и kleopatra. Я добавил более сложный механизм, чтобы вернуть нежелательный ход при отпускании кнопки мыши:

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 - это индекс "замороженного" столбца в модели таблицы.

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

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

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

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

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

Ответ 8

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

@Override
public void moveColumn(int from, int to) {
      super.moveColumn(from, to);
      if (from == 0 || to == 0) {
           super.moveColumn(to, from);
      }
}