How to prevent JPanel from repainting everything? - java

Making a paint-like application that works by saving mouse points in an arraylist. My idea is to have a "points" arraylist with all the previously drawn stuff, and a "temp" arraylist to get and modify the current brush stroke the user just entered. This is necessary as the user can change the color and size, so my idea is to modify the current brush stroke based off what buttons have been pressed, then add that brush stroke to the rest of the picture. I searched around StackOverflow and found some code but cant get it to work how I want to (assuming I found the right code).
#Override
public void paintComponent(Graphics g1) {
super.paintComponent(g1);
final Graphics2D g = (Graphics2D)g1.create();
try {
g.setColor(brushColor);
for (Point point : tempArrayList){
g.fillOval(point.x, point.y, brushSize, brushSize);
}
} finally {
g.dispose();
}
The problem is that I need to clear the tempArrayList for the next brush stroke, which I can do when they change the color/size, but then it erases what was previously there. I am starting to think that I don't even need the "points" arraylist as descibed above because I was hoping that the g1 graphic would just save what the g graphic created.
I guess I just need to figure out how to add the g graphic to g1

Painting is controlled by the Swing API, there is a expectation that whenever paintComponent is called, you will repaint the entire state of the component (as part of your component might have been damaged due to some system event), so the short answer is, no, you can't...however....
You Could...
Paint to a BufferedImage instead and paint the BufferedImage when paintComponent is called
You Could...
Establish a series of "paintable" objects which contain information about what is to be painted and how it is to the painted, including the brush stroke, stroke color and fill color.
These would then be added to some kind of List and "painted" when the paintComponent method is called

Check out Custom Painting Approaches for exampled of painting from:
A List of objects
A BufferedImage.
The examples show how to draw Rectangles of different colours.

Related

Overiding AWT and storing graphics

-Hi all! I'm making a Java applet that simulates wave interference, which I have almost finished (will license under GPL). However, I have two questions regarding the AWT paint cycle that I am having difficulty finding answers to.
I want to make an 'about' overlay that appears when I press a button. The way I want to do this is to draw over the entire applet window with my static message and legend objects. The problem is stopping the AWT components from drawing themselves in the foreground without using remove(). Is there a way I can stop AWT from drawing itself temporarily?
For my standing waves mode I want to have node and anti-node markers calculated and drawn to a secondary graphics every time the standing wave reaches a maximum amplitude (all of which I can do myself), but drawn to the primary graphics (and thus displayed) every paint cycle. Could someone explain the steps to do so? I imagine it would involve creating a second graphics object, drawing to it once, then drawing it to the primary graphics every cycle.
If you are able to answer either of my questions I would be very grateful!
Cheers, Jack Allison
Responding to your first question:
You can't disable the paint()/paintComponent() method if you've inlcluded it in your code. If its there, it runs. However, you can create a flag so that only if the flag is true, the stuff gets drawn. Let me show you what I mean:
boolean flag;
...
public void paintComponent(Graphics comp) {
if (flag) {
Graphics2D comp2D = (Graphics2D) comp;
//drawing statements
}
}
public void actionPerformed(ActionEvent event) {
flag = true;
repaint();
}

How to show icons during drag and drop

I am working on some NetBeans platform app, and i am currently stuck on some detail in Visual Library. Ok, here is problem. I have Visual Editor for my app, with pallet, scene and everything works great, just there is a problem when i am dragging icons from pallet to scene. They are not displayed during drag event, i would like to create that effect, can someone help on this?
I do this in two phases:
1) Create a screenshot (an image) of a palette element. I create the screenshot lazily and then cache it within the view. To create the screenshot, you can use this snippet:
screenshot = new BufferedImage(getWidth(), getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE);// buffered image
// creating the graphics for buffered image
Graphics2D graphics = screenshot.createGraphics();
// We make the screenshot slightly transparent
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f));
view.print(graphics); // takes the screenshot
graphics.dispose();
2) Paint the screenshot on the receiving view. When the drag gesture is recognized, find a way to make the screenshot available to the receiving view or one of its ancestor (you could make it available on the frame or its content pane, depending on where you want to make the screenshot drag available) and paint the image within the paint method. Something like this:
a. Make the screenshot available:
capturedDraggedNodeImage = view.getScreenshot(); // Transfer the screenshot
dragOrigin = SwingUtilities.convertPoint(e.getComponent(), e.getDragOrigin(), view); // locate the point where the click was made
b. As the mouse is dragged, update the location of the screenshot
// Assuming 'e' is a DropTargetDragEvent and 'this' is where you want to paint
// Convert the event point to this component coordinates
capturedNodeLocation = SwingUtilities.convertPoint(((DropTarget) e.getSource()).getComponent(), e.getLocation(), this);
// offset the location by the original point of drag on the palette element view
capturedNodeLocation.x -= dragOrigin.x;
capturedNodeLocation.y -= dragOrigin.y;
// Invoke repaint
repaint(capturedNodeLocation.x, capturedNodeLocation.y,
capturedDraggedNodeImage.getWidth(), capturedDraggedNodeImage.getHeight());
c. paint the screenshot in paint method:
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.drawImage(capturedDraggedNodeImage, capturedNodeLocation.x,
capturedNodeLocation.y, capturedDraggedNodeImage.getWidth(),
capturedDraggedNodeImage.getHeight(), this);
}
Instead of calling repaint() and perform the painting in the paint() method, you could invoke paintImmediately() as the mouse moves but the rendering will be a lot poorer and you could observe some flickering, so I would not recommend that option. Using paint() and repaint() provides a better user experience and a smooth rendering.
If I hear you well, you are creating a graphical editor of some sort, with drag and drop of elements, and you want to create an effect during that drag and drop?
If so, you basically need to create a ghost of the object you're dragging and attach it to the move of the mouse. Easier said than done, of course, but you get the gist.
So what you need is to take the image of what you're dragging (it shouldn't be too much trouble) and move it according to the position of the mouse (think of substracting the relative position of the mouse cursor in the object you're dragging).
But I think that kind of code is available somewhere. I'd advise you to look that up :
http://free-the-pixel.blogspot.fr/2010/04/ghost-drag-and-drop-over-multiple.html
http://codeidol.com/java/swing/Drag-and-Drop/Translucent-Drag-and-Drop/
Hope that helps you!

How to delete lines that are drawn in GUI?

I've been doing a program that will paint lines in a JLabel with picture. after creating those lines, I want to delete those lines that I've drawn. For example, I want to delete the
d.drawLine(label.getGraphics(), 120,215,330,120);
Drawing does not work like that. Once you draw a line, it no longer exists as a line, just as a bunch of pixels that aren't functionally different from all the other pixels. There are however possible workarounds:
Redraw the line using the background colour (e.g. white). This only works if the line doesn't cover anything.
Make a Line class and keep a list of them. When you want to delete a line, remove it from the list, clear all lines, and then redraw all the lines in the list.
You can't delete anything on a GUI. All you can do is keep writing over the top of what's there. If you want to change a black line back to a white background, you can write a white line over the top.
I think you're confusing a canvas type implementation (Java) with a graphics DOM tree type implementation (SVG in browsers, for example).
In a DOM implementation you can remove the element and have the application work out what is the dirty region and to repaint any elements that have sections that fall within that dirty region (usually a rectangle).
In a canvas implementation like Java, you paint directly to a graphics object and once you've painted, the canvas doesn't know where you painted. Generally, you need to implement a mechanism to work out what is dirty and repaint your component in the affected areas. Such a mechanism is known a scene graph.
label.revalidate();
label.repaint();
...
public void paintComponent(Graphics g) { do not draw lines but the rest }
Drawing happens event based, so you put your drawing code into paintComponent or paint.
The redrawing can be triggered i.a. with a repaint.

Java 2D Graphics Rectangles

I'm attempting to make a Java Applet that will allow me to draw a graph data structure in a canvas. I will do this by clicking where I want to create nodes, and clicking the nodes to connect them. The problem is I cannot get the paint() method to behave correctly. I add new nodes to the graph (and squares on the canvas) inside the mousePressed(MouseEvent e) method using,
Graphics g = this.getGraphics();
g.setColor(Color.blue);
g.fillRect(e.getX(), e.gety(), 40, 40);
Everything works fine, until I resize the window, and then all the filled rectangles vanish. I overrided the paint method to just an empty method, but the same thing still happens. I can't add the fillRect commands inside paint() because I don't know what rectangles exist until the user interacts with it using the mouse.
How can I use g.fillRect() inside the mouse listener methods and make them stick?
The problem is the place you're drawing to isn't persistant. At any moment, you can lose everything you've drawn to it. The paint(Graphics) method is called when this happens. You'll either need to repaint the entire picture every time this happens, or you'll need to set aside a canvas to draw to and copy the contents to your applet's Graphics as needed.
Here's how to create and draw to an image:
http://java.sun.com/docs/books/tutorial/2d/images/drawonimage.html
Then, in your paint method, use your Graphics' drawImage(...) method to display the image you've created.
I don't know if I'm reading this correctly, but why not just store the location of the last click in a variable to be painted later, when the paint() method is called?
You've got to override the window resize action listener and call repaint inside of it.
The Graphics is temporary. When a region gets dirty, it will be repainted.
The best way is to create a BufferedImage, paint to it on mousePressed and call repaint.
When paint is called, draw the Image onto the passed Graphics Object. This way you don't need to store the Rectangles and you got a buffer which will improve performance.

How can I draw something on a jPanel that will not be repainted?

How can I draw something in JPanel that will stay the same and not be repainted, I am doing a traffic simulation program and I want the road to be drawn once because It will not change.
Thanks
I'm not sure you actually want your road to never be repainted - repaint events fire (for example) when your window is resized, or when it becomes visible following another window obstructing it. If your panel never repaints then it'll look peculiar.
As far as I remember, Swing will only fire appropriate paint events for these circumstances, so you should be OK following the usual method of subclassing JPanel with a suitable override:
public class RoadPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// your drawing code here
}
}
If you cache your road into an image or another graphics format (to save calculating the display data multiple times) once drawn once, this might save you some time on subsequent paints.
To my knowledge, no, unless there is a trick with transparent overlays.
Most graphical applications I saw (and did) just re-draw the whole panel on each repaint. Now, you can do that once, in a graphic buffer, and then just paint the whole background at once, quickly, by copying the graphic buffer to the JPanel. It should be faster than calling all graphical primitives to draw the road.
Or, the way some 2D games do, perhaps paint it once and update the moving parts, like sprites: it needs to erase the old place used by the sprites (restore the background there) and re-draw the sprites at the new place. So you still have a copy of the road in a graphic buffer but instead of re-drawing it whole each time, you update only some small parts. Can be slightly faster.
The component will need to be repainted every time that the panel is obscured (ie frame minimized/another window put on top). Therefore drawing something only once will not work as you want it to. To make parts that do not change be drawn more efficiently you can draw them once to a 'buffer' image, and then just draw this buffer each time that the panel or component needs to be redrawn.
// Field that stores the image so it is always accessible
private Image roadImage = null;
// ...
// ...
// Override paintComponent Method
public void paintComponent(Graphics g){
if (roadImage == null) {
// Create the road image if it doesn't exist
roadImage = createImage(width, height);
// draw the roads to the image
Graphics roadG = roadImage.getGraphics();
// Use roadG like you would any other graphics
// object to draw the roads to an image
} else {
// If the buffer image exists, you just need to draw it.
// Draw the road buffer image
g.drawImage(roadImage, 0, 0, null);
}
// Draw everything else ...
// g.draw...
}
What I do is set a boolean value to whether or not a certain part needs to be redrawn. Then, in the paintComponent() method I can check the value and redraw the certain thing, or not.
protected void paintComponent(Graphics g){
super.paintComponent(g);
if (drawRoad) {
drawRoadMethod(g);
}
drawTheRest(g);
}
Kinda like that.

Categories

Resources