I am trying to make a gradient effect on an AWT component using a series of lines drawn on its java.awt.Component.getGraphics() using the java.awt.Graphics.drawLine() method, but this never stays painted. Does anyone know how to commit these graphics to the component so they stay through resizing and other events that invoke repainting?
Are you limited to AWT components? If not I suggest you go with Swing components (the ones starting with "J". For custom painting you override the paintComponent method, for example:
import java.awt.Graphics;
import javax.swing.JPanel;
public class CustomPaintingComponent extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g); // This will clear everything...
// ... now you can apply your custom painting, for example:
g.drawLine(10, 10, getWidth() - 10, getHeight() - 10); // A line from upper left to lower right corner
}
}
you need to override the paint method of the component. Otherwise, if you just use a graphics object and write lines to it a single time, the next time the component paint method is called, it will redraw over all your changes.
Related
This is The Code :
public void paint(Graphics g) {
g.fillOval(x, y, 25, 25);
repaint();
}
This will create an output like This where if i move the pointer it doesn't clear the previous Graphics and creates a Path .
Output of Above Code
And by adding the super statement like this it Doesn't show the Path but only the Current location of the Oval Graphic.
public void paint(Graphics g) {
super.paint(g);
g.fillOval(x, y, 25, 25);
repaint();
}
The output is This
I just want to the Mechanism and The Reason Behind This.
Sorry, but there is so much wrong with your code, that we have to fix it:
First off, don't override a JComponent's (or JFrame's) paint method but rather override the JComponent's or JPanel's paintComponent. This way you avoid mistakingly messing up painting of borders or child components, both of which paint is responsible for. Also since paintComponent has automatic double buffering, your animation will be much smoother.
Next, you almost always do want to call the super's painting method, which for paintComponent is super.paintComponent(g) as this will continue to propagate the painting chain, whereas by not calling this you break this chain. Calling the super's method will call the component's housekeeping painting including the overpainting of "dirty" pixels which is why the trails are removed. This is likely the answer that you're looking for.
Most important never call repaint() from within any painting method. Doing this results in a poor-man's animation that is completely uncontrollable, and puts too much responsibility on the painting method which should focus on its sole job -- to paint the component. Use a game loop such as a Swing Timer instead.
Most important -- read the tutorials as most of this is already explained there:
Lesson: Performing Custom Painting: introductory tutorial to Swing graphics
Painting in AWT and Swing: advanced tutorial on Swing graphics
Straight from the docs
public void paint(Graphics g)
Paints the container. This forwards the paint to any lightweight
components that are children of this container. If this method is
reimplemented, super.paint(g) should be called so that lightweight
components are properly rendered. If a child component is entirely
clipped by the current clipping setting in g, paint() will not be
forwarded to that child.
As he said that super.paint() cleans the dirty pixels. This says it all .!! :)
Here is simple example of drawing an oval.
public class SwingPainter extends JFrame{
public SwingPainter() {
super("Swing Painter");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().add(new MySwingComponent());
setSize(200, 200);
setVisible(true);
}
public static void main(String[] args) {
new SwingPainter();
}
class MySwingComponent extends JComponent {
public void paintComponent(Graphics g) {
System.out.println("paintComponent");
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(10, 10, 50, 50);
}
#Override
protected void paintBorder(Graphics g) {
System.out.println("Paint border");
super.paintBorder(g);
}
#Override
protected void paintChildren(Graphics g) {
System.out.println("Paint children");
super.paintChildren(g);
}
}
}
But in debug mode or adding some info to console before drawing (as in example), you can see that swing draws components twice.
paintComponent
Paint border
Paint children
paintComponent
Paint border
Paint children
I cannot understand why it happens, but I think it can affect performance in a difficult GUI.
The article Painting in AWT and Swing: Additional Paint Properties: Opacity suggests why: "The opaque property allows Swing's paint system to detect whether a repaint request on a particular component will require the additional repainting of underlying ancestors or not." Because you extend JComponent, the opaque property is false by default, and optimization is not possible. Set the property true to see the difference, as well as the artifact from not honoring the property. Related examples may be found here and here.
I agree with Konstantin, what you need to remember, the repaint manager is responding to any number of requests when the application starts, this typically includes the initial paint request when the window is shown and resized (there's two).
Try this. Wait till the application is running and resize the window. I'm sure you'll get more then a couple of repaint requests ;)
This works fine to me. In fact, even in debug mode the output was:
paintComponent
Paint border
Paint children
Please, bear in mind that in AWT and Swing components there are many methods (paint, paintBorder, paintChildren, paintComponent, repaint, and others) that are called via call-back, whenever the GUI engine finds suitable. That may vary from JVM to JVM or even from different execution sessions. They can also be triggered from the interaction to your program (if you minimize/maximize, for example). Or they may not, at all.
I have a JPanel added to a JViewport, and the panel has several other panels added to it. I'm trying to implement a dragging selection, where you can select more than one component by dragging the mouse. The only problem I'm facing is that the selection rectangle is being painted behind the components added to the main JPanel. How can I paint over the top of them?
My structure is as follows: JFrame -> ContentPane -> JLayeredPane -> JScrollPane -> JPanel -> JPanel [].
Design draft for college assignment:
As you can see, the rectangle is behind the other panels.
This is what I'm already doing (on a much simpler level obviously), and Swing paints the rectangle underneath the components added to it.
This is one case where you should override the paint() method of the panel and not the paintComponent() method. Then the custom painting will be done AFTER all the child components have been painted.
Use a Layered Pane:
http://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html
This allows you to create overlapping components.
Use a glass pane to handle the drag painting, and possibly events as well:
http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane
hot really sure what do you really needed and final effect, maybe is there two another ways painting to
1) GlassPane
2) Viewport
you can put that together, carrefully Insets to the visible Rectanle
Without seeing your actual code, it is difficult to say what you are doing wrong. However, I can still say what I would do:
Create a JPanel that represents the whole area where you want to draw, which — of course — contains every component.
Override that panel its paintComponents(Graphics) like this (EDITED, notice the s is now the last character from the method name):
#Override
public void paintComponents(Graphics g)
{ // ^
super.paintComponents(g);
// Draw your selection rectangle:
g.setColor(Color.RED);
g.drawRectangle(selectionRectangle);
}
Okay, this is what I've decided to do in the end:
I'm not sure if this is the best way to do it, but it seems to work okay.
Note: Using MigLayout.
In the constructor of the JPanel lying underneath the colored blocks.
...
this.add(new JPanel() {
#Override
public boolean isOpaque() {
return false;
}
#Override
public void paintComponent(Graphics g) {
if (dragShape != null) {
g.setColor(Colors.SECONDARY);
g.setStroke(new BasicStroke(2));
g.draw(dragShape);
}
}
}, "pos 0 0, width 100%, height 100%", 0);
...
Custom painting on top of Swing components is facilitated by JLayeredPane. This article describes an abstract base class that facilitates overpainting specific areas (like selection rectangles or component bounds).
I am making a program in which there is a square that changes its x and y positions when a key is pressed. The square moves but the the old square is still there. How do I remove/clear everything from a panel before I repaint it? Calling removeAll had no effect.
Presumably your code includes custom paintComponent() logic. The key thing to observe is what does your panel look like when you do not override paintComponent()? An empty (or cleared) panel:
Thus the solution is to invoke the paintComponent() method of the parent type on the panel, before executing your custom paintComponent() logic:
public class CustomPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // first draw a clear/empty panel
// then draw using your custom logic.
}
}
I think this should work.
g.clearRect (0, 0, panel.getWidth(), panel.getHeight());
Also, you could keep the old location of the square and just clear that rather than clear the whole background.
I'm trying to make a paint editor with Java in which I have a toolbar with the objects that I would like to paste in the canvas. I'm using Swing components to make the GUI, but when I looked for the way of making the canvas, I only found the class canvas from AWT.
Is there any way to make something similar to canvas with Swing? (for example, JPanel?) I have read that using the class canvas from AWT with a GUI made with swing won't work correctly, is that true?
In order to make a custom 'Canvas' in swing you usually write a subclass of a JPanel. Then, you must overwrite the protected paintComponent(Graphics g) method of JPanel.
In the paint method, you can call methods on the Graphics object to actually draw on the JPanel.
As always, the Java Tutorials have a great reference on this to get you started.
You'll probably want to make a subclass of JPanel and implement your own way of painting components you want to draw onto the panel.
The basic approach will probably be along the line of assigning a MouseListener to the subclass of JPanel, then implement painting functionality.
The basic idea may be something along the line of:
class MyCanvas extends JPanel implements MouseListener
{
Image img; // Contains the image to draw on MyCanvas
public MyCanvas()
{
// Initialize img here.
this.addMouseListener(this);
}
public void paintComponent(Graphics g)
{
// Draws the image to the canvas
g.drawImage(img, 0, 0, null);
}
public void mouseClicked(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
Graphics g = img.getGraphics();
g.fillOval(x, y, 3, 3);
g.dispose();
}
// ... other MouseListener methods ... //
}
The above example is incomplete (and not tested -- it definitely won't compile), but it gives an idea about how to implement a MyCanvas class in which a user can click on and draw circles.
The img object is used to hold the image of the canvas. The paintComponent method is used to paint the img object to the canvas. In the mouseClicked method, the Graphics object associated with img is retrieved in order to fillOval onto the image.
Since one the requirements is to paste images onto the canvas, it may be a good idea to hold some Images that you want to paste into the canvas. Perhaps something along the line of:
Image[] myImages; // Used to store images to paint to screen.
Then, in the routine to paint the image onto img stored in MyCanvas:
g.drawImage(myImage[INDEX_OF_DESIRED_IMAGE], 0, 0, null);
By using the drawImage method of the Graphics object, other Images can be drawn onto Images.
As for the question on AWT and Swing, yes, it is true that you do not want to mix components from the AWT and Swing, as they differ in the way they render GUI components. AWT is based on heavyweight components, meaning they native windowing for painting the GUI, while Swing is based on lightweight components, meaning the GUI is drawn by Java itself without using native components.
A good guide on the difference of AWT and Swing is provided in Painting in AWT and Swing article from Sun.
Simply subclass JComponent.
JPanel is an inappropriate class. It is often suggested as it appears to have setOpaque(true) invoked on it automatically. It's actually the PL&F which does that, and whether or not it actually happens is implementation and vendor dependent.
Canvas is a heavyweight component. That is to say that it is controlled by the underlying windowing system. The result is that it will typically be drawn over the top of Swing components, without respect to z-order or clipping (putting it in a scroll pane will give odd behaviour).
You might want to look at the Minueto API. It is a very simple to use graphics api, and you can combine the Java event listening with it to provide your drawing capability.
http://minueto.cs.mcgill.ca/