Java 当从单元格编辑器组件显示对话框时,JTable会失去焦点
在我的主应用程序中,当单元格编辑器组件显示对话框时,JTable会失去焦点 下面是我为您制作的一个简单SSCCE,让您看到问题所在 做这些简单的实验:Java 当从单元格编辑器组件显示对话框时,JTable会失去焦点,java,swing,jtable,jdialog,tablecelleditor,Java,Swing,Jtable,Jdialog,Tablecelleditor,在我的主应用程序中,当单元格编辑器组件显示对话框时,JTable会失去焦点 下面是我为您制作的一个简单SSCCE,让您看到问题所在 做这些简单的实验: 在第一个表格列中按F2开始编辑。然后将“列内容”更改为数字2,然后按ENTER键。表格将失去焦点,窗体中的第一个字段将失去焦点 在第一个表格列中按F2开始编辑。然后将“列内容”更改为数字2,然后按TAB键。表格将失去焦点,窗体中的第一个字段将失去焦点 表单中的第一个字段也是SearchField组件。因为它不在JTable中,所以当您将其内容
- 在第一个表格列中按F2开始编辑。然后将“列内容”更改为数字2,然后按ENTER键。表格将失去焦点,窗体中的第一个字段将失去焦点
- 在第一个表格列中按F2开始编辑。然后将“列内容”更改为数字2,然后按TAB键。表格将失去焦点,窗体中的第一个字段将失去焦点
class SearchFieldCellEditor extends DefaultCellEditor
{
SearchFieldCellEditor(final SearchField searchField)
{
super(searchField);
searchField.removeActionListener(delegate);
delegate = new EditorDelegate()
{
@Override
public void setValue(Object value)
{
searchField.setValue(value);
}
@Override
public Object getCellEditorValue()
{
return searchField.getValue();
}
};
searchField.addActionListener(delegate);
}
@Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
SearchField searchField = (SearchField) getComponent();
searchField.setPreparingForEdit(true);
try
{
return super.getTableCellEditorComponent(
table, value, isSelected, row, column);
}
finally
{
searchField.setPreparingForEdit(false);
}
}
@Override
public boolean stopCellEditing()
{
SearchField searchField = (SearchField) getComponent();
try
{
searchField.commitEdit();
}
catch (ParseException ex)
{
ex.printStackTrace();
}
Component table = searchField.getParent();
table.requestFocusInWindow();
return super.stopCellEditing();
}
}
class SearchField extends JFormattedTextField implements PropertyChangeListener
{
private boolean _isPreparingForEdit;
private Object _oldValue;
SearchField()
{
setupFormatter();
addPropertyChangeListener("value", this);
}
void setPreparingForEdit(boolean isPreparingForEdit)
{
_isPreparingForEdit = isPreparingForEdit;
}
private void setupFormatter()
{
NumberFormat integerFormat = NumberFormat.getIntegerInstance();
integerFormat.setGroupingUsed(false);
NumberFormatter integerFormatter =
new NumberFormatter(integerFormat)
{
@Override
public Object stringToValue(String text) throws ParseException
{
return text.isEmpty() ? null : super.stringToValue(text);
}
};
integerFormatter.setValueClass(Integer.class);
integerFormatter.setMinimum(Integer.MIN_VALUE);
integerFormatter.setMaximum(Integer.MAX_VALUE);
setFormatterFactory(new DefaultFormatterFactory(integerFormatter));
}
@Override
public void propertyChange(PropertyChangeEvent evt)
{
final Object newValue = evt.getNewValue();
if (!Objects.equals(newValue, _oldValue))
{
_oldValue = newValue;
// Suppose that a value of 2 means that the data wasn't found.
// So we display a message to the user.
if (new Integer(2).equals(newValue) && !_isPreparingForEdit)
{
JOptionPane.showMessageDialog(null, "Not found: " + newValue + ".", "Warning",
JOptionPane.WARNING_MESSAGE);
}
}
}
}
你是否也发现了更多的缺陷?我想请你复习一下
马科斯
更新3
kleopatra建议后的另一种解决方案:
对于最后一个解决方案的意见和建议,我们仍将不胜感激。这是最终的最佳解决方案吗
Marcos使用stopCellEditing()方法进行编辑 在本例中,您必须输入一个由5个字符组成的字符串:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableEdit extends JFrame
{
TableEdit()
{
JTable table = new JTable(5,5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollpane = new JScrollPane(table);
add(scrollpane);
// Use a custom editor
TableCellEditor fce = new FiveCharacterEditor();
table.setDefaultEditor(Object.class, fce);
add(new JTextField(), BorderLayout.NORTH);
}
class FiveCharacterEditor extends DefaultCellEditor
{
FiveCharacterEditor()
{
super( new JTextField() );
}
public boolean stopCellEditing()
{
JTable table = (JTable)getComponent().getParent();
try
{
System.out.println(getCellEditorValue().getClass());
String editingValue = (String)getCellEditorValue();
if(editingValue.length() != 5)
{
JTextField textField = (JTextField)getComponent();
textField.setBorder(new LineBorder(Color.red));
textField.selectAll();
textField.requestFocusInWindow();
JOptionPane.showMessageDialog(
null,
"Please enter string with 5 letters.",
"Alert!",JOptionPane.ERROR_MESSAGE);
return false;
}
}
catch(ClassCastException exception)
{
return false;
}
return super.stopCellEditing();
}
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
Component c = super.getTableCellEditorComponent(
table, value, isSelected, row, column);
((JComponent)c).setBorder(new LineBorder(Color.black));
return c;
}
}
public static void main(String [] args)
{
JFrame frame = new TableEdit();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
正如我已经评论过的:在编辑器中更改表的状态有点可疑,特别是当它与焦点相关时,即使在最好的情况下焦点也是脆弱的。所以我会竭尽全力避免它 这种错误行为感觉类似于错误实现的InputVerifier,它的verify和shouldYieldFocus都有副作用(比如抓取焦点),这是正确的:在这种情况下,focusManager会感到困惑,它会“忘记”之前最后一个自然的focusOwner 补救办法可能是让经理先完成自己的工作,并且只在完成后才显示消息。在您的示例代码中,可以通过包装到invokeLater中来实现:
if (needsMessage()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(null, "Not found: " +
newValue + ".", "Warning",
JOptionPane.WARNING_MESSAGE);
}
});
}
在我的实际应用程序中,搜索字段只是在找不到实体时将值还原为null。因此,我并不是真的试图验证输入,因为不允许用户进入下一个组件(或表格单元格)。此外,对话框来自搜索组件,而不是表格单元格编辑器,这对我来说非常重要。这意味着我的应用程序中有很多代码需要更改。恐怕我不能用你的解决办法。我认为这可能是另一种不用在stopCellEditing方法中进行验证的方法(尽管很复杂)。我不知道解决方案(并且不得不承认我没有真正研究这个问题,现在已经太迟了:-),但不要在编辑器中更改表的状态(就像您请求将焦点放回它一样):这会带来比解决问题更多的问题。@kleopatra现在,改变表的状态似乎是唯一能让它工作的事情。我甚至可以将选择调用限制在显示对话框的情况下,从而最小化问题。我在
表.requestFocusInWindow
中看到,永久焦点所有者是null
,因此我建议焦点管理器下一步必须聚焦哪个组件,否则它将选择它喜欢的,在本例中是表单中的第一个字段。无论如何,这个问题似乎很棘手。如果您有其他解决方案,我将非常感谢您的回复。谢谢。:)信不信由你,在我把问题贴在这里之前,我已经找到了这个确切的解决方案。我只是没有使用它,因为在显示对话框之前,我觉得在表中看到选择有点奇怪。我也没有把它贴在这里,以为人们不会试图找到更好的。但这也许是唯一合理的解决办法。我只是希望这个解决方案不会出现任何死角,而且它总是有效的。不过,我会更新我的帖子并给你学分。再次感谢您对我的帮助。好吧,您的基本问题仍然是您让编辑组件做的比编辑器应该做的更多:-)在另一端,您允许将不太有效的值提交给模型。这有点极端。。。站在你的立场上,我会尝试将通知负担从编辑组件中移开,首先对该需求进行仔细观察。关于提交给模型的无效值,在我的案例中这不是问题。模型中仅暂时接受无效值。模型只有在有效时才会发布到数据库。因此,在用户编辑时,可以使用暂时无效的模型。我真正的编辑器组件也必须在表之外工作。如果消息是出于它,我会复制代码传播,我强迫我的应用程序的其他部分为此付出代价。我仍然认为消息是编辑器的工作。如果消息不在其中,我认为它不应该直接耦合到组件中,而是应该是一个可重用的小型coin类,可以注入任何协作者;-)但是,维护遗留应用程序从来都不是件有趣的事……我甚至可以将搜索组件配置为在JTable中使用时不显示消息,但请注意,我们仍然存在与stopCellEditing方法中显示的消息对话框相同的问题(我不考虑在此处使用SwingUtilities.invokeLater
)。唯一的解决方案是从stopCellEditing(@camickr solution)返回false
,但由于他在帖子中提到的原因,我无法使用此解决方案。幸运的是,我的应用程序不是遗留的:)。
class SearchFieldCellEditor extends DefaultCellEditor
{
SearchFieldCellEditor(final SearchField searchField)
{
super(searchField);
searchField.setShowMessageAsynchronously(true);
searchField.removeActionListener(delegate);
delegate = new EditorDelegate()
{
@Override
public void setValue(Object value)
{
searchField.setValue(value);
}
@Override
public Object getCellEditorValue()
{
return searchField.getValue();
}
};
searchField.addActionListener(delegate);
}
@Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
SearchField searchField = (SearchField) getComponent();
searchField.setPreparingForEdit(true);
try
{
return super.getTableCellEditorComponent(
table, value, isSelected, row, column);
}
finally
{
searchField.setPreparingForEdit(false);
}
}
@Override
public boolean stopCellEditing()
{
SearchField searchField = (SearchField) getComponent();
try
{
searchField.commitEdit();
}
catch (ParseException ex)
{
ex.printStackTrace();
}
return super.stopCellEditing();
}
}
class SearchField extends JFormattedTextField implements PropertyChangeListener
{
private boolean _showMessageAsynchronously;
private boolean _isPreparingForEdit;
private Object _oldValue;
SearchField()
{
setupFormatter();
addPropertyChangeListener("value", this);
}
public boolean isShowMessageAsynchronously()
{
return _showMessageAsynchronously;
}
public void setShowMessageAsynchronously(boolean showMessageAsynchronously)
{
_showMessageAsynchronously = showMessageAsynchronously;
}
void setPreparingForEdit(boolean isPreparingForEdit)
{
_isPreparingForEdit = isPreparingForEdit;
}
private void setupFormatter()
{
NumberFormat integerFormat = NumberFormat.getIntegerInstance();
integerFormat.setGroupingUsed(false);
NumberFormatter integerFormatter =
new NumberFormatter(integerFormat)
{
@Override
public Object stringToValue(String text) throws ParseException
{
return text.isEmpty() ? null : super.stringToValue(text);
}
};
integerFormatter.setValueClass(Integer.class);
integerFormatter.setMinimum(Integer.MIN_VALUE);
integerFormatter.setMaximum(Integer.MAX_VALUE);
setFormatterFactory(new DefaultFormatterFactory(integerFormatter));
}
@Override
public void propertyChange(PropertyChangeEvent evt)
{
final Object newValue = evt.getNewValue();
if (!Objects.equals(newValue, _oldValue))
{
_oldValue = newValue;
// Suppose that a value of 2 means that the data wasn't found.
// So we display a message to the user.
if (new Integer(2).equals(newValue) && !_isPreparingForEdit)
{
if (_showMessageAsynchronously)
{
SwingUtilities.invokeLater(
new Runnable()
{
@Override
public void run()
{
showMessage(newValue);
}
}
);
}
else
{
showMessage(newValue);
}
}
}
}
private void showMessage(Object value)
{
JOptionPane.showMessageDialog(null, "Not found: " + value + ".",
"Warning", JOptionPane.WARNING_MESSAGE);
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableEdit extends JFrame
{
TableEdit()
{
JTable table = new JTable(5,5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollpane = new JScrollPane(table);
add(scrollpane);
// Use a custom editor
TableCellEditor fce = new FiveCharacterEditor();
table.setDefaultEditor(Object.class, fce);
add(new JTextField(), BorderLayout.NORTH);
}
class FiveCharacterEditor extends DefaultCellEditor
{
FiveCharacterEditor()
{
super( new JTextField() );
}
public boolean stopCellEditing()
{
JTable table = (JTable)getComponent().getParent();
try
{
System.out.println(getCellEditorValue().getClass());
String editingValue = (String)getCellEditorValue();
if(editingValue.length() != 5)
{
JTextField textField = (JTextField)getComponent();
textField.setBorder(new LineBorder(Color.red));
textField.selectAll();
textField.requestFocusInWindow();
JOptionPane.showMessageDialog(
null,
"Please enter string with 5 letters.",
"Alert!",JOptionPane.ERROR_MESSAGE);
return false;
}
}
catch(ClassCastException exception)
{
return false;
}
return super.stopCellEditing();
}
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
Component c = super.getTableCellEditorComponent(
table, value, isSelected, row, column);
((JComponent)c).setBorder(new LineBorder(Color.black));
return c;
}
}
public static void main(String [] args)
{
JFrame frame = new TableEdit();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
if (needsMessage()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(null, "Not found: " +
newValue + ".", "Warning",
JOptionPane.WARNING_MESSAGE);
}
});
}