我有一个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 年过去了,在任何地方都看不到最佳解决方案。
另一种防止拖动第一列(以及第一列上的其他列)的次优方法是在 uielegate 安装的 mouseInputListener 可以处理它们之前拦截 mouseEvents(类似于最近的 QA)。
合作者
自定义 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;
}
};
我认为您需要覆盖columnMoved()
. TableColumnModelListener
该类TableColumnModelEvent
有一个getFromIndex()
方法,您应该能够查看它以确定它是否是您的固定列,然后您应该能够取消该事件。
希望有帮助。一个
首先你需要定义一个更好更简单的方法。您不喜欢 2 表方法的哪些方面?
您不能使用 TableColumnModelListener,因为该事件是在列已被移动“之后”触发的。
在 BasicTableHeaderUI 中可以找到用于拖动列的代码。因此,您可以尝试覆盖那里的代码,但是您需要为所有 LAF 执行此操作。
上面的代码在 mousePressed 事件上调用 JTableHeader.getReorderingAllowed() 以确定是否允许列重新排序。我想您可以在 JTableHeader 中覆盖该方法,并可能使用 MouseInfo 类来获取当前鼠标位置以确定它是否在第一列上,然后返回 false。但是现在您还需要创建一个使用自定义表头的自定义 JTable。
当然,使用上述任何一个建议,您都可以防止移动第一列。但不要忘记,您还需要防止在第一列之前插入第二列。我不相信这个问题有一个简短的答案。
固定列表是我如何用两个表实现的版本。这个会比较好吗?我不知道,但它很简单,因为它只需一行代码即可使用它。
我有同样的问题,我正在寻找它。到目前为止,我找到了两种方法。
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,这并不理想。但这是最适合的,因为它在它应该处理的地方处理问题。
首先,要阻止拖动第一列,我们需要一个来自 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
方法中的鼠标位置,您将从拖动的列跳转到另一个位置。
所以我还在寻找,想知道是否有人对这部分有建议。
起初,我使用了 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 是检测到最近一次非法移动时从其移动的列的索引。
非法表列移动到索引是检测到最近一次非法移动时被移动到的列的索引。
mouseDragged 中的代码足以防止冻结列被拖动,其余代码允许防止另一列被拖动到冻结列。
它在 Microsoft Windows 下正常工作,因为我扩展了 WindowsTableHeaderUI 而是使用反射 API 设置表头 UI 的鼠标输入侦听器,调用 uninstallerListeners() 并最后调用 header.addMouseListener(mouseInputListener) 和 header.addMouseMotionListener(mouseInputListener)为了在不对每个表头 UI 的类名做任何假设的情况下跨平台驱动我的解决方案。
我承认它可能没有 kleopatra 的解决方案那么强大。我感谢大家的帮助,我真的很感激,我很高兴看到协作工作很有效:)
我使用了“'让我们尝试用我们实际拥有的'方法阻止正常行为”的方法。Gnoupi说他没有解决问题的第二部分。这是仅适用于 Windows XP L&F 的解决方案:
WindowsTableHeaderUI
。看看源代码。getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());
感谢Gnoupi的努力。
移动完成后,我将把柱子放回去。所以类似的东西。
@Override
public void moveColumn(int from, int to) {
super.moveColumn(from, to);
if (from == 0 || to == 0) {
super.moveColumn(to, from);
}
}