Java 在JOptionPane.showOptionDialog()中设置组件焦点

Java 在JOptionPane.showOptionDialog()中设置组件焦点,java,swing,focus,joptionpane,Java,Swing,Focus,Joptionpane,为了在输入对话框中具有自定义按钮标题,我创建了以下代码: String key = null; JTextField txtKey = new JTextField(); int answerKey = JOptionPane.showOptionDialog(this, new Object[] {pleaseEnterTheKey, txtKey}, decryptionKey, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTI

为了在输入对话框中具有自定义按钮标题,我创建了以下代码:

String key = null;
JTextField txtKey = new JTextField();        
int answerKey = JOptionPane.showOptionDialog(this, new Object[] {pleaseEnterTheKey, txtKey}, decryptionKey, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] {okCaption, cancelCaption}, okCaption);        
if (answerKey == JOptionPane.OK_OPTION && txtKey.getText() != null) {
  key = txtKey.getText();
}
如何在显示对话框时将焦点(光标)移动到文本字段

更新

这对我不起作用,我的意思是文本字段没有焦点: OS:Fedora-侏儒

public class Test {
  public static void main(String[] args) {
    String key = null;
    JTextField txtKey = new JTextField();
    txtKey.addAncestorListener(new RequestFocusListener());
    int answerKey = JOptionPane.showOptionDialog(null, new Object[]{"Please enter the key:", txtKey}, "Title", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[]{"OKKK", "CANCELLLL"}, "OKKK");
    if (answerKey == JOptionPane.OK_OPTION && txtKey.getText() != null) {
      key = txtKey.getText();
    }
  }
}
试试这个

String key = null;
JTextField txtKey = new JTextField();
Object[] foo = {pleaseEnterTheKey, txtKey};      
int answerKey = JOptionPane.showOptionDialog(this, foo, decryptionKey, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] {okCaption, cancelCaption}, foo[1]);        
if (answerKey == JOptionPane.OK_OPTION && txtKey.getText() != null) {
  key = txtKey.getText();
}

将null作为最后一个参数传递是解决方案。至少它对我有用。

String key = null;
JTextField txtKey = new JTextField();        
int answerKey = JOptionPane.showOptionDialog(this, new Object[] {pleaseEnterTheKey, txtKey}, decryptionKey, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] {okCaption, cancelCaption}, null);        
if (answerKey == JOptionPane.OK_OPTION && txtKey.getText() != null) {
  key = txtKey.getText();
}
但即使是这种解决方案也带来了另一个问题:

聚焦组件和默认组件是不同的。Default component(默认组件)或Default button(默认按钮)是当您按下[code>ENTER键]/code>时,其onclick会触发的按钮。最后一个参数定义了默认组件,该组件也会获得焦点,传递null会带来没有默认组件的问题! 我用这种方式为我的代码解决了它,但我想这不是一种最佳实践:

String key = null;
    final JTextField txtKey = new JTextField();
    txtKey.addKeyListener(new KeyAdapter() {

      @Override
      public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (keyCode == 10) { //enter key
          Container parent = txtKey.getParent();              
          while (!(parent instanceof JOptionPane)) {
            parent = parent.getParent();
          }

          JOptionPane pane = (JOptionPane) parent;
          final JPanel pnlBottom = (JPanel) pane.getComponent(pane.getComponentCount() - 1);
          for (int i = 0; i < pnlBottom.getComponents().length; i++) {
            Component component = pnlBottom.getComponents()[i];
            if (component instanceof JButton) {
              final JButton okButton = ((JButton)component);
              if (okButton.getText().equalsIgnoreCase(okCaption)) {
                ActionListener[] actionListeners = okButton.getActionListeners();
                if (actionListeners.length > 0) {
                  actionListeners[0].actionPerformed(null);
                }
              }
            }
          }
        }
      }

    });
String key=null;
最终JTextField txtKey=新JTextField();
addKeyListener(新的KeyAdapter(){
@凌驾
按下公共无效键(按键事件e){
int keyCode=e.getKeyCode();
如果(keyCode==10){//输入key
容器父级=txtKey.getParent();
while(!(JOptionPane的父实例)){
parent=parent.getParent();
}
JOptionPane=(JOptionPane)父级;
最终JPanel pnlBottom=(JPanel)pane.getComponent(pane.getComponentCount()-1);
对于(int i=0;i0){
actionListeners[0]。actionPerformed(null);
}
}
}
}
}
}
});

展示了如何在模式对话框中轻松设置任何组件的焦点。

我发现RequestFocusListener()在Linux上不工作时也存在同样的问题,在讨论之后,我发现添加invokeLater暂时解决了这个问题

