我有一个错误潜伏在弹出包含文本字段的JOptionPane
后,焦点偶尔不会出现在我想要的文本字段上。
我最终将其归结为一个合理的例子:
import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Component;
public class FocusIssueTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// The real thing has many more components in there,
// but I removed them for the demo.
MyInputPane myInputPane = new MyInputPane();
myInputPane.showDialog(null);
}
});
}
public static class MyInputPane extends JPanel {
private final JTextField textField;
protected MyInputPane() {
textField = new JTextField();
textField.selectAll();
textField.setColumns(30);
setLayout(new BorderLayout());
add(textField, BorderLayout.CENTER);
}
public boolean showDialog(Component parentComponent) {
final JOptionPane optionPane = new JOptionPane(
this, JOptionPane.PLAIN_MESSAGE,
JOptionPane.OK_CANCEL_OPTION);
JDialog dialog = optionPane.createDialog(
parentComponent, "Select a Thing");
/* Attempted solution #1 - wait until the window is active
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowActivated(WindowEvent event) {
textField.requestFocusInWindow();
}
});
*/
/* Attempted solution #2 - camickr's RequestFocusListener
textField.addAncestorListener(new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent event) {
JComponent component = event.getComponent();
component.requestFocusInWindow();
component.removeAncestorListener(this);
}
@Override
public void ancestorRemoved(AncestorEvent event) {
}
@Override
public void ancestorMoved(AncestorEvent event) {
}
});
*/
/* Attempted solution #3 - HierarchyListener
textField.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent event) {
Component component = event.getComponent();
if ((HierarchyEvent.SHOWING_CHANGED &
event.getChangeFlags()) != 0 &&
component.isShowing()) {
component.requestFocusInWindow();
component.removeHierarchyListener(this);
}
}
});
*/
// Attempted solution #4 - appears to work but can't be
// right, because eww.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
textField.requestFocusInWindow();
}
});
}
});
}
});
dialog.setVisible(true);
Object selectedValue = optionPane.getValue();
return selectedValue instanceof Integer &&
(int) selectedValue == 0;
}
}
}
尝试的解决方案 #1 到 #3 都无法将焦点放在文本字段中。尝试的解决方案 #4 有效,但必须使用三级嵌套SwingUtilities.invokeLater
调用不可能是执行此操作的正确方法。
那么正确的方法是什么呢?
我注意到JOptionPane.showInputDialog
'文本字段确实会获得焦点,所以显然有一种方法可以做到这一点。
规范的解决方案,但您可以尝试一次调用来在窗口侦听器中的事件线程上排队。例如,
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowActivated(WindowEvent event) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textField.requestFocusInWindow();
}
});
}
});
其他可能的"笨蛋"包括使用短的单运行摆动计时器。
您是否考虑过直接子类化/初始化JDialog而不是使用JOptionPane?
java之所以没有"setFocusInWindow",是因为在某些平台上,不可能直接"设置"焦点(就像请求它一样)。
对我来说,似乎对"setVisible()"的调用是在EDT上放置一个事件,以使窗口可见,这反过来又将焦点从文本字段转移开。