1

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!

user3485470
  • 121
  • 5
  • 11

2 Answers2

1

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.

Community
  • 1
  • 1
Shikamu
  • 382
  • 1
  • 16
  • Isn't there a quick fix to regain focus on the parent JFrame? Or will this not work? – user3485470 Jun 04 '14 at 11:15
  • Thanks for the help ;D, but the problem is that the administrator of the quiz should be able to make a new question -> focus goes to the JFrame where the new question is made, but the teams should still be able to press their button during that time (listener is inside the parent JFrame), you understand what I mean? I like your dirty fix but that still doesn't really solve this problem. Is there some way how the parent JFrame can still respond to the keys that get pressed? Or should I make the add question JFrame a JPanel inside this JFrame to solve the problem? – user3485470 Jun 04 '14 at 11:31
  • Since this is model-view-controller is there an option how i could grabFocus in the JFrame in the update? That would solve the problem aswell. :) – user3485470 Jun 04 '14 at 11:36
  • So it is possible to type in JTextField inside a JFrame inside the parent JFrame and still respond to keys pressed like F1 and handle them in the parent JFrame? – user3485470 Jun 04 '14 at 11:43
  • using a JPanel and putting your components in it will not solve the problem I'm afraid. If you're interested in capturing specific keys, I suggest looking into the question I linked in my answer. – Shikamu Jun 04 '14 at 11:43
  • you can have a look at Peter's answer in this question http://stackoverflow.com/questions/286727/java-keylistener-for-jframe-is-being-unresponsive ... just tried it (so using the KeyEvenDispatcher) and that works. The example in his answer features a textfield, in which you can type in it and the dispatcher gets triggered, regardless of what component has the focus. – Shikamu Jun 04 '14 at 11:44
  • So then i should be able to type in a popped-up JFrame and still receive key presses and handle them in another JFrame? – user3485470 Jun 04 '14 at 11:54
  • the KeyEventDispatcher will essentially act as a global keylistener. So it will always get triggered, regardless of what has the focus. Then, if you have a keylistener on a JFrame, that keylistener will be triggered when that JFrame has focus. If you have another JFrame with a KeyListener of its own, then that one will get triggered, but only when that JFrame is focused. The same principle applies to all components in those JFrame. – Shikamu Jun 04 '14 at 11:58
  • Ok thank you very much :) I think everything will work fine now, kudos to you! – user3485470 Jun 04 '14 at 12:00
0

I realize this thread is a bit old, but I keep coming across it when trying to capture keystrokes at the global level. I finally figured out a good way to do it and figured I would post here for others (and myself, because I'm sure I'll forget 2 days from now).

   KeyboardFocusManager kbfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
   kbfm.addKeyEventDispather( new KeyEventDispatcher(){
     public boolean dispatchKeyEvent(KeyEvent e){
       if(e.getID() == KeyEvent.KeyPressed) {
         switch(e.getKeyCode()) {
           case KeyEvent.VK_A:
             //Stuff to do when A is pressed
             System.out.println("You pressed the 'A' key");
           case KeyEvent.VK_B:
             //Stuff to do when B is pressed
             System.out.println("You pressed the 'B' key");
           // add other cases as needed for other keys
         } 
       }
       //Allow event to be redispatched
       return false;
     }
   } );

I realize that keyBindings are the preferred method, but sometimes you shouldn't use a hatchet to remove a mosquito from your forehead. ;-)

Chad Estes
  • 371
  • 1
  • 3
  • 15