【问题标题】:Setting component focus in JOptionPane.showOptionDialog()在 JOptionPane.showOptionDialog() 中设置组件焦点
【发布时间】:2011-09-09 05:58:19
【问题描述】:

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

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();
}

如何在显示对话框时将焦点(光标)移动到文本字段?

更新

这对我不起作用,我的意思是文本字段没有焦点: 操作系统:Fedora - Gnome

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();
    }
  }
}

【问题讨论】:

  • @ehsun7b,请问有什么问题?
  • @mre,如何将光标移动到文本字段?
  • @ehsun7b,试试txtKey.requestFocusInWindow(); - download.oracle.com/javase/6/docs/api/java/awt/…
  • @mre:这在我的简单测试中似乎不起作用。我确定我已经看到了对此的“单行”答案,但我这辈子都不记得了。
  • 请将您的建议发布为我可以接受的答案。 :)

标签: java swing focus joptionpane


【解决方案1】:

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

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

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)
{
    
}

如果您应该多次按“Tab”来获取您的组件,您可以使用以下方法:

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());
            }
        }
    });
}

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

【讨论】:

    【解决方案2】:

    更好的方法:使用构造函数创建 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);
    

    【讨论】:

      【解决方案3】:
          public static String getPassword(String title) {
              JPanel panel = new JPanel();
              final JPasswordField passwordField = new JPasswordField(10);
              panel.add(new JLabel("Password"));
              panel.add(passwordField);
              JOptionPane pane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
                  @Override
                  public void selectInitialValue() {
                      passwordField.requestFocusInWindow();
                  }
              };
              pane.createDialog(null, title).setVisible(true);
              return passwordField.getPassword().length == 0 ? null : new String(passwordField.getPassword());
          }
      

      【讨论】:

      • 你应该在 dialog.setVisible(true) 之前调用 dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE),之后调用 dialog.dispose()
      【解决方案4】:

      诀窍是 (a) 在文本组件上使用 AncestorListener 来请求焦点,当焦点再次丢失时(给定默认按钮),在文本组件上使用 FocusListener 再次请求焦点(但之后不要一直要求焦点):

      final JPasswordField accessPassword = new JPasswordField();
      
      accessPassword.addAncestorListener( new AncestorListener()
      {
        @Override
        public void ancestorRemoved( final AncestorEvent event )
        {
        }
        @Override
        public void ancestorMoved( final AncestorEvent event )
        {
        }
        @Override
        public void ancestorAdded( final AncestorEvent event )
        {
          // Ask for focus (we'll lose it again)
          accessPassword.requestFocusInWindow();
        }
      } );
      
      accessPassword.addFocusListener( new FocusListener()
      {
        @Override
        public void focusGained( final FocusEvent e )
        {
        }
        @Override
        public void focusLost( final FocusEvent e )
        {
          if( isFirstTime )
          {
            // When we lose focus, ask for it back but only once
            accessPassword.requestFocusInWindow();
            isFirstTime = false;
          }
        }
        private boolean isFirstTime = true;
      } );
      

      【讨论】:

        【解决方案5】:

        我遇到了同样的问题,RequestFocusListener() 在 Linux 上不起作用,在关注 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5018574 的讨论后,我发现添加一个 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) {}
        }
        

        【讨论】:

        【解决方案6】:

        Dialog Focus 展示了如何轻松地将焦点设置在模态对话框中的任何组件上。

        【讨论】:

        • 非常好的文章,但不幸的是它对我没有帮助,因为默认按钮的 requestFocus 将在 TextField 使用 AncestorListener 获得焦点后发生。 :(
        • @ehsun7b,我在 XP 上使用 JDK6_7 效果很好。发布您的 SSCCE (sscce.org) 来证明您的问题。您发布的代码不是 SSCCE,因为我们不知道您所有变量的值是什么,并且它无法编译并且没有 main() 方法。
        • @ehsun7b,您发布的代码对我有用,所以它们可能是两个系统之间事件处理的差异。您是否添加了任何输出以确保调用事件侦听器?如果您阅读该博客,有人建议也可以使用 HierarchyListener。试试看会发生什么。或者另一种选择是将来自 AncestorListener 的代码包装在 SwingUtltities.invokeLater() 中。这会将代码添加到 EDT 的末尾,以便在焦点放在按钮上之后执行。
        【解决方案7】:

        将 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();
        }
        

        但即使是这种解决方案也会带来另一个问题:

        Focused 组件和Default 组件是不同的。默认组件或默认按钮是点击ENTER KEY时触发的按钮。最后一个参数定义了默认组件,它也获得焦点,传递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);
                        }
                      }
                    }
                  }
                }
              }
        
            });
        

        【讨论】:

          【解决方案8】:

          试试这个

          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();
          }
          

          【讨论】:

          • 我想第一个解决方案适合我,因为我需要一个文本字段并让用户以文本形式输入他/她的输入。
          • @ehsun7b,第二种解决方案也适合您。关于我包含的代码sn-p,只需将txtKey 包含为options 的一项,然后在最后一个参数options[indexOfTextField] 中指向它。我已经测试过了,效果很好。
          • 组件显示永远不会执行!
          • @ehsun7b,它应该,否则你不会在显示中看到txtKey。无论如何,我建议您使用我提供给您的第二个选项。它已经过测试和验证。
          • @mre:我之前尝试过第二种解决方案,问题是:它显示按钮旁边的文本字段,而不是对话框中间。 :)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多