public class RequestFocusListener implements AncestorListener
{
public void ancestorAdded(final AncestorEvent e)
{
    final AncestorListener al= this;   
    SwingUtilities.invokeLater(new Runnable(){

        @Override
        public void run() {
            JComponent component = (JComponent)e.getComponent();
            component.requestFocusInWindow();
            component.removeAncestorListener( al );
        }
    });
}

public void ancestorMoved(AncestorEvent e) {}
public void ancestorRemoved(AncestorEvent e) {}
}
诀窍是(a)在文本组件上使用AncestorListener请求焦点,当焦点再次丢失时(默认按钮),在文本组件上使用FocusListener请求第二次焦点(但之后不要一直请求焦点):


更好的方法是:使用构造函数创建JOptionPane,覆盖selectInitialValue以设置焦点,然后使用createDialog构建对话框

// Replace by the constructor you want
JOptionPane pane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
  @Override
  public void selectInitialValue() {
    textArea.requestFocusInWindow();
  }
};

JDialog dialog = pane.createDialog(owner, title);
dialog.setVisible(true);

我找到了解决办法 非常原始,但有效

只需通过java.awt.Robot使用键“Tab”跳转到字段即可。
我已经创建了utils方法调用“按Tab(…)” 例如:

GuiUtils.pressTab(1);   <------------- // add this method before popup show

int result = JOptionPane.showConfirmDialog(this, inputs, "Text search window", JOptionPane.PLAIN_MESSAGE);
if (result == JOptionPane.OK_OPTION)
{
    
}
定义:

public static void pressTab(int amountOfClickes)
{
    SwingUtilities.invokeLater(new Runnable()
    {
        public void run()
        {
            try
            {
                Robot robot = new Robot();
                int i = amountOfClickes;
                while (i-- > 0)
                {
                    robot.keyPress(KeyEvent.VK_TAB);
                    robot.delay(100);
                    robot.keyRelease(KeyEvent.VK_TAB);
                }
            }
            catch (AWTException e)
            {
                System.out.println("Failed to use Robot, got exception: " + e.getMessage());
            }
        }
    });
}

如果组件位置是动态的,则可以不受限制地运行while循环,但可以在组件上添加一些焦点侦听器,以便在到达该组件后停止循环。

@ehsun7b,问题是什么?@mre,如何将光标移动到文本字段?@ehsun7b,请尝试
txtKey.requestFocusInWindow()-@mre:这在我的简单测试中似乎不起作用。我相信我已经看到了“一句话”的答案,但就我的一生而言,我想不起来了。请将你的建议作为我可以接受的答案发布。:)我想第一种解决方案适合我,因为我需要一个文本字段,让用户以文本形式输入他/她的输入。@ehsun7b,第二种解决方案也适合您。关于我包含的代码片段,只需将
txtKey
作为
options
的一项包含在内,然后在最后一个参数
options[indexOfTextField]
中指向它。我已经测试过了,它工作得很好。显示的组件从未执行过@ehsun7b,应该是这样的,否则您不会在显示屏上看到
txtKey
。无论如何,我建议你选择我给你的第二个选择。它已经被测试和验证。@ MRE:我已经尝试过第二个解决方案,它的问题是:它显示除了按钮之外的文本字段,而不是在对话框的中间。非常好的文章,但不幸的是,它对我没有帮助,因为默认按钮的requestFocus将在使用AncestorListener通过TextField获得焦点后出现(@ehsun7b,适合我在XP上使用JDK6_7。发布你的SSCCE()来演示你的问题。你发布的代码不是SSCCE,因为我们不知道你所有变量的值,它不编译,也没有main()方法。@ehsun7b,您发布的代码对我有效,因此可能是两个系统在事件处理方面存在差异。您是否添加了任何输出以确保事件侦听器被调用?如果您阅读了日志,有人建议也可以使用HierarchyListener。试试看会发生什么。或者另一个选项是从t包装代码他在SwingUttities.invokeLater()中创建了一个AncestorListener。这将把代码添加到EDT的末尾,以便在将焦点放在按钮上之后执行。然后看这里是的,这适用于该解决方案,您应该在dialog.setDefaultCloseOperation(WindowConstants.DISPOSE\u on\u CLOSE)之前调用dialog.setVisible(true)和dialog.dispose()
GuiUtils.pressTab(1);   <------------- // add this method before popup show

int result = JOptionPane.showConfirmDialog(this, inputs, "Text search window", JOptionPane.PLAIN_MESSAGE);
if (result == JOptionPane.OK_OPTION)
{
    
}
GUIUtils.pressTab(3);
public static void pressTab(int amountOfClickes)
{
    SwingUtilities.invokeLater(new Runnable()
    {
        public void run()
        {
            try
            {
                Robot robot = new Robot();
                int i = amountOfClickes;
                while (i-- > 0)
                {
                    robot.keyPress(KeyEvent.VK_TAB);
                    robot.delay(100);
                    robot.keyRelease(KeyEvent.VK_TAB);
                }
            }
            catch (AWTException e)
            {
                System.out.println("Failed to use Robot, got exception: " + e.getMessage());
            }
        }
    });
}