我有一个JTable功能请求,允许更像Excel中的数据输入。如果用户键入单元格,则先前的值将替换为新值,并且可以使用箭头键在行和列之间导航。
我找到了几个解决这个问题的方法并实现了一个。
然后给了我一个额外的要求 - 如果用户点击表外,则更新了输入数据的单元格(编辑停止)。
我怀疑我只是遗漏了一些东西,但是我试图实现这两个要求都失败了。
我有一个自定义单元格编辑器,用于侦听focusLost和停止单元格编辑。如果我在表格中双击然后在表格外单击,则此方法有效。但是,如果我只是在单元格中键入数字,则此方法不会运行。
tableMe.setSurrendersFocusOnKeystroke(真);这个a)导致我失去了第一次按键,并且b)把我放在编辑器中,所以我可以使用右箭头键和左箭头键来移出单元格。
我在桌面上添加了一个FocusListener。然后,我可以在单元格中键入一个数字并导航到另一个单元格,但我永远无法进入编辑器。双击会立即停止编辑。我想我可以在“真正”编辑(在编辑器窗口中有光标)和在单元格中输入数字之间找到一些区别。我到目前为止所尝试的(isEditing()总是出现,hasFocus()总是出现错误)不起作用。
有人能为我指出解决方案吗?我的尝试如下。我评论了错误的开始,但他们在那里,所以你可以看到我尝试的东西。
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import java.text.NumberFormat;
import javax.swing.text.JTextComponent;
import javax.swing.border.*;
import javax.swing.BorderFactory;
import javax.swing.border.Border;
class ExcelTableTest extends JFrame
{
public static final Color activeDark = new Color(230, 185, 184);
public static final Color activeLight = new Color(244, 233, 233);
private MyJTable tableMe;
private MyTableModel modelMe;
private JTextField textMe;
private JScrollPane scrollTable;
private String[] tableHeaders = { "Column 1", "Column 2" };
private Object[][] tableData = new Object[ tableHeaders.length ][ 4 ];
ExcelTableTest()
{
setLayout( new BorderLayout() );
modelMe = new MyTableModel(tableData, tableHeaders );
tableMe = new MyJTable(modelMe);
tableMe.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
TableColumn column = null;
for (int i = 0; i < tableHeaders.length; i++)
{
column = tableMe.getColumnModel().getColumn(i);
column.setCellRenderer( new MyCellRenderer() );
column.setCellEditor( new NumberCellEditor() );
}
tableMe.setRowSelectionAllowed(false);
// tableMe.addFocusListener( new MyFocusListener() );
// tableMe.setSurrendersFocusOnKeystroke(true);
// modelMe.addTableModelListener(new MyTableModelListener());
scrollTable = new JScrollPane(tableMe);
scrollTable.setVerticalScrollBarPolicy(
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollTable.setHorizontalScrollBarPolicy(
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollTable.setPreferredSize(new Dimension(500,150));
textMe = new JTextField( 6 );
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500,210);
add(BorderLayout.CENTER, scrollTable );
add(BorderLayout.SOUTH, textMe );
setVisible(true);
}
class MyJTable extends JTable
{
MyJTable( TableModel model)
{
super(model);
}
//
// Trying to get cells selected when editing starts
@Override
public Component prepareEditor(TableCellEditor editor,
int row, int column)
{
Component editComponent = super.prepareEditor(editor, row, column);
if (editComponent instanceof JTextComponent)
{
System.out.println( "In prepare editor" );
((JTextComponent) editComponent).selectAll();
// System.out.println( "Requesting Focus" );
// System.out.println(
// ((JTextComponent) editComponent).isFocusable());
// System.out.println(
// ((JTextComponent) editComponent).requestFocusInWindow());
}
return editComponent;
}
//
// This also works, but end result is the same- when using this, the editor
// does not have focus, so I cannot update the cell just by switching focus
// to another part of the GUI.
// @Override
// public boolean editCellAt( int row, int col, EventObject e )
// {
// boolean result = super.editCellAt( row, col, e );
//
// Component editComponent = getEditorComponent();
// if ( editComponent == null ||
// !(editComponent instanceof JTextComponent) )
// return result;
// if (e instanceof KeyEvent)
// ((JTextComponent) editComponent).selectAll();
//
// return result;
// }
//
}
class MyFocusListener implements FocusListener
{
@Override
public void focusGained(FocusEvent e)
{
System.out.println("Table has focus");
}
@Override
public void focusLost(FocusEvent e)
{
System.out.println("Table lost focus");
MyJTable table = (MyJTable) e.getSource();
int[] columns = table.getSelectedColumns();
//
// I will not be allowing multiple column selection
//
// This is still a problem, as the table IS editing, even though the editor
// does not seem to have focus. This is true whether I am in the case where
// I really have the editor going (in which case the lostFocus of the editor
// nicely cleans things up) or the editor is not really going, and then the
// lostFocus of the editor never triggers.
// if ( columns.length > 0 && ! table.isEditing() )
if ( columns.length > 0 )
{
TableColumn tableColumn = table.getColumnModel()
.getColumn(columns[0]);
DefaultCellEditor cellEditor = (DefaultCellEditor)
tableColumn.getCellEditor();
// Does not have focus even when I double click to edit the cell
if (cellEditor != null && ! cellEditor.getComponent().hasFocus() );
{
System.out.println( "Editor focus: " +
cellEditor.getComponent().hasFocus() );
if (!cellEditor.stopCellEditing())
{
cellEditor.cancelCellEditing();
}
}
}
}
}
class MyTableModel extends DefaultTableModel
{
public MyTableModel(Object rowData[][], Object columnNames[]) {
super(rowData, columnNames);
}
@Override
public Class getColumnClass(int col) {
return Double.class;
}
@Override
public boolean isCellEditable(int row, int col)
{
return true;
}
}
class MyCellRenderer extends DefaultTableCellRenderer
{
public Component getTableCellRendererComponent(
JTable table, Object value, boolean selected, boolean focus,
int row, int col) {
Component renderComponent = super.getTableCellRendererComponent(
table, value, selected, focus, row, col);
//
// This is called constantly
// System.out.println( "In renderer" );
if ((row % 2) == 0) {
renderComponent.setBackground(activeDark);
} else {
renderComponent.setBackground(activeLight);
}
NumberFormat nf = NumberFormat.getInstance();
nf.setMinimumFractionDigits(0);
if (value != null)
System.out.println( "Value: " + value + "; " + value.getClass() );
super.setText((value == null) ? "" : nf.format(value));
super.setHorizontalAlignment( SwingConstants.RIGHT );
return renderComponent;
}
}
class NumberCellEditor extends DefaultCellEditor
{
private Double minimum = 0.0;
private Double value = null;
private final JTextField textField;
private final NumberFormat nf = NumberFormat.getInstance();
public NumberCellEditor()
{
super(new JTextField());
textField = (JTextField) getComponent();
nf.setMinimumFractionDigits(0);
textField.setHorizontalAlignment(JTextField.RIGHT);
textField.addFocusListener(new FocusListener()
{
@Override
public void focusGained(FocusEvent e)
{
System.out.println( "In editor" );
if (value != null)
textField.setText(nf.format(value.doubleValue()));
else
textField.setText("");
textField.setCaretPosition(0);
}
@Override
public void focusLost(FocusEvent e)
{
System.out.println( "Lost focus editing cell" );
if (!stopCellEditing()) cancelCellEditing();
}
});
}
public boolean stopCellEditing()
{
String s = (String)super.getCellEditorValue();
System.out.println( "Stop Cell editing: " + value );
if ("".equals(s))
{
value = null;
super.stopCellEditing();
}
try
{
value = nf.parse(s).doubleValue();
}
catch (Exception e)
{
textField.setBorder(new LineBorder(Color.red));
return false;
}
return super.stopCellEditing();
}
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected,
int row, int col)
{
this.value = (Double) value;
textField.setBorder(new LineBorder(Color.black));
return super.getTableCellEditorComponent(
table, value, isSelected, row, col);
}
public Object getCellEditorValue()
{
value = (Double) value;
return value;
}
}
public static void main( String[] args )
{
ExcelTableTest excelTest = new ExcelTableTest();
}
}
这是下面描述的简单解决方案。它只需要一行 -
tableMe.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
如果我开始输入一个单元格,然后在单元格中单击鼠标,我会进入编辑器并丢失正在键入的内容。如果我点击其他任何地方,保存的内容将被保存,编辑器将被停止。
我曾尝试过定义一个标志 -
private boolean reallyInEditor = true;
然后覆盖editCellAt而不是准备编辑器
@Override
public boolean editCellAt( int row, int col, EventObject e )
{
boolean result = super.editCellAt( row, col, e );
Component editComponent = getEditorComponent();
if ( editComponent == null ||
!(editComponent instanceof JTextComponent) )
return result;
if (e instanceof KeyEvent)
{
((JTextComponent) editComponent).selectAll();
reallyInEditor = false;
}
return result;
}
}
设置我的标志以通过键入而不是双击
来指示用户进入编辑器并将FocusListener放在表格
上 tableMe.addFocusListener( new MyFocusListener() );
class MyFocusListener implements FocusListener
{
@Override
public void focusGained(FocusEvent e)
{
System.out.println("Table has focus");
}
@Override
public void focusLost(FocusEvent e)
{
System.out.println("Table lost focus");
MyJTable table = (MyJTable) e.getSource();
int[] columns = table.getSelectedColumns();
//
// I will not be allowing multiple column selection
if ( columns.length > 0 )
{
TableColumn tableColumn = table.getColumnModel()
.getColumn(columns[0]);
DefaultCellEditor cellEditor = (DefaultCellEditor)
tableColumn.getCellEditor();
if (cellEditor != null && !reallyInEditor)
{
System.out.println( "Editor focus: " +
cellEditor.getComponent().hasFocus() );
if (!cellEditor.stopCellEditing())
{
cellEditor.cancelCellEditing();
}
reallyInEditor = true;
}
}
}
}
与所有这些额外工作的区别在于,如果我开始在单元格中输入,然后单击单元格,单元格将使用我输入的值进行更新,编辑器将停止。
答案 0 :(得分:4)
使用以下代码行来实现您的目标:
tableMe.putClientProperty("terminateEditOnFocusLost",Boolean.TRUE);