Search code examples
javaswingjpanelfocusjtextfield

How to request focus on JComponent after changing JPanel in JFrame


I have these two classes:

class Test:

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test {

    //frame
    private static JFrame frame = new JFrame() {
        private static final long serialVersionUID = 1L;

        {
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(400, 200);
            setLocationRelativeTo(null);
            setLayout(new BorderLayout());

            viewPanel = new JPanel(new BorderLayout());
            add(viewPanel);
        }
    };

    private static JPanel viewPanel;

    //change the panels
    public static void showView(JPanel panel) {
        viewPanel.removeAll();

        viewPanel.add(panel, BorderLayout.CENTER);
        viewPanel.revalidate();
        viewPanel.repaint();
    }

    //main method
    public static void main (String [] args) {

        SwingUtilities.invokeLater(() -> showView(Panels.panel1));

        SwingUtilities.invokeLater(() -> {
            frame.setVisible(true);
        });
    }
}

class Panels:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

//panels
public class Panels {

    //first panel
    static JPanel panel1 = new JPanel() {
        private static final long serialVersionUID = 1L;
        {
            JButton button = new JButton("Click here!");

            add(button);

            button.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {
                    SwingUtilities.invokeLater(() -> Test.showView(panel2));
                }
            });
        }
    };

    //second panel
    static JPanel panel2 = new JPanel() {
        private static final long serialVersionUID = 1L;
        {
            JTextField textField = new JTextField(5);

                add(textField);
        }
    };
}

And as you can see, the JPanel changes inside the JFrame, after clicking the JButton: How can I change the JPanel from another Class?

But how can I now set the focus on the JTextField, after changing panel1 to panel2?

I've tried to add grabFocus(); to the JTextField, but it didn't work and requestFocus(); didn't work as well.

Thanks in advance!


Solution

    1. There's no need to call showView(...) with invokeLater. Your ActionListener is being called on the EDT, so this is unnecessary code.
    2. If you had a handle to the JTextField, you could call requestFocusInWindow() on it after making it visible, and it should have focus.

    For example:

    button.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            // SwingUtilities.invokeLater(() -> Test.showView(panel2)); // not needed
            Test.showView(panel2);
            Component[] comps = panel2.getComponents();
            if (comps.length > 0) {
                comps[0].requestFocusInWindow();
            }
        }
    });
    

    Myself, I would use CardLayout to do my swapping and would not use the kludge of getting components via getComponents() but rather using much less brittle method calls.

    For example:

    import java.awt.CardLayout;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class MyPanelTest extends JPanel {
        private TextFieldPanel textFieldPanel = new TextFieldPanel();
        private CardLayout cardLayout = new CardLayout();
    
        public MyPanelTest() {
            JPanel buttonPanel = new JPanel();
            buttonPanel.add(new JButton(new ButtonAction("Press Me")));        
            setPreferredSize(new Dimension(400, 200));
            setLayout(cardLayout);
            add(buttonPanel, "button panel");
            add(textFieldPanel, TextFieldPanel.NAME);
        }
    
        private class ButtonAction extends AbstractAction {
    
            public ButtonAction(String name) {
                super(name);
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                cardLayout.show(MyPanelTest.this, TextFieldPanel.NAME);
                textFieldPanel.textFieldRequestFocus();
            }
        }
    
        private static void createAndShowGui() {
            JFrame frame = new JFrame("My Panel Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(new MyPanelTest());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    }
    

    @SuppressWarnings("serial")
    class TextFieldPanel extends JPanel {
        public static final String NAME = "TEXT_FIELD_PANEL";
        private JTextField textField = new JTextField(10);
    
        public TextFieldPanel() {
            add(textField);
        }
    
        public void textFieldRequestFocus() {
            textField.requestFocusInWindow();
        }
    }