如何防止 JTable 中的单个列被重新排序?

发布于 2024-07-27 13:33:52 字数 248 浏览 2 评论 0原文

我有一个 JTable,我需要能够对列进行重新排序。 但是我希望第一列无法重新排序。 我使用以下命令来启用重新排序:

table.getTableHeader().setReorderingAllowed(true);

现在可以对列进行重新排序,包括我不想要的第一列。 有什么办法可以锁定第一列吗?

我见过一些使用两个表的解决方案,第一列位于单独的表中,但也许有更好/更简单的方法。

I have a JTable and I need to be able to reorder the columns. However I want the first column to not be able to be re-ordered. I used the following to enable reordering:

table.getTableHeader().setReorderingAllowed(true);

The columns can now be reordered including the first column which I don't want. Is there any way to lock the first column?

I have seen some solutions that use two tables with the first column being in a separate table, but maybe there's a better/simpler way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

一枫情书 2024-08-03 13:33:52

这是我用来防止第一列被重新排序的解决方案

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

干杯,

This is the solution that I used to prevent the 1st column from being re-ordered

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

Cheers,

晌融 2024-08-03 13:33:52

近四年过去了,仍然看不到最佳解决方案。

防止拖动第一列(以及第一列上的其他列)的另一种次优方法是在 uidelegate 安装的 mouseInputListener 可以处理 mouseEvents 之前拦截它们(类似于最近的质量检查)。

协作者

  • 自定义 MouseMotionListener,它将所有事件委托给最初安装的事件,除非拖动会导致第一个之上的另一列,
  • 用自定义更新替换原始事件
  • 每当 LAF 更改时, (因为原始事件是由 ui 控制的) )。 这需要 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、fi 切换后仍可使用:

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

};

Nearly 4 years later, there's still no optimal solution in sight anywhere.

Yet another suboptimal approach to prevent dragging of the first column (and other columns over the first) is to intercept the mouseEvents before the mouseInputListener installed by the uidelegate can handle them (similar to a recent QA).

The collaborators

  • a custom MouseMotionListener which delegates all events to the originally installed, except the dragged if it would lead to another column above the first
  • replace the original with the custom
  • update the replacement whenever the LAF is changed (because the original is controlled by the ui). This requires subclassing of JTableHeader and do the wiring in updateUI

The custom 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);
        }
    }

}

Usage which survives switching of 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;
    }

};
一个人的旅程 2024-08-03 13:33:52

我认为您需要重写 TableColumnModelListener 中的 columnMoved() 方法。 TableColumnModelEvent 类有一个 getFromIndex() 方法,您应该能够查看该方法以确定它是否是您的固定列,然后您应该能够取消该事件。

希望有帮助。 A

I think that you need to override the columnMoved() method in TableColumnModelListener. the TableColumnModelEvent class has a getFromIndex() method that you should be able to look at to determine if it's your fixed column, and then you should be able to cancel the event.

Hope that helps. A

尬尬 2024-08-03 13:33:52

首先,您需要定义一种更好、更简单的方法。 您不喜欢 2 表方法的哪些方面?

您不能使用 TableColumnModelListener,因为该事件是在列已移动“之后”触发的。

拖动列的代码可以在BasicTableHeaderUI 中找到。 因此,您可以尝试覆盖那里的代码,但随后您需要对所有 LAF 执行此操作。

上面的代码在 mousePressed 事件上调用 JTableHeader.getReorderingAllowed() 来确定是否允许列重新排序。 我想您可以重写 JTableHeader 中的该方法,并可能使用 MouseInfo 类来获取当前鼠标位置,以确定它是否位于第一列上方,然后返回 false。 但现在您还需要创建一个使用自定义表头的自定义 JTable。

当然,通过上述任一建议,您也许可以防止第一列被移动。 但不要忘记,您还需要防止第二列插入到第一列之前。 我不认为这个问题有一个简短的答案。

固定列表 是我的版本用两个表来实现。 好点吗? 我不知道,但它很简单,因为只需一行代码即可使用它。

First you need to define a better and simpler way. What don't you like about the 2 table approach?

You can't use a TableColumnModelListener, because the event is fired "after" the column has already been moved.

The code for dragging the column is found in the BasicTableHeaderUI. So you could try overriding the code there, but then you would need to do it for all LAFs.

The above code invokes JTableHeader.getReorderingAllowed() on a mousePressed event to determine if column reordering is allowed. I guess you could override that method in the JTableHeader and perhaps use the MouseInfo class to get the current mouse location to determine if it was over the first column and then return false. But then now you would also need to create a custom JTable that uses the custom table header.

Of course with either of the above suggestions you might be able to prevent the first column from being moved. But don't forget you also need to prevent the 2nd column from being inserted before the first column. I don't believe there is a short simple answer to the question.

Fixed Column Table is my version of how this would be imlemented with two tables. Is it better? I don't know, but it is simple since its only a single line of code to use it.

小猫一只 2024-08-03 13:33:52

我有同样的问题,我正在搜索它。 到目前为止,我找到了两种方法。

  • “如果我自己重写”方法:从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()”方法。 这样,当您将一列拖到这个不可移动的列时,您仍然会拖动它,但不会交换它们。

请注意,此方法需要您显式设置用于 JTable 的 JTableHeader 的 UI,这并不是很理想。 但这是最适合的,因为它在它应该处理的地方处理问题。


  • “让我们尝试用我们实际拥有的东西来阻止正常行为”方法:不修改 UI,此方法专注于 JTableHeader 来阻止 UI 发出的命令。

首先,要阻止拖动第一列,我们需要 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);
}

