Java Swing JTable中的多单元选择
我想为JTable提供多单元格编辑功能:双击仍将编辑所选单元格中的值(标准行为),而右键单击将打开一个弹出菜单,其中有一个条目“编辑所选单元格” 当用户点击此菜单项时,选定范围内的最后一个单元格将变为可编辑。其他选定单元格保持选定状态。然后写入新值,编辑完成后(通常通过按Enter键),所有选定的单元格都会获得该值 为了简单起见,让我们假设所有单元格都包含相同的值类型,例如整数 下面是显示弹出对话框的代码,以便开始:Java Swing JTable中的多单元选择,java,swing,jtable,Java,Swing,Jtable,我想为JTable提供多单元格编辑功能:双击仍将编辑所选单元格中的值(标准行为),而右键单击将打开一个弹出菜单,其中有一个条目“编辑所选单元格” 当用户点击此菜单项时,选定范围内的最后一个单元格将变为可编辑。其他选定单元格保持选定状态。然后写入新值,编辑完成后(通常通过按Enter键),所有选定的单元格都会获得该值 为了简单起见,让我们假设所有单元格都包含相同的值类型,例如整数 下面是显示弹出对话框的代码,以便开始: table.setSelectionMode(ListSelectionMod
table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
table.setCellSelectionEnabled(true);
table.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
doPop(e);
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
doPop(e);
}
}
private void doPop(MouseEvent e) {
MultiEditPopUp menu = new MultiEditPopUp(tblRanges);
menu.show(e.getComponent(), e.getX(), e.getY());
}
});
class MultiEditPopUp extends JPopupMenu {
JMenuItem menuItem;
MultiEditPopUp(JTable table) {
menuItem = new JMenuItem("Edit selected");
menuItem.setAction(new BulkEditAction(table));
add(menuItem);
}
}
class BulkEditAction extends AbstractAction {
private final JTable table;
public BulkEditAction(JTable table) {
this.table = table;
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
// TODO: let the user edit the last cell, and then apply to the others
}
}
我怎么能做这样的事呢?还是不太清楚问题出在哪里。基本方法是
- 存储选定的单元格
- 让用户编辑其中一个
- 最后,获取编辑后的值并将其设置为以前存储的所有单元格
public class BulkEditAction extends AbstractAction {
JTable table;
List selectedCells;
public BulkEditAction(JTable table) {
this.table = table;
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
// store, here rows only, refine for cell selection
selectedCells = Arrays.asList(table.getSelectedRows());
final int rowToEdit = // ...
final int columnToEdit = // ...
table.editCellAt(rowToEdit, columnToEdit);
CellEditorListener l = new CellEditorListener() {
@Override
public void editingStopped(ChangeEvent e) {
((AbstractCellEditor) e.getSource()).removeCellEditorListener(this);
propagateEditedValue(rowToEdit, columnToEdit);
}
@Override
public void editingCanceled(ChangeEvent e) {
((AbstractCellEditor) e.getSource()).removeCellEditorListener(this);
}
};
table.getCellEditor().addCellEditorListener(l);
}
private void propagateEditedValue(final int row, final int column) {
// need to invoke to be sure that the table has updated itself after
// editingStopped
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// foreach selectedCell (with coordinates selectedRow/-column
table.setValueAt(table.getValueAt(row, column), selectedRow, selectedColumn);
}
});
}
}
我将扩展JTable以创建MultiCellEditJTable
public class MultiCellEditJTable extends JTable{
public MultiCellEditJTable(){
setColumnSelectionAllowed(true);
getSelectionModel().setSelectionMode(DefaultListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
}
@Override
public Component prepareEditor(TableCellEditor editor, int row, int column){
Component component = super.prepareEditor(editor, row, column);
if(component instanceof JTextField){
JTextField textField = (JTextField)component;
textField.selectAll();
}
return component;
}
@Override
public void editingStopped(ChangeEvent e){
int editingRow = getEditingRow();
int editingColumn = getEditingColumn();
super.editingStopped(e);
if(1 < getSelectedRowCount() && 1 == getSelectedColumnCount() && editingColumn == getSelectedColumn()){
Object value = getValueAt(editingRow, editingColumn);
Arrays.stream(getSelectedRows()).filter(row->row != editingRow).forEach(row->
setValueAt(value, row, editingColumn)
);
}
}
}
公共类MultiCellEditJTable扩展了JTable{
公共多小区表(){
setColumnSelectionAllowed(真);
getSelectionModel().setSelectionMode(默认列表SelectionModel.MULTIPLE\u INTERVAL\u SELECTION);
}
@凌驾
公共组件prepareEditor(TableCellEditor编辑器,int行,int列){
组件=super.prepareEditor(编辑器、行、列);
if(JTextField的组件实例){
JTextField textField=(JTextField)组件;
textField.selectAll();
}
返回组件;
}
@凌驾
公共作废编辑停止(变更事件e){
int editingRow=getEditingRow();
int editingColumn=getEditingColumn();
超级编辑(e);
如果(1行!=editingRow).forEach(行->
setValueAt(值、行、编辑列)
);
}
}
}
当选定一列的多行且编辑完成时,所有选定的单元格都将设置为编辑产生的值
作为一个额外的好处,我让编辑器selectAll提供了一个功能,即在选择一系列单元格后,可以键入所需的值。否则,必须先将当前值退格。到底是什么问题?当收到编辑后的值时,将其传播到所有选定的单元格并感到高兴:-)两条注释(与我没有得到的问题无关;-)a)不将任何JSomething子类化,而是使用它们(jpopmpmenu设计用于添加动作/项,不需要仅为添加特定项而子类化)b)始终使用最高抽象,这里可用这意味着setComponentPopupMenu代替了mouseListener(它提供了不完整的功能,因为它不包括键盘上的弹出窗口),问题是现在如何让用户在保留选择的同时编辑该单元格。谢谢你关于子类化的建议。也许这是一个很好的解决方案,它很有魅力。我试图解决一个用户体验问题:当您编辑该单元格时,“全选”不起作用,更糟糕的是,如果您点击一个箭头,光标将移动,并将执行批量编辑。如果我能做到的话,我会发帖补充这个答案。有什么想法吗?