在JFileChooser(NimbusLookAndFeel)中为JTextField获取焦点

3
默认情况下,在 NimbusLookAndFeel 模式下的 JFileChooser 并不会在用户键入文件路径的 JTextField 显示焦点。如图所示,JComboBoxJFileChooser 的焦点所有者。
现在我该怎么让 JFileChooser 打开时 JTextField 获取焦点呢?我尝试通过递归逻辑从 JFileChooser 中获取 JTextField 并对其调用 requestFocusInWindow() 方法。以下是我完成的全部代码。
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class GetFocusForJTextField extends JFrame
{
JButton jb;
JFileChooser jf;

    public GetFocusForJTextField()
    {
        createAndShowGUI();
    }

    private void createAndShowGUI()
    {
        // For NimbusLookAndFeel, JTextField is not
        // the default focus owner in JFileChooser
        try
        {
            UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
        }catch(Exception e){}

        setTitle("Get Focus for JTextField");
        setLayout(new FlowLayout());
        setSize(400,400);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        jb=new JButton("Open JFileChooser");
        jb.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae)
            {
                showDialog();
            }
        });

        jf=new JFileChooser();

        add(jb);
    }

    // Loop to find the JTextField, the first
    // JTextField in JFileChooser
    private void grabFocusForTextField(Component[] c)
    {
        for(Component k:c)
        {
            if(k instanceof JTextField)
            {
                JTextField jt=(JTextField)k;
                jt.requestFocusInWindow();
                break;
            }
            else if(k instanceof JPanel)
            {
                JPanel jp=(JPanel)k;
                grabFocusForTextField(jp.getComponents());
            }
        }
    }

    private void showDialog()
    {
        jf.showOpenDialog(this);
        grabFocusForTextField(jf.getComponents());
    }

    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable(){
            public void run()
            {
                new GetFocusForJTextField();
            }
        });
    }
}

我仍然无法聚焦。为什么我无法做到这一点。

(Note: 该文本已被翻译成中文,保留了HTML标签,并且没有解释。)

请仅限于Nimbus引起了此问题或SystemL&F也是如此。 - mKorbel
问题仅出现在 NimbusLookAndFeel 上。我已经测试了 WindowsLookAndFeelWindowsClassicLookAndFeelMetalLookAndFeelMotifLookAndFeel - JavaTechnical
我认为我已经发布了一次(或几次)具有值的设置焦点,我可以搜索还是您会执行该操作(始终在[标签]内使用标签,否则您将失去大部分线程==疯狂引擎在第一眼。:-) - mKorbel
没有看到添加的图片,你是指在选中值为My Document的JComboBox周围绘制焦点吗? - mKorbel
+1 很好的发现 :-) 技术原因是 SynthFileChooserUI 正在监听 fileChooser 的祖先属性并委托给一个内部方法 doAncestorChange(..),但该方法是...空的。今天我学到了一件事,还有就是 JComponent.addNotify() 会触发 propertyChange。 - kleopatra
显示剩余3条评论
1个回答

3
在调用grabFocusForTextField()时,JTextField不可显示,因此您无法使JTextField获得焦点。要使组件获得焦点,该组件必须首先存在、可见且可显示,启用并且可聚焦。有关更多信息,请参见文档中的焦点子系统
您需要在JFileChooser上注册自己的HierarchyListener以侦听HierarchyEvent。在NimbusLookAndFeel中,可能没有正确执行此操作,或者选择了JComboBox作为焦点所有者。每当组件可显示时,每当JFileChooser的层次结构发生更改时,都会触发此事件,此时JTextField是可显示的。
我已经重写了代码以使其正常工作。
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class GetFocusForJTextField extends JFrame
{
JButton jb;
JFileChooser jf;

    public GetFocusForJTextField()
    {
        createAndShowGUI();
    }

    private void createAndShowGUI()
    {
        // For NimbusLookAndFeel, JTextField is not
        // the default focus owner in JFileChooser
        try
        {
            UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
        }catch(Exception e){}

        setTitle("Get Focus for JTextField");
        setLayout(new FlowLayout());
        setSize(400,400);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        jb=new JButton("Open JFileChooser");
        jb.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae)
            {
                showDialog();
            }
        });

        jf=new JFileChooser();

        // Even if you add some other JTextField
        // as accessory to JFileChooser
        jf.setAccessory(new JTextField(20));

        jf.addHierarchyListener(new HierarchyListener(){
            public void hierarchyChanged(HierarchyEvent he)
            {
                grabFocusForTextField(jf.getComponents());
            }
        });     

        add(jb);
    }

    // Loop to find the JTextField, the first
    // JTextField in JFileChooser
    // Even if you setAccessory which contains a JTextField
    // or which is JTextField itself, it will not get focus
    private void grabFocusForTextField(Component[] c)
    {
        for(Component k:c)
        {
            if(k instanceof JTextField)
            {
                JTextField jt=(JTextField)k;
                jt.grabFocus();
                break;
            }
            else if(k instanceof JPanel)
            {
                JPanel jp=(JPanel)k;
                grabFocusForTextField(jp.getComponents());
            }
        }
    }

    private void showDialog()
    {
        jf.showOpenDialog(this);
    }

    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable(){
            public void run()
            {
                new GetFocusForJTextField();
            }
        });
    }
}

你也可以使用 requestFocusInWindow() 代替 grabFocus()


重点是简单的异步操作,需要包装成invokeLater,特别是针对基于JDialog的容器,没有父级的JDialog。 - mKorbel
1
@mKorbel 异步或同步 - 不将焦点集中在文本字段上只是 SynthFileChooserUI 中的一个错误。 - kleopatra
嗯,听起来不错,焦点是异步的,但其他外观仅在JTextField上有焦点,这可能是一个错误。+1 kleopatra - JavaTechnical
@kleopatra类似的问题已经在Metal主题中得到解决(我会去搜索),由@ - mKorbel
@kleopatra,仅仅是在我HDD/ROM上的一个bug。这是关于插入符号和选择的问题,而不是关于焦点的问题。详情请参考:https://dev59.com/snPYa4cB1Zd3GeqPhU4Z - mKorbel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接