但这不起作用,因为 UI 无论如何都会更新 mouseDragged 方法中的鼠标位置,您将从拖动的列跳转到另一个地方。

所以我仍在寻找,想知道是否有人对这部分有建议。

I had the same issue, and I was searching about it. So far I found two ways of doing that.

  • The "if I was rewriting it myself" method : Modifying the base classes from Java.

TableColumn would need a new property, like the "resizingAllowed", it would need the "reorderingAllowed".
From this, the modifications take place in BasicTableHeaderUI :

There is already :

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

It would need too :

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

(Note that if you don't want the first column only to not move, you can do without changing the TableColumns :

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

)

After, two places to modify in the MouseInputListener :

  • in the mousePressed, calling the canMove() instead of the header.getReorderingAllowed(). This ensures that a column which shouldn't be moved, won't be.
  • But this is not enough, we need to prevent the immobile columns from being moved during dragging another one. You need to change the mouseDragged, too, when it is getting the "newColumnIndex" :

    if (0 < newColumnIndex && newColumnIndex < cm.getColumnCount())

You need to add the condition if this new index can be moved, for example using the "canMove()" method. This way, when you will drag a column to this immobile one, you will still drag it, but it won't swap them.

Note that this method would require you to explicitly set the UI for the JTableHeader used for your JTable, which is not really ideal. But this is the most adapted though, as it deals with the problem on the place it is supposed to.


  • The "Let's try to block the normal behavior with what we actually have" method : Not modifying the UI, this method focus on the JTableHeader to block the commands made by the UI.

First, to block dragging the first column, we need a subclass from JTableHeader, with this overridden method :

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

This will prevent a user from dragging the first column. But like described earlier, this is only one part of the problem, we need to prevent another dragged column from swapping with this first one.

So far, I don't have a correct method for this. I tried by subclassing the TableColumnModel, and overriding the moveColumn() method :

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

But this won't work, as the UI will update anyway the mouse position in the mouseDragged method, you will have a jump from your dragged column to another place.

So I'm still searching, and wonder if someone has propositions concerning this part.

半窗疏影 2024-08-03 13:33:52

起初,我使用了 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) {
        }
    });

请注意,接受最新的有效移动而不是完全恢复列拖动。

freezeColumnModelIndex 是表模型中“冻结”列的索引。

invalidTableColumnMoveFromIndex 是检测到最新非法移动时从其移动位置开始的列的索引。

invalidTableColumnMoveToIndex 是检测到最新非法移动时移动到的列的索引。

mouseDragged 中的代码足以防止冻结列被拖动,其余代码可以防止另一列被拖动到冻结列。

它在 Microsoft Windows 下工作,因为我扩展了 WindowsTableHeaderUI,而是使用反射 API 设置表头 UI 的鼠标输入侦听器,调用 uninstallerListeners() 并最终调用 header.addMouseListener(mouseInputListener) 和 header.addMouseMotionListener(mouseInputListener)为了跨平台驱动我的解决方案,而不对每个表头 UI 的类名称做出任何假设。

我承认它可能比克利奥帕特拉的解决方案稍微弱一些。 我感谢大家的帮助,我真的很感激,我很高兴看到协作工作正常进行:)

At first, I used the very last Gnoupi's suggestion consisting in subclassing the TableColumnModel and overriding moveColumn but there were still some annoying jumps.

This is "my" fully working and tested solution with no nasty jump, it mainly relies on StanislavKo and kleopatra's suggestions. I added a more complicated mechanism to revert the unwanted move when releasing the mouse button :

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

Note that the latest valid move is accepted instead of completely reverting the column drag.

frozenColumnModelIndex is the index of the "frozen" column in the table model.

illegalTableColumnMoveFromIndex is the index of the column from where it was moved when the latest illegal move was detected.

illegalTableColumnMoveToIndex is the index of the column to where it was moved when the latest illegal move was detected.

The code inside mouseDragged is enough to prevent the frozen column from being dragged, the rest allows to prevent another column from being dragged to the frozen column.

It works as is under Microsoft Windows as I extend WindowsTableHeaderUI but rather use the reflection API to set the mouse input listener of the table header UI, call uninstallerListeners() and finally call header.addMouseListener(mouseInputListener) and header.addMouseMotionListener(mouseInputListener) in order to drive my solution cross-platform without making any assumption on the name of the class for each table header UI.

I admit it might be a bit less robust than kleopatra's solution. I thank you all for your help, I'm really grateful and I'm really happy to see that collaborative work just works :)

沉溺在你眼里的海 2024-08-03 13:33:52

我使用了“‘让我们尝试用我们实际拥有的东西来阻止正常行为’方法”的方法。 Gnoupi表示他没有解决问题的第二部分。 以下是仅适用于 Windows XP L&F 的解决方案:

  1. 将 XPStyle 类复制给您自己。
  2. 扩展WindowsTableHeaderUI。 查看源代码
  3. 使用它: getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());

感谢 Gnoupi 的努力。

I have used the "The 'Let's try to block the normal behavior with what we actually have' method" approach. Gnoupi said that he did not solve the second part of the problem. Here is the solution for just Windows XP L&F:

  1. copy XPStyle class to yourself.
  2. extend WindowsTableHeaderUI. Take a look at the source code.
  3. use it: getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());

Thanks to Gnoupi for the efforts.

南烟 2024-08-03 13:33:52

移动完成后我会把柱子放回去。 所以类似的事情。

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

I will just put the column back after the move is complete. So something like.

@Override
public void moveColumn(int from, int to) {
      super.moveColumn(from, to);
      if (from == 0 || to == 0) {
           super.moveColumn(to, from);
      }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文