繁体   English   中英

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

[英]How to keep a single column from being reordered in a JTable?

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

table.getTableHeader().setReorderingAllowed(true);

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

我已经看到一些使用两个表的解决方案,其中第一列在单独的表中,但是也许有更好/更简单的方法。

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

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年后,仍然找不到最佳解决方案。

防止拖动第一列(以及第一列上的其他列)的另一个次佳方法是, 由uidelegate安装的mouseInputListener可以处理mouseEvent 之前对其进行拦截( 类似于最近的QA )。

合作者

  • 一个自定义的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切换后仍可使用的用法:

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

};

我认为您需要重写TableColumnModelListenercolumnMoved()方法。 TableColumnModelEvent类具有getFromIndex()方法,您应该可以查看该方法来确定它是否是您的固定列,然后可以取消该事件。

希望能有所帮助。 一种

首先,您需要定义一个更好,更简单的方法。 您不喜欢2桌方法吗?

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

在BasicTableHeaderUI中找到用于拖动列的代码。 因此,您可以尝试覆盖那里的代码,但是随后您需要对所有LAF都这样做。

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

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

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

我遇到了同样的问题,我正在寻找它。 到目前为止,我发现了两种方法。

  • “如果我自己重写”方法 :从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”:

    如果(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方法中更新鼠标的位置,您都将从跳出的列跳转到另一个位置。

因此,我仍在搜索,想知道是否有人对这部分有主张。

我使用了“让我们尝试用我们实际拥有的来阻止正常行为的方法”方法。 格努皮说,他没有解决问题的第二部分。 这是仅适用于Windows XP L&F的解决方案:

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

感谢Gnoupi的努力。

最初,我使用了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是表模型中“冻结”列的索引。

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

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

mouseDragged内部的代码足以防止拖动冻结的列,其余的代码则可以防止将另一列拖动到冻结的列。

当我扩展WindowsTableHeaderUI时,它的工作方式与Microsoft Windows相同,而是使用反射API来设置表标题UI的鼠标输入侦听器,调用uninstallerListeners(),最后调用中的header.addMouseListener(mouseInputListener)和header.addMouseMotionListener(mouseInputListener)为了跨平台驱动我的解决方案,而无需为每个表头UI的类名做任何假设。

我承认这可能不及kleopatra的解决方案强大。 我感谢大家的帮助,我非常感谢,也很高兴看到协作工作奏效了:)

移动完成后,我将把列放回去。 像这样。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM