Dynamic Graphics Object Painting

16,851

There are various strategies you might pursue for this.

  1. If the objects are never removed from the drawing once done, use a BufferedImage, put it in a (ImageIcon in a) JLabel. When it comes time to update:
    1. Get the graphics instance of the image and draw the new element.
    2. Dispose of the graphics object.
    3. Call repaint() on the label.
  2. Keep a list of the drawn elements. In the paint method, paint them all. When a new element is added, call repaint() on the rendering component.

Here is an example of the 1st technique:

MyCanvas

import java.awt.image.BufferedImage;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;

public class MyCanvas
{
    JLabel view;
    BufferedImage surface;
    Random random = new Random();

    public MyCanvas()
    {
        surface = new BufferedImage(600,400,BufferedImage.TYPE_INT_RGB);
        view = new JLabel(new ImageIcon(surface));
        Graphics g = surface.getGraphics();
        g.setColor(Color.ORANGE);
        g.fillRect(0,0,600,400);
        g.setColor(Color.BLACK);
        // Keep this until I figured out if it's painted on load or not.
        g.drawLine(10, 20, 350, 380);
        g.dispose();

        ActionListener listener = new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                addNewElement();
            }
        };
        Timer timer = new Timer(200, listener);
        timer.start();
    }

    public void addNewElement() {
        boolean drawArc = random.nextBoolean();
        int x = random.nextInt(60);
        int y = random.nextInt(40);
        Graphics g = surface.getGraphics();
        if (drawArc) {
            g.setColor(Color.BLUE);
            int xx = random.nextInt(60);
            int yy = random.nextInt(40);
            drawArc(x,y,xx,yy,g);
        } else {
            drawNode(x,y,g);
        }
        g.dispose();
        view.repaint();
    }

    public static void main(String[] args)
    {
        MyCanvas canvas = new MyCanvas();
        JFrame frame = new JFrame();
        int vertexes = 0;
        // Change this next part later to be dynamic.
        vertexes = 10;
        int canvasSize = vertexes * vertexes;
        frame.setSize(canvasSize, canvasSize);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(canvas.view);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public void drawNode(int x, int y, Graphics g)
    {
            // Treat each location as a 10x10 block. If position 1,1 then go to (5,5) - If position 3,5 then go to (25, 45) eg: (x*10)-5, (y*10)-5
            int xLoc = (x*10) - 5;
            int yLoc = (y*10) - 5;
            g.setColor(Color.white);
            g.fillOval(xLoc, yLoc, 8, 8);
            g.drawOval(xLoc, yLoc, 8, 8);
    }

    public void drawArc(int x, int y, int xx, int yy, Graphics g)
    {
            int xLoc = (x*10) - 5;
            int yLoc = (y*10) - 5;
            int xxLoc = (xx*10) - 5;
            int yyLoc = (yy*10) - 5;
            g.drawLine(xLoc, yLoc, xxLoc, yyLoc);
    }
}

Further tip

You might notice that the lines look quite 'jagged' & ugly. Both the BufferedImage or a JComponent has access to the more useful Graphics2D object (for the JComponent it is necessary to cast it in paintComponent()). A Graphics2D instance accepts rendering hints that can be used to smooth (dither) the elements drawn.

Share:
16,851
TJ Biddle
Author by

TJ Biddle

Lead DevOps Engineer for RateBeer.com @ ZX-Ventures

Updated on June 04, 2022

