Dynamic Graphics Object Painting
There are various strategies you might pursue for this.
- 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:- Get the graphics instance of the image and draw the new element.
- Dispose of the graphics object.
- Call
repaint()
on the label.
- 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:
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.
Comments
-
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 almost 12 yearsWow - 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 almost 12 yearsWon't follow the pastebin link. If it is short enough, add it as an edit.
-
TJ Biddle almost 12 yearsEdited. Out of curiosity - any reason why you won't follow a pastebin link?
-
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 almost 12 yearsI 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 almost 12 yearsAlso.. 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 almost 12 years1) 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 almost 12 yearsYeah - 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 almost 11 years@AndrewThompson : +1, what I was about to ask, you answered that under
Further Tips
section :-)