Search code examples
javaswingjframekeylistenersetfocus

JFrame not responding to keylistener


Hi Im trying to add a KeyListener to a JFrame. I've done this before and this worked perfectly. Now I am copying the code to let it listen to another JFrame, but now it stopped working for some reason. I don't know what i did wrong.

EDIT: It looks like everything works untill i push a button from one of the 2 JPanels inside the screen. It's like it lost focus after that. How can i fix this?

This is the constructor of the new JFrame where I add the KeyListener :

public class QuizSoftwareView extends JFrame implements View{

private Observable $model;
private Controller $controller;

public EditQuestionsView $editQuestionsView;
public EditTeamsView $editTeamsView;
public AdministratorMenu $adminMenu;


private boolean $isPressed; /* To check if we already listened to a key press event */


/**
 * Constructor to make a new quiz
 */
public QuizSoftwareView(Observable model, Controller controller) {

    this.setTitle(QuizSoftwareModel.$language.getMessages().getString("title"));

    $model = model;

    if (controller == null) 
        $controller = new QuizSoftwareController(model);
    else
        $controller = controller;

    $isPressed = false;

    initComponents();

    setFocusable(true);

    /* Add a keylistener for every team */
    addKeyListener(new KeyAdapter() {
        public void keyPressed(KeyEvent e) {

            System.out.println("TEST");
            int teamSize; /* team size*/
            teamSize = ((QuizSoftwareModel) getModel()).getQuiz().getModel().getTeams().size();

            System.out.println(teamSize);

            /* F1 is keycode 112, so % 111 gives 1 -> team 1, team 1 gets button F1, team 2 gets button F2 and so on... */
            if((e.getKeyCode() % 111) <= teamSize /*&& !alreadyPressed((e.getKeyCode() % 111))*/) { /* If you pressed a number under the teamsize we listen to it, and that team hasn't pressed their button before */  
                /* Give a pop up message to the admin that a team has pushed their button */
                //buttonPressed((e.getKeyCode() % 111));
                System.out.println("TESTT");
                ((QuizSoftwareController)getController()).showScoreView((e.getKeyCode() % 111));
            }
        }
    }); 

    $isPressed = false;

}
}

This is the code of where i did this before (works perfect) :

public class QuizView extends JFrame implements View {
private Observable $model;
private Controller $controller;

private QuestionView $questionView;
private MediaPlayer $mediaView;

private Question $question;

private boolean $isPressed; /* To check if we already listened to a key press event */


public QuizView(Observable model, Controller controller){
    setFocusable(true);

    $model = model;

    if (controller == null)
        $controller = new QuizController(model);
    else
        $controller = controller;

    $question = null;   
    $isPressed = false;

    $questionView = new QuestionView($model, null); /* null -> Give the default controller as parameter */
    $mediaView = new MediaPlayer($model, null); /* null -> Use default controller */

    $model.addObserver($questionView);
    $model.addObserver($mediaView);


    setTitle("Quiz"); /* Universal word so no messagebundle */
    getContentPane().setLayout(new BorderLayout());

    getContentPane().add($questionView, BorderLayout.CENTER);
    getContentPane().add($mediaView, BorderLayout.EAST);

    setExtendedState(this.MAXIMIZED_BOTH);

    addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
            ((QuizController)getController()).stop();
            dispose();

        }
    });

    setFocusable(true);

    /* Add a keylistener for every team */
    addKeyListener(new KeyAdapter() {
        public void keyPressed(KeyEvent e) {
            int teamSize; /* team size*/
            teamSize = ((QuizModel) getModel()).getTeams().size();

            /* F1 is keycode 112, so % 111 gives 1 -> team 1, team 1 gets button F1, team 2 gets button F2 and so on... */
            if((e.getKeyCode() % 111) <= teamSize && !alreadyPressed((e.getKeyCode() % 111))) { /* If you pressed a number under the teamsize we listen to it, and that team hasn't pressed their button before */  
                /* Give a pop up message to the admin that a team has pushed their button */
                buttonPressed((e.getKeyCode() % 111));
                ((QuizController)getController()).showScoreView((e.getKeyCode() % 111));
            }
        }
    }); 

    $isPressed = false;

    pack();
    setVisible(false);
}
}

Someone knows what's wrong? Help would be greatly appreciated since this is for a project for school.

Thanks!


Solution

  • ah yeah I see now :) In that case it's a bit more involved, you need to use application wide actions, or well an application-wide key listener, because KeyListeners won't work on containers when a child component has the focus... See this question

    Setting up application wide Key Listeners

    edit: sure there's always a quick and dirty fix :p

    try this:

    public class Test2 extends JFrame {
    
        private boolean $isPressed; /*
                                     * To check if we already listened to a key
                                     * press event
                                     */
    
        /**
         * Constructor to make a new quiz
         */
        public Test2() {
    
            this.setTitle("");
    
            $isPressed = false;
    
            setFocusable(true);
            getContentPane().setLayout(new BorderLayout());
            JButton b = new JButton();
            b.addFocusListener(new FocusListener() {
    
                @Override
                public void focusLost(FocusEvent e) {
                    // TODO Auto-generated method stub
    
                }
    
                @Override
                public void focusGained(FocusEvent e) {
                    Test2.this.requestFocus();
                }
            });
            getContentPane().add(b);
            /* Add a keylistener for every team */
            addKeyListener(new KeyAdapter() {
                public void keyPressed(KeyEvent e) {
    
                    System.out.println("TEST");
                }
            });
            $isPressed = false;
            b.grabFocus();
            pack();
        }
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            new Test2().setVisible(true);
    
        }
    }
    

    so essentially you can add a focus listener to your components.. but then for this particular hack you'll need to do it for all your child components... obviously you'd only need to define one FocusListener and you can reuse it everywhere, but generally there's a reason for why a component has focus so you don't want to just make it lose focus.. but maybe in your case it doesn't matter.

    so just to clarify, without the FocusListener on the JButton above, "TEST" gets printed in the console until I click on the button, then it no longer works. Then with the FocusListener, it will always work because the JFrame will regain focus when the button has the focus, so the KeyListener on it will work again.

    Another way to solve this problem would be to define one instance of the KeyAdapter, and set it as KeyListener to all your components in that window.