Search code examples
javaeventsgraphicskeylistenertowers-of-hanoi

KeyPressed() not responding (Regular Java)


I am programming a Tower of Hanoi program as per the specifications of instructions given in class. Before proceeding, I would like to state that I have searched for similar answers and understand it is preferable to use KeyBinding. We are required to use KeyListener in class.

If you wish to see how Tower of Hanoi works please read here: https://en.wikipedia.org/wiki/Tower_of_Hanoi

My program uses the keys 1, 2 and 3 for each of the three rods. You click these twice to make one move: the first click to specify the sending rod and the second to specify the receiving rod.

I have added addKeyListener(this) in my constructor along with writing a addNotify() method which does {requestFocus();}. Compiling creates no errors. I added a System.out.println("This ran!"); inside several areas of the KeyPressed method to see if it runs- it does not.

The only other piece of information that may be useful would be that I am using the getKeyChar() method to identify which key is being pressed.

Any help or comment upon if I am missing some aspect of using KeyListener and KeyPressed correctly would be much appreciated. Thanks.

Here is the minimal code needed to recreate an example:

Main Class:

class MainFile {
    public static void main(String[] args) {
        new TFrame("Frame");
    }
}

Panel Class:

import java.awt.*;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class TPanel extends JPanel implements KeyListener {
    boolean towerA;
    boolean towerB;
    boolean towerC;

    public TPanel() {
        super();
        setSize(600, 600);
        addKeyListener(this);
    }

    public void keyTyped(KeyEvent e) {
    }

    public void addNotify() {
        requestFocus();
    }

    public void paint(Graphics g) {
        addNotify();
        if (towerA == true) {
            g.setColor(Color.GREEN);
            g.fillRect(20, getHeight(), 40, 100);
        } else {
            g.setColor(Color.RED);
            g.fillRect(20, 0, 40, 100);
        }
        if (towerB == true) {
            g.setColor(Color.GREEN);
            g.fillRect(100, 0, 40, 100);
        } else {
            g.setColor(Color.RED);
            g.fillRect(100, 0, 40, 100);
        }
        if (towerC == true) {
            g.setColor(Color.GREEN);
            g.fillRect(180, 0, 40, 100);
        } else {
            g.setColor(Color.RED);
            g.fillRect(180, 0, 40, 100);
        }
        repaint();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // TODO Auto-generated method stub
        addNotify();
        if (e.getKeyChar() == '1') {
            towerA = true;
        }
        if (e.getKeyChar() == '2') {
            towerB = true;

        }
        if (e.getKeyChar() == '3') {
            towerC = true;

        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub

    }
}

Frame Class:

    import javax.swing.JFrame;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.Dimension;

public class TFrame extends JFrame implements java.awt.event.KeyListener {
    public TFrame(String title) {
        super(title);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        pack();
        addKeyListener(this);

        TPanel p = new TPanel();
        Insets frameInsets = getInsets();

        int frameWidth = p.getWidth() + (frameInsets.left + frameInsets.right);
        int frameHeight = p.getHeight() + (frameInsets.top + frameInsets.bottom);

        setPreferredSize(new Dimension(frameWidth, frameHeight));

        setLayout(null);
        add(p);
        pack();
        setVisible(true);
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyPressed(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub

    }

}

UPDATE: I have put in some further research and find that it may be something with focusing the component. I have added many focuses and addNotify() calls throughout the program and it does not seem to have an effect.


Solution

  • This, being a single copy/paste, compile, run is an MCVE. There are some number of problems with the code seen in the question. I fixed some of them (see comments in code) before stumbling across the fundamental reason for the problem, in that the code overrides the addNotify() method. Note that the call to requestFocus() at that time will fail in any case, given the component needs to be not only focusable, but also visible on screen for it to work.

    That part is left as an exercise for you to implement, but if you have trouble, please post another question with an MCVE.

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class TPanel extends JPanel implements KeyListener {
    
        boolean towerA;
        boolean towerB;
        boolean towerC;
    
        public TPanel() {
            super();
            setSize(600, 600);
            // for testing
            setBackground(Color.BLACK);
            addKeyListener(this);
            // A component must BE focusable before it can hop to accept the focus.
            setFocusable(true); 
            // create an animation listener
            ActionListener animationListener = new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    repaint();
                }
            };
            // create and start an animation timer
            Timer timer = new Timer(200, animationListener);
            timer.start();
            requestFocus();
        }
    
        public void keyTyped(KeyEvent e) {
        }
    
        @Override
        /* I've never bothered to find out what addNotify() method is for.
        Inadvisable to use it to request the focus! 
        public void addNotify() {
            requestFocus();
        } */
    
        /* For any JComponent, we should override the paintComponent(Graphics)
        method rather than the paint(Graphics) method. */
        public void paintComponent(Graphics g) {
            /* 1st thing should be done in any overridden paint method is to
            call the super method. */
            super.paintComponent(g);
            //addNotify();
            if (towerA == true) {
                g.setColor(Color.GREEN);
                g.fillRect(20, getHeight(), 40, 100);
            } else {
                g.setColor(Color.RED);
                g.fillRect(20, 0, 40, 100);
            }
            if (towerB == true) {
                g.setColor(Color.GREEN);
                g.fillRect(100, 0, 40, 100);
            } else {
                g.setColor(Color.RED);
                g.fillRect(100, 0, 40, 100);
            }
            if (towerC == true) {
                g.setColor(Color.GREEN);
                g.fillRect(180, 0, 40, 100);
            } else {
                g.setColor(Color.RED);
                g.fillRect(180, 0, 40, 100);
            }
            /* As mentioned in comment, this will cause an infinite loop & block
            the EDT! Use a Swing Timer for animation. */
            // repaint(); 
        }
    
        @Override
        public void keyPressed(KeyEvent e) {
            // for testing
            System.out.println("e: " + e);
            addNotify();
            if (e.getKeyChar() == '1') {
                towerA = true;
            }
            if (e.getKeyChar() == '2') {
                towerB = true;
    
            }
            if (e.getKeyChar() == '3') {
                towerC = true;
    
            }
        }
    
        @Override
        public void keyReleased(KeyEvent e) {
        }
    
        public static void main(String[] args) {
            new TFrame("Frame");
        }
    }
    
    class TFrame extends JFrame implements java.awt.event.KeyListener {
    
        public TFrame(String title) {
            super(title);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setResizable(false);
            pack();
            addKeyListener(this);
    
            TPanel p = new TPanel();
            Insets frameInsets = getInsets();
    
            int frameWidth = p.getWidth() + (frameInsets.left + frameInsets.right);
            int frameHeight = p.getHeight() + (frameInsets.top + frameInsets.bottom);
    
            setPreferredSize(new Dimension(frameWidth, frameHeight));
    
            // Java GUIs were designed to work with layouts. Use them! 
            // setLayout(null);
            add(p);
            pack();
            setVisible(true);
        }
    
        @Override
        public void keyTyped(KeyEvent e) {
        }
    
        @Override
        public void keyPressed(KeyEvent e) {
        }
    
        @Override
        public void keyReleased(KeyEvent e) {
        }
    }