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

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

 table.getTableHeader().setReorderingAllowed(true); 

现在可以重新排序列,包括我不想要的第一列。 有没有办法锁定第一列?

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

我认为您需要覆盖TableColumnModelListenercolumnMoved()方法。 TableColumnModelEvent类有一个getFromIndex()方法,您应该能够查看它是否是您的固定列,然后您应该能够取消该事件。

希望有所帮助。 一个

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

 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可以处理它们之前拦截mouseEvents( 类似于最近的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() - rx -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; } }; 

首先,您需要定义一种更好,更简单的方法。 你不喜欢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() 。 这确保了不应移动的列不会。
  • 但这还不够,我们需要防止在拖动另一个列时移动不动的列。 当获取“newColumnIndex”时,您还需要更改mouseDragged:

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

这将阻止用户拖动第一列。 但是如前所述,这只是问题的一部分,我们需要阻止另一个拖动列与第一个交换列。

到目前为止,我没有正确的方法。 我尝试通过moveColumn() 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方法中的鼠标位置,您将从拖动的列跳转到另一个地方。

所以我仍然在寻找,并想知道是否有人有关于这一部分的主张。

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

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

感谢Gnoupi的努力。

起初,我使用了最后一个Gnoupi的建议,包括inheritanceTableColumnModel并覆盖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,而是使用reflectionAPI来设置表头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); } }