Search code examples
javapaintkeylistenergraphics2dshapes

Keylistener works but is not executing the intended operation. Why?


This is the MainApplication which creates a Frame and holds the class which implements Graphics.

MainApplication.java

import java.awt.*;  

public class MainApplication 
{  
    public MainApplication()  
    {
        Frame frame= new Frame("Test App");
        frame.add(new KeyTest());
        frame.setBackground(Color.RED);
        frame.setLayout(null);  
        frame.setSize(700,750);  
        frame.setVisible(true); 
    } 
    public static void main(String args[])  
    {  

        new MainApplication();

    }
}  

This class creates all the Graphical shapes and implements the KeyListener too.

KeyTest.java

import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.RoundRectangle2D;

public class KeyTest extends Canvas {


    /**
     * 
     */
    private static final long serialVersionUID = 3L;
    Graphics2D graphics2D;
    Color color = Color.BLACK;
    private static float borderThickness = 5;
    Shape firstShape = new RoundRectangle2D.Double(20,40,300,50,10,10);
    Shape secondShape = new RoundRectangle2D.Double(20,150,300,50,10,10);
    public KeyTest()
    {
        setBackground(Color.RED);
        setSize(700,750);               
    }
    public void paint(Graphics graphics)  
    {  

        graphics2D = (Graphics2D)graphics; //TypeCasting to 2D
        System.out.println("I am inside paint");

        //      Smoothening the corners
        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        //      Apple border color
        Stroke oldStroke = graphics2D.getStroke();
        graphics2D.setStroke(new BasicStroke(borderThickness));
        graphics2D.setColor(Color.WHITE);

        //      Drawing a RoundedRectangle for Apple    
        graphics2D.draw(firstShape);
        graphics2D.setStroke(oldStroke);

        //      Setting the Background Color
        graphics2D.setColor(Color.BLACK); 
        graphics2D.fill(firstShape);


        //      Setting the font inside the shape
        Font firstFont = new Font("Serif", Font.BOLD,35);
        graphics2D.setFont(firstFont);
        graphics2D.setColor(Color.WHITE);
        graphics2D.drawString("Apple",30,80);


        //      Pineapple border color 
        graphics2D.setStroke(new BasicStroke(borderThickness));
        graphics2D.setColor(Color.WHITE);

        //      Drawing a RoundedRectangle for Pineapple    
        graphics2D.draw(secondShape);
        graphics2D.setStroke(oldStroke);

        //      Setting the Background Color
        graphics2D.setColor(Color.BLACK); 
        graphics2D.fill(secondShape);

        //      Setting the font inside the shape
        Font secondFont = new Font("Serif", Font.BOLD,35);
        graphics2D.setFont(secondFont);
        graphics2D.setColor(Color.WHITE);
        graphics2D.drawString("Pineapple",30,190);

        addKeyListener(new KeyListener(){

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

            }

            @Override
            public void keyPressed(KeyEvent e) {
                int keyCode = e.getKeyCode();

                System.out.println(keyCode);
                System.out.println(KeyEvent.VK_UP);
                if(keyCode==KeyEvent.VK_UP){
                    System.out.println("Going to move up");
                    move(firstShape);

                }
                if(keyCode==KeyEvent.VK_DOWN){
                    System.out.println("Going to move down");
                    move(secondShape);

                }
            }

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

            }});
    }
    public void move(Shape s){

        System.out.println("Check:"+s.getBounds2D());
        graphics2D.setColor(Color.GREEN);
        graphics2D.fill(s);
        System.out.println("moving out");
    }
}

My console output clearly shows that my Key Listener works but its not executing the task which I intend it to do.

Console Output

I am inside paint Going to move up

Check:java.awt.geom.Rectangle2D$Double[x=20.0,y=40.0,w=300.0,h=50.0]

moving out Going to move down

Check:java.awt.geom.Rectangle2D$Double[x=20.0,y=150.0,w=300.0,h=50.0]

moving out

OUTPUT:

The Output which am getting now

The output I expect when I press DOWN ARROW Button

The Output I expect when I press UP ARROW Button

The Output which am getting now.. (IMAGE 1)

The output I expect when I press DOWN ARROW Button (IMAGE 2)

The Output I expect when I press UP ARROW Button (IMAGE 3)


