2

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.

AeroFighter76
  • 73
  • 1
  • 9
  • Are you sure you're calling your addNotify method? Sometimes you have to precede requestFocus() with setFocusable(true). Alternatively depending on your setup you may need a requestFocusInWindow() instead. If my memory serves me correctly that one is for JApplets. – Perry Monschau Sep 18 '16 at 22:53
  • 1
    You will have to add relevant parts of your code. – PM 77-1 Sep 18 '16 at 22:53
  • @Perry Monschau I do call it- in a paint() method and in the KeyListener just to be sure. – AeroFighter76 Sep 18 '16 at 22:57
  • @PM77-1 Apologies- as part of class policy we cannot paste parts of school code on the internet. I can try to recreate parts of it for purposes of the question- what should I add? – AeroFighter76 Sep 18 '16 at 22:58
  • Add the minimal required code that reproduces your problem. – PM 77-1 Sep 18 '16 at 23:00
  • *"we cannot paste parts of school code on the internet"* We don't want to see them, instead post a [mcve]. Note there are two close reasons that mention 'no MCVE'. – Andrew Thompson Sep 19 '16 at 01:34
  • @AndrewThompson have recreated a similar situation that should satisfy the requirements of the Minimal, Complete and Verifiable example. – AeroFighter76 Sep 19 '16 at 03:00
  • Use a logical and consistent form of indenting code lines and blocks. The indentation is intended to make the flow of the code easier to follow! – Andrew Thompson Sep 19 '16 at 03:01
  • `public void paint(Graphics g) .. repaint();` Is the repaint in the paint method? (Hard to tell from code indentation.) That's the wrong paint method for a `JComponent` and the wrong way to go about animation.. – Andrew Thompson Sep 19 '16 at 03:03
  • I apologize, that is not my doing. It's the website's auto formatting- This is copy pasted from my code in Eclipse, which is much better formatted. – AeroFighter76 Sep 19 '16 at 03:03
  • Yes, the repaint is in the Paint method, at it's end. – AeroFighter76 Sep 19 '16 at 03:04
  • *"It's the website's auto formatting.."* Really? I doubt that for a couple of reasons. 1) I've posted oodles of code and am unfamiliar with any 'site auto formatting'. The code comes through as it was formatted in Netbeans. 2) I've seen many code samples from what is apparently Eclipse, and they are all formatted as I'd expect. 3) That indentation is not simply 'removed' but indeed inconsistent! - The code formatting I'm talking about is to select the text and click the `{}` button at the top of the message posting/editing form. – Andrew Thompson Sep 19 '16 at 03:07
  • There is no reason why I would not post it correctly formatted and easier for someone to fix if possible. I understand it is less than desirable and if you have any questions as to where code is placed, please ask and I will be happy to answer. – AeroFighter76 Sep 19 '16 at 03:14
  • *KeyPressed() not responding"* BTW - the 'not responding' strongly suggests the code is blocking the EDT (research 'block EDT'). Calling `repaint()` from within a paint method is a likely source of that. *"if you have any questions"* Nope. It's you with the question. Since so many people **can** figure out how to post a proper MCVE with code formatting, the start of this comment is all the help I'll be offering until you can figure it out. That ball is in your court. – Andrew Thompson Sep 19 '16 at 04:01
  • I will attempt to fix the formatting in the examples to the best of my ability. To avoid further confusion, is there any StackOverflow reference you could point me to that clearly details the correct formatting of code for examples? Also, repaint() seems to not be the issue. I removed it from my paint method and the program exhibited the same behavior. – AeroFighter76 Sep 19 '16 at 04:12
  • @AndrewThompson Updated formatting – AeroFighter76 Sep 19 '16 at 04:23
  • @AeroFighter76 I'd just like to point out that calling requestFocus in paint or in the keylistener are bad ideas. Put it where you put addKeyListener. Don't override methods until you know what they're for. If you want to work with the Java SDK you'll have to respect the library and the way it works. Assuming you haven't setup documentation in your IDE, you can always google "Java " and start researching from there what the methods are for. Research is a step closer to the solution, and a step away from chaos. – Perry Monschau Sep 19 '16 at 08:19

1 Answers1

2

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) {
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 1
    As an addendum, here's clarification for when to override paint/paintComponent: http://stackoverflow.com/questions/9389187/difference-between-paint-paintcomponent-and-paintcomponents-in-swing – Perry Monschau Sep 19 '16 at 08:10
  • The AddNotify method was not calling requestFocus correctly, as you said. Marked correct – AeroFighter76 Sep 19 '16 at 19:12