Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何避免在JTable中对单个列重新排序?_Java_Swing_Jtable - Fatal编程技术网

Java 如何避免在JTable中对单个列重新排序?

Java 如何避免在JTable中对单个列重新排序?,java,swing,jtable,Java,Swing,Jtable,我有一个JTable,我需要能够对列重新排序。但是,我希望第一列不能重新排序。我使用以下选项来启用重新排序: table.getTableHeader().setReorderingAllowed(true); 这些列现在可以重新排序,包括我不想要的第一列。有没有办法锁定第一列 我见过一些解决方案使用两个表,第一列在一个单独的表中,但可能有更好/更简单的方法。我认为需要重写TableColumnModelListener中的columnMoved方法。TableColumnModelEvent

我有一个JTable,我需要能够对列重新排序。但是,我希望第一列不能重新排序。我使用以下选项来启用重新排序:

table.getTableHeader().setReorderingAllowed(true);
这些列现在可以重新排序,包括我不想要的第一列。有没有办法锁定第一列


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

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


希望有帮助。A

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

不能使用TableColumnModelListener,因为事件是在列已被移动后激发的

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

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

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


这是我用两个表实现的版本。更好吗?我不知道,但它很简单,因为它只有一行代码可以使用。

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

if我自己重写它的方法:从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中有两个要修改的位置:

在鼠标按下的情况下,调用canMove而不是header.getReorderingAllowed。这样可以确保不应移动的列不会移动。 但这还不够,我们需要防止在拖动另一根柱子时移动不动的柱子。在获取newColumnIndex时,还需要更改鼠标标记:

如果0 如果可以移动此新索引,则需要添加条件,例如使用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);
}
@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);
}
这将防止用户拖动第一列。但正如前面所描述的,这只是问题的一部分,我们需要防止另一个拖动的列与第一个列交换

到目前为止,我还没有一个正确的方法。我尝试将TableColumnModel子类化,并重写moveColumn方法:

@Override
public void setDraggedColumn(TableColumn pAColumn)
{
    int lIndex  = -1;
    if (pAColumn != null)
        lIndex = getColumnModel().getColumnIndex(pAColumn.getIdentifier());
    if (lIndex != 0)
        super.setDraggedColumn(pAColumn);
}
@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方法中的鼠标位置,您将从拖动的列跳转到另一个位置


所以我还在搜索,想知道是否有人对这一部分有意见。

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

将XPStyle类复制到您自己。 扩展WindowsTableHeaderUI。请看下面的图片。 使用它:getTableHeader.setUInew TreeTableWindowsTableHeaderUI;
感谢Gnoupi的努力。

这是我用来防止第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; 
    } 
}); 

干杯,

将近4年后,仍然没有选择 imal解决方案随处可见

另一种防止在第一列上拖动第一列和其他列的次优方法是在uidelegate安装的mouseInputListener能够处理mouseEvents之前拦截mouseEvents

合作者

自定义MouseMotionListener,它将所有事件委托给最初安装的,但拖动的事件除外,前提是它将导致第一列之上的另一列 将原始文件替换为自定义文件 每当LAF更改时更新替换,因为原始LAF由ui控制。这需要对JTableHeader进行子类化,并在updateUI中进行连接 自定义鼠标输入侦听器:

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

}

起初,我使用了最后一个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中的代码足以防止冻结列被拖动,其余代码允许防止另一列被拖动到冻结列

当我扩展WindowsTableHeaderUI时,它与Microsoft Windows下的一样工作,而是使用反射API设置表头UI的鼠标输入侦听器,调用uninstallerListeners,最后调用header.addMouseListenermouseInputListener和header.addMouseMotionListenermouseInputListener,以便跨平台驱动我的解决方案,而无需对每个表头UI的类的名称做任何假设


我承认它可能比克利奥帕特拉的解决方案要差一点。感谢大家的帮助,我真的很感激,我也很高兴看到协作工作能够奏效:

我会在移动完成后将专栏放回原处。差不多吧

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

我通过你的问题得到了70%的答案,并开始写一个回复,告诉你使用两个表格…然后我读完了你的文章。你是说第一列应该独立于其他列?他说的是移动整个列,左右。哦,我在想排序。这更有意义。是的,将列向左或向右移动,而不是对列中的数据进行排序这看起来是最好的方法。我一直在玩弄它,我的问题是,因为我是Java新手,如何取消事件?您可以选择是否将方法调用传递给超类。否则,它将不会应用于表。您必须对TableColumnModel而不是TableColumnModelListener的实现进行此更改,覆盖moveColumnint columnIndex、int newIndex。您无法取消该事件,即使MouseeEvent.consume无效。TableColumnModelListener没有超类,因为它是一个接口。正如@gouessej所说,TableColumnModelListener是一个接口——没有super.columnMoved可调用或不可调用。答案是错误的。我猜,他可能不喜欢2tables方法,因为它打破了数据模型。这将迫使最终在两个模型中分离数据,在JTable和图形方面确实很简单,但在模型方面更烦人。这并不强迫您使用两个模型。只有OP知道他的要求是什么。我们无法理解他的想法。你不需要两个模型,你可以很容易地从同一个表模型中使用两个表的方法。无论是从概念角度还是从维护角度来看,这都是最简单的方法。干得好。请让我知道您对我的建议的看法,再次感谢您的详细解释。