1

I am creating a virtual piano in Java. So far I have action listeners for two of the keys which work for the most part, just not after one another. For example, I hit q on the keyboard and it presses the c key and plays a c, which is what it's supposed to do. But then I want to hit the d key on the piano by hitting w on the keyboard, and it won't do it if I've already hit the q key.

// c key
        JButton btnC3 = new JButton("");
        btnC3.addKeyListener(new KeyAdapter() 
        {
            @Override
            public void keyPressed(KeyEvent e) 
            {
                if (e.getKeyCode() == KeyEvent.VK_Q)
                {
                    btnC3.doClick();
                }
            }
        });
        btnC3.addActionListener(new ActionListener() 
        {
            public void actionPerformed(ActionEvent e) 
            {
                // play c
                try 
                {
                    keys.playNote(Notes.c3.getValue());
                } 
                catch (InterruptedException e1) 
                {
                    e1.printStackTrace();
                }
                catch (InvalidMidiDataException e2)
                {
                    e2.printStackTrace();
                }
            }
        });
// d key
JButton btnD3 = new JButton("");
        btnD3.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) 
            {
                if (e.getKeyCode() == KeyEvent.VK_W)
                {
                    btnD3.doClick();
                }
            }
        });
        btnD3.addActionListener(new ActionListener() 
        {
            public void actionPerformed(ActionEvent e) 
            {
                // play d
                try 
                {
                    keys.playNote(Notes.d3.getValue());
                } 
                catch (InterruptedException e1) 
                {
                    e1.printStackTrace();
                }
                catch (InvalidMidiDataException e2)
                {
                    e2.printStackTrace();
                }
            }
        });
        btnD3.setBackground(Color.WHITE);
        btnD3.setBounds(wKeyWidth*1, 0, wKeyWidth, wKeyHeight);
        frame.getContentPane().add(btnD3);
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
Matt
  • 379
  • 7
  • 18
  • Key Bindings are your cleanest solution. [For example](http://stackoverflow.com/questions/8087536/keylistener-on-jpanel-randomly-unresponsive/8087810#8087810). – Hovercraft Full Of Eels May 01 '16 at 02:39
  • [For example](http://stackoverflow.com/questions/27283908/java-jbutton-keylistener/27284080#27284080) and [example](http://stackoverflow.com/questions/25073094/jbutton-does-not-change-color-when-button-is-typed/25073230#25073230) – MadProgrammer May 01 '16 at 03:14

2 Answers2

3

Focus is the problem. It will work when the q is pressed because that button has the focus. It wont work if w is pressed no matter if you press q first because the "q" button has the focus throughout. You should use KeyBindings instead. They work despite whatever component has focus.

Another solution would be to add the q and w button presses to a keylistener added onto the JFrame and use requestFocus() and grabFocus().

But you would need to add implementation for both the keylistener in the JFrame and the button press and release for the button.

Here is the perfect link to see how focus affects how your key presses work in swing

http://www.javaworld.com/article/2076720/core-java/focus-on-swing.html

DarkV1
  • 1,776
  • 12
  • 17
1

The problem is related to key board focus, a KeyListener will only generate events when the component the listener is registered to IS focusable and HAS focus.

Obviously, when a button is "clicked" it gains focus, meaning that none of the other components can responds.

The basic answer is, use is the Use Key Bindings API.

There are a number of choices you can make, you can use the current container to register the key bindings against, for example...

InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();

JButton btnC3 = new JButton("");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_Q, 0), "c3");
am.put("c3", new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) {
        btnC3.doClick();
    }
});
btnC3.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // play c
        try {
            keys.playNote(Notes.c3.getValue());
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        } catch (InvalidMidiDataException e2) {
            e2.printStackTrace();
        }
    }
});

JButton btnD3 = new JButton("");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "d3");
am.put("d3", new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) {
        btnD3.doClick();
    }
});

or you can register them against in the individual components. The choice will mostly come down to how reusable you want the solution to be.

For example, you could create a Action which can be applied to both the JButton and key binding which means you don't need to programmatically click the button.

For example...

public class NoteAction extends AbstractAction {
    private Note note;
    private Keys keys;

    public NoteAction(Note note, Keys keys) {
        this.note = note;
        this.keys = keys;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        keys.playNote(note.getValue());
    }

}

(I don't have your code so I'm just make some of the class names up)

Then you could simply use...

NoteAction noteAction = new NoteAction(Notes.d3, keys);
JButton btnC3 = new JButton(noteAction);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_Q, 0), "c3");
am.put("c3", noteAction);

to set it up.

See How to Use Actions for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Question, though: I had put in the code you did and it made my entire GUI disappear. Is there any specific reason why this happened? – Matt May 01 '16 at 03:18
  • I'm talking about the first block of code you posted. – Matt May 01 '16 at 03:22
  • Well, my example didn't add any of the components to the container ... did you remember to add the buttons? – MadProgrammer May 01 '16 at 03:58