Search code examples
javaswingobjectpaintcomponent

Drawing on a canvas declared in another function


I have an application which has a swing user interface class, which has buttons that send variables to a canvas class like so;

public class createWindow extends JFrame implements ActionListener
{
    createCanvas canvas = new createCanvas(10, 10);
    JPanel mainPanel = new JPanel();
    public createWindow()
    {
        mainPanel.add(canvas, BorderLayout.CENTER);
    }
}

createCanvas is a class which declares a paintComponent;

public class createCanvas extends JPanel
{
    int xValue;
    int yValue;
    int xCoordinate;
    int yCoordinate;

    public createCanvas(int x, int y)
    {
        xValue = x;
        yValue = y;
    }

    public void setXCoordinate(int x)
    {
        xCoordinate = x;
    }

    public void setYCoordinate(int y)
    {
        yCoordinate = y;
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        drawGrid(g, this.xValue, this.yValue);
        g.fillArc(xCoordinate, yCoordinate, 6, 6, 0, 360);
    }

    public drawGrid(Graphics g, int xLength, int yLength)
    {
        //creates a grid that is xLength x yLength
    }
}

However, I also have a selection of Objects which I want to have a .draw() function, which can use the paintComponent in createCanvas. The problem is, of course, when I need to draw the node on the grid, I can set the coordinates, but how do I display the node on the canvas I declared in createWindow?

public class Node()
{
    int xCoordinate;
    int yCoordinate;

    //Suitable constructors, sets, gets
    public void draw()
    {
        createCanvas canvas = new createCanvas();
        canvas.setXCoordinate(this.xCoordinate);
        canvas.setYCoordinate(this.yCoordinate);
        canvas.repaint();
    }
}

So, I am wondering if there is a way for me to keep what I have drawn on the canvas in createWindow, as well as what I draw in my Object class.

Thanks.


Solution

  • What you want to do is have the draw method in your object take a Graphics argument. This Graphics object will be the same Graphics context in your paintComponent method. You can create the object in your JPanel class. Something like this

    public class Circle {
        int x, y;
    
        public Circle(int x, int y) {
            this.x = x;
            this.y = y;
        }
    
        public void drawCirlce(Graphics g) {
            g.fillRect(x, y, 50, 50);
        }
    }
    

    Then in you JPanel class

    public class CirclePanel extends JPanel {
        Circle circle = new Circle(100, 100);
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            circle.drawCircle(g);
        }
    }
    

    You can have a setter for the x and y in your Circle class. You should change them somewhere in your JPanel class then call repaint() afterwards. You could move it with the press of a key or you can animate it with a java.util.Timer. For example

    With a Timer

    public class CirclePanel extends JPanel {
        Circle circle = new Circle(100, 100);
    
        public CirclePanel() {
            Timer timer = new Timer(50, new ActionListener(){
                @Override
                public void actionPerfomed(ActionEvent e) {
                    circle.x += 10;
                    repaint();
                }
            });
            timer.start();
        }
    
        @Override
        protected void paintComponent(Graphics g) {
           super.paintComponent(g);
           circle.drawCircle(g);
        }
    }
    

    With a key binding

    public class CirclePanel extends JPanel {
        Circle circle = new Circle(100, 100);
    
        public CirclePanel() {
            InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED_IN_WINDOW);
            inputMap.put(KeyStroke.getKeyStroke("RIGHT"), "moveRight");
            getActionMap().put("moveRight", new AbstractAction(){
                public void actionPerformed(ActionEvent e) {
                    circle.x += 10;
                    repaint();
                } 
            });
        }
    
        @Override
        protected void paintComponent(Graphics g) {
           super.paintComponent(g);
           circle.drawCircle(g);
        }
    }
    

    With a button press

    public class CirclePanel extends JPanel {
        Circle circle = new Circle(100, 100);
    
        public CirclePanel() {
            JButton button = new JButton("Move Right");
            button.addActionListener(new ActionListener(){
                public void actionPerformed(ActionEvent e) {
                    circle.x += 10;
                    repaint();
                } 
            });
        }
    
        @Override
        protected void paintComponent(Graphics g) {
           super.paintComponent(g);
           circle.drawCircle(g);
        }
    }