I'm writing an app in java using a JPanel to draw maps, I've used Landmass as a class, and the workspace (extends JPanel) uses multiple landmasses to represent each continent/island.
currently, the paintComponent runs a for loop over every Landmass every time I repaint(), which is everytime the mouse is moved. This seems inefficient as most Landmasses aren't changing, and is causing my program to slow down when too many Landmass objects are added. I want the landmasses to still be displayed by the graphics, I'm looking for a way to not redraw the ones I haven't edited.
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2= (Graphics2D) g;
zoom(g2);
int radius = zoomHandler.divZ(this.radius);
graphicsHandler.setRadius(radius);
...
if(!landmasses.isEmpty())
for(int i = 0; i < landmasses.size(); i++)
graphicsHandler.drawLandmass(g2, landmasses.get(i), i);
}
Where landmasses is the ArrayList<Landmass> that I'm using to store every Landmass.
To be clear, I never directly call the paintComponent, I only ever use repaint();
If anyone was wondering drawLandmas does:
public void drawLandmass(Graphics2D g, Landmass l, int index) {
int[] xPoints = new int[l.nodes.size()]; //setup two integer arrays to store the x and y coordinates of each point
int[] yPoints = new int[l.nodes.size()];
for(int j = 0; j < l.nodes.size(); j++)//and assign each of the arrays with the points we take from the nodes
{
xPoints[j] = l.nodes.get(j).x();
yPoints[j] = l.nodes.get(j).y();
}
fillLandmass(l, xPoints, yPoints,g);
if(l.getIsSelected()) {
traceLandmass(l, xPoints, yPoints, g);
boxLandmass(l, w.zoomHandler, g);
}
}
public void fillLandmass(Landmass l, int[] x, int[] y, Graphics2D g) {
g.setColor(l.getColor());
g.fillPolygon(x, y, l.nodes.size());
}
public void traceLandmass(Landmass l, int[] x, int[] y, Graphics2D g) {
g.setColor(Color.RED);
g.drawPolygon(x,y,l.nodes.size());
for(int j = 0; j < l.nodes.size(); j++)//and assign each of the arrays with the points we take from the nodes
{
if(l.getSelectedNode() == j)
circle(g, l.nodes.get(j), "fill", radius,g.getColor());
else
circle(g, l.nodes.get(j), "draw", radius,g.getColor());
}
}
public void boxLandmass(Landmass l, ZoomHandler z, Graphics2D g) {
g.setColor(Color.YELLOW);
g.drawRect(l.prox[0] - radius, l.prox[1] - radius,
l.prox[2]-l.prox[0] + 2*radius,
l.prox[3]-l.prox[1] + 2*radius);
g.fillRect(l.prox[0] - z.divZ(3) - radius, l.prox[1] - z.divZ(3) - radius, 2*z.divZ(3), 2*z.divZ(3));
g.fillRect(l.prox[2] - z.divZ(3) + radius, l.prox[1] - z.divZ(3) - radius, 2*z.divZ(3), 2*z.divZ(3));
g.fillRect(l.prox[2] - z.divZ(3) + radius, l.prox[3] - z.divZ(3) + radius, 2*z.divZ(3), 2*z.divZ(3));
g.fillRect(l.prox[0] - z.divZ(3) - radius, l.prox[3] - z.divZ(3) + radius, 2*z.divZ(3), 2*z.divZ(3));
if(l.centreNodeVisible)
circle(g,l.centreNode,"draw",radius, g.getColor());
}
I obviously can't just use an if(landmasses.get(i).isEdited()) statement to only draw landmasses which have been edited, and the unedited ones won't be shown.
I tried researching the paintComponent, but I couldn't find anything there that was helpful.
As mentioned in the technical details Painting in AWT and Swing, the
Graphicsobject has a "clip rectangle" property, which indicates which area should be redrawn:It is used/set when you call the
repaint()method with the integer arguments to specify the region you need to repaint. So when you callthe
paint*()methods get called with aGraphicsobject, wheregetClip()will return aShape/Rectanglewith these coordinates (or the coordinates related to that GUI component). You can use this information to (re)draw only the parts in that area. But this also means that you have to callrepaint()on the area that has been changed. If you callrepaint()without arguments, you will give the Swing/AWT drawing library no information whatsoever what has been "changed" and what is "unchanged", resulting in repainting everything (because that's what you are requesting).See the following prototype to show the effect of
repaint(...);and how thepaintComponent()method is called:This will create a window like this:
The debug output looks as follow:
As you see in the output, the
paintComponent()method is called with a rectangle of the wholeJPanelsize first, since in the beginning it has to been drawn fully at least once. After that only the specific part should be drawn and thepaintComponent()method does that.However, keep in mind that the drawing system of Swing/AWT will decide really quick that is must redraw everything. GUI actions like focus switch or resize can result in the Swing/AWT system thinking "Well, what I am currently showing is not current anymore, I have to redraw everything" and calls
repaint()without any limits.