Solution

  • First, you shouldn't add a KeyListener from inside the paint method, since it will register an additional one each time paint is called.

    Then, don't rely on storing a Graphics object of a Componentand painting things on it, because many things can happen to it that are outside of your control (for instance it may get cleared by other occuring AWT UI operations).

    The only relevant Graphics object is the instance you receive in paint, and only in the scope of the paint method.

    So in the following code :

    • addKeyListener has been moved outside of paint .
    • The key listener calls move with the index of the Shape to move.
    • The move method now only registers the Shape to highlight depending on the index received, and calls repaint.
    • The Graphics2D object is set as a local variable to paint, since it isn't relevant outside.
    • paintHighlightedShape is responsible for painting the highlighted Shape, on a Graphics2D object received as a parameter
    • The paint method now calls paintHighlightedShape with its Graphics2D object passed as a parameter.

    Note that a main method has been added to KeyTest for testing purpose, its content should go in your main class.

    Also if you plan on having more Shapes to highlight, you may use an array of Shape, and the index passed to move could be the array index of the Shape to highlight.

    Finally, to optimize things, we shouldn't draw the regular shape if it is a highlighted one, since it will be hidden by the green shape anyway. Consider creating a drawShape(Graphics2D, Shape) method that will draw the regular shape or the green shape, depending on the shape being highlighted or not. You would call this method from the paint method, looping on all shapes.

    import java.awt.BasicStroke;
    import java.awt.Canvas;
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Frame;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.Shape;
    import java.awt.Stroke;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.awt.geom.RoundRectangle2D;
    
    public class KeyTest extends Canvas {
    
        /**
         *
         */
        private static final long serialVersionUID = 3L;
        Color color = Color.BLACK;
        private static float borderThickness = 5;
        Shape firstShape = new RoundRectangle2D.Double(20, 40, 300, 50, 10, 10);
        Shape secondShape = new RoundRectangle2D.Double(20, 150, 300, 50, 10, 10);
    
        Shape highlightedShape;
    
        public KeyTest() {
            setBackground(Color.RED);
            setSize(700, 750);
        }
    
        public static void main(final String[] args) {
            Frame frame = new Frame("Test App");
            final KeyTest keyTest = new KeyTest();
            keyTest.addKeyListener(new KeyListener() {
    
                @Override
                public void keyTyped(final KeyEvent e) {
                    // TODO Auto-generated method stub
    
                }
    
                @Override
                public void keyPressed(final KeyEvent e) {
                    int keyCode = e.getKeyCode();
    
                    System.out.println(keyCode);
                    System.out.println(KeyEvent.VK_UP);
                    if (keyCode == KeyEvent.VK_UP) {
                        System.out.println("Going to move up");
                        keyTest.move(1);
    
                    }
                    if (keyCode == KeyEvent.VK_DOWN) {
                        System.out.println("Going to move down");
                        keyTest.move(2);
    
                    }
                }
    
                @Override
                public void keyReleased(final KeyEvent e) {
                    // TODO Auto-generated method stub
    
                }
            });
            frame.add(keyTest);
            frame.setBackground(Color.RED);
            frame.setLayout(null);
            frame.setSize(700, 750);
            frame.setVisible(true);
        }
    
        public void paint(final Graphics graphics) {
    
            Graphics2D graphics2D = (Graphics2D) graphics; //TypeCasting to 2D
            System.out.println("I am inside paint");
    
            //      Smoothening the corners
            graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
    
            //      Apple border color
            Stroke oldStroke = graphics2D.getStroke();
            graphics2D.setStroke(new BasicStroke(borderThickness));
            graphics2D.setColor(Color.WHITE);
    
            //      Drawing a RoundedRectangle for Apple
            graphics2D.draw(firstShape);
            graphics2D.setStroke(oldStroke);
    
            //      Setting the Background Color
            graphics2D.setColor(Color.BLACK);
            graphics2D.fill(firstShape);
    
            //      Setting the font inside the shape
            Font firstFont = new Font("Serif", Font.BOLD, 35);
            graphics2D.setFont(firstFont);
            graphics2D.setColor(Color.WHITE);
            graphics2D.drawString("Apple", 30, 80);
    
            //      Pineapple border color
            graphics2D.setStroke(new BasicStroke(borderThickness));
            graphics2D.setColor(Color.WHITE);
    
            //      Drawing a RoundedRectangle for Pineapple
            graphics2D.draw(secondShape);
            graphics2D.setStroke(oldStroke);
    
            //      Setting the Background Color
            graphics2D.setColor(Color.BLACK);
            graphics2D.fill(secondShape);
    
            //      Setting the font inside the shape
            Font secondFont = new Font("Serif", Font.BOLD, 35);
            graphics2D.setFont(secondFont);
            graphics2D.setColor(Color.WHITE);
            graphics2D.drawString("Pineapple", 30, 190);
    
            paintHighlightedShape(graphics2D);
    
        }
    
        private void paintHighlightedShape(final Graphics2D graphics2D) {
    
            if (highlightedShape != null) {
    
                graphics2D.setColor(Color.GREEN);
                graphics2D.fill(highlightedShape);
    
            }
        }
    
        public void move(final int shapeNumber) {
    
            switch (shapeNumber) {
            case 1:
                highlightedShape = firstShape;
                break;
            case 2:
                highlightedShape = secondShape;
                break;
            default:
    
            }
    
            System.out.println("Check:" + highlightedShape.getBounds2D());
            System.out.println("Moving shape : " + highlightedShape);
            repaint();
    
            System.out.println("moving out");
    
        }
    }