Comments

  • TJ Biddle
    TJ Biddle almost 2 years

    Trying to figure out the best way to do this (And without crossing any specifics DO NOTs that I don't know about).

    I'm working on visually displaying a graph (Various nodes, with edges connecting them) with circles and lines to represent such. Each node will be added during runtime and I can't hardcode this. From what I understand, all painting needs to be done in the paint(Graphics g) method - which isn't that helpful, since I can't be change the parameters and it seems this is only called during the initial creation?

    Right now I was thinking about having it call various other methods, passing the Graphics object, and depending on other variables - I'll decide whether that's what I even want to call (Since the paint() method is the only one I can call).

    Am I going about this completely wrong? Never bothered with this before.

    To give you a better idea of what I want to end up with: I want to be able to pass the coordinates of the shape I want to add for the node, and then add it to whatever I have on the graph so far. And then same with the edges, I want to be able to pass the beginning and end point of the line to repaint on top of whatever is existing at that time.

    Not exactly what I want right now - but you'll get the idea from what I patched together so far:

    import java.awt.*;
    import javax.swing.*;
    public class MyCanvas extends Canvas
    {
        public MyCanvas()
        {
        }
        public void paint(Graphics graphics)
        {
            // Keep this until I figured out if it's painted on load or not.
            graphics.drawLine(10, 20, 350, 380);
        }
        public static void main(String[] args)
        {
            MyCanvas canvas = new MyCanvas();
            JFrame frame = new JFrame();
            int vertexes = 0;
            // Change this next part later to be dynamic.
            vertexes = 10;
            int canvasSize = vertexes * vertexes;
            frame.setSize(canvasSize, canvasSize);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(canvas);
            frame.setVisible(true);
        }
        public void drawNode(int x, int y, Graphics g)
        {
                // Treat each location as a 10x10 block. If position 1,1 then go to (5,5) - If position 3,5 then go to (25, 45) eg: (x*10)-5, (y*10)-5 
                int xLoc = (x*10) - 5;
                int yLoc = (y*10) - 5;
                g.setColor(Color.white);
                g.fillOval(xLoc, yLoc, 8, 8);
                g.drawOval(xLoc, yLoc, 8, 8);
        }
        public void drawArc(int x, int y, int xx, int yy, Graphics g)
        {
                int xLoc = (x*10) - 5;
                int yLoc = (y*10) - 5;
                int xxLoc = (xx*10) - 5;
                int yyLoc = (yy*10) - 5;
                g.drawLine(xLoc, yLoc, xxLoc, yyLoc);
        }
    
    }
    

    Edit: (Response for Andrew)

    import java.awt.*;
    import java.awt.image.BufferedImage;
    import javax.swing.*;
    public class MyCanvas extends JPanel
    {
    
        public MyCanvas() {
    
        }
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
        }
    
        public static void main(String[] args)
        { 
            int vertexes = 0;
            // Change this next part later to be dynamic.
            vertexes = 10;
            int canvasSize = vertexes * vertexes;
    
            JFrame frame = new JFrame();
            JLabel label = new JLabel();
            BufferedImage bImage = new BufferedImage(canvasSize, canvasSize, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = bImage.createGraphics();
            g2d.drawLine(50, 50, 300, 300);
            ImageIcon iIcon = new ImageIcon(bImage); 
            label.setIcon(iIcon);
            frame.add(label);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            g2d = drawNode(1,1,g2d);
            label.repaint();
        }
    
        public static Graphics2D drawNode(int x, int y,Graphics2D g2d)
        {
                // Treat each location as a 10x10 block. If position 1,1 then go to (5,5) - If position 3,5 then go to (25, 45) eg: (x*10)-5, (y*10)-5 
                int xLoc = (x*10) - 5;
                int yLoc = (y*10) - 5;
                g2d.setColor(Color.white);
                g2d.fillOval(xLoc, yLoc, 8, 8);
                g2d.drawOval(xLoc, yLoc, 8, 8);
                return g2d;
        }
        public static void drawArc(int x, int y, int xx, int yy)
        {
                int xLoc = (x*10) - 5;
                int yLoc = (y*10) - 5;
                int xxLoc = (xx*10) - 5;
                int yyLoc = (yy*10) - 5;
               // g.drawLine(xLoc, yLoc, xxLoc, yyLoc);
        }
    
    }
    
  • TJ Biddle
    TJ Biddle almost 12 years
    Wow - okay you literally edited it 12 seconds ago, but was just going to ask if this is what you meant: pastebin.com/tsgVeshn I'll read over what you said real quick though.
  • Andrew Thompson
    Andrew Thompson almost 12 years
    Won't follow the pastebin link. If it is short enough, add it as an edit.
  • TJ Biddle
    TJ Biddle almost 12 years
    Edited. Out of curiosity - any reason why you won't follow a pastebin link?
  • TJ Biddle
    TJ Biddle almost 12 years
    @trashgod - Are you making a comment about having posted the pastebin link? You can copy the raw code from the pastebin as well (Plus get some pretty syntax highlighting :p)
  • Andrew Thompson
    Andrew Thompson almost 12 years
    I usually will, though I'm feeling lazy today. In general though, people feel that if code is short enough to post to SO - they will look at it, if not.. Also a) some people fear 'foreign links' (OK - not so much with pastebin) & b) If pastebin disappears, we will have the code for future reference. It is better if a question is 'self contained' with 'no external dependencies'. c) See also trashgod's point.
  • Andrew Thompson
    Andrew Thompson almost 12 years
    Also.. I don't quite 'see' the point of the latest code. It seems to be heading in the right direction, but stops short of doing anything (pretty or) useful.
  • Andrew Thompson
    Andrew Thompson almost 12 years
    1) See also the 'further tip' in an edit. 2) I suggest you base any further code examples using the label technique, on my source (if only for the fact it 'does something').
  • TJ Biddle
    TJ Biddle almost 12 years
    Yeah - It doesn't do anything at the moment, ha - Just added what was necessary to get the visual so I knew I was doing things correctly. And as regards to pastebin, okay that makes more sense - I was thinking you were having an issue with foreign links, like you said - which surprised me since it was pastebin. But awesome - thanks for all of the help!
  • nIcE cOw
    nIcE cOw almost 11 years
    @AndrewThompson : +1, what I was about to ask, you answered that under Further Tips section :-)