i have a jrame on which i add some JComponent objects.
Each JComponent has a list of containers i add by using JComponent.add( Component).
Now in my main JComponent class, called MyComponent, i override the protected method paintComponent where i can paint things, which works pretty fine.
But i dont want to paint on the main JComponent, i only want to paint on the containers i have added to my main JComponent.
So in MyComponent in paintComponent i do the following.
protected void paintComponent( Graphics g) {
super.paintComponent( g);
Graphics page_g = this.getPage( "main").getGraphics();
page_g.setColor( Color.RED);
page_g.drawRect( 10, 10, this.getWidth() - 20, this.getHeight() - 20);
page_g.setColor( Color.BLUE);
page_g.drawString( "HELLO WORLD", this.getWidth() / 2, this.getHeight() / 2);
}
The line this.getPage( "main").getGraphics(); takes the Graphics object from one of my containers added to the MyComponents list of containers and of course to the main component list using JComponents add method. The container is set to visible by calling the setVisible( true); method.
But nothing happens. The screen is empty. When i replace page_g with g, then painting works, because its painting on my JComponent (MyComponent), but i want to paint on the container which is a children of MyComponent in this case.
I often heard "Never use getGraphics()". But how else can ONLY draw on sub components of a parent component when the parents paintComponent method gets called?
Really the best bet is to have the classes that are actually doing the custom painting override their own paintComponent() method. Let the AWT worry about the graphics contexts.
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 .!! :)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
currently I am creating a custom JFrame with a custom JPanel. Rendering the JFrame works without flickering, but when I am rendering a custom `JPanel the components flicker. My code:
My code for painting the JFrame:
public void paint(Graphics g) {
Dimension d = getSize();
Graphics og = g;
g = b.getGraphics();
//background
g.setColor(SConstants.ct.FRAME_BACKGROUND());
g.fillRect(SConstants.ct.FRAME_FRAME_WIDTH(), SConstants.ct.FRAME_FRAME_HEIGHT(), d.width - SConstants.ct.FRAME_FRAME_WIDTH(), d.height - SConstants.ct.FRAME_FRAME_WIDTH());
//frame
if(isFocused()){
g.setColor(SConstants.ct.FRAME_FOCUSED_FRAME());
}else{
g.setColor(SConstants.ct.FRAME_UNFOCUSED_FRAME());
}
//top
g.fillRect(0, 0, d.width, SConstants.ct.FRAME_FRAME_HEIGHT());
//left
g.fillRect(0, 0, SConstants.ct.FRAME_FRAME_WIDTH(), d.height);
//bottom
g.fillRect(0, d.height - SConstants.ct.FRAME_FRAME_WIDTH(), d.width, SConstants.ct.FRAME_FRAME_WIDTH());
//right
g.fillRect(d.width - SConstants.ct.FRAME_FRAME_WIDTH(), 0, SConstants.ct.FRAME_FRAME_WIDTH(), d.height);
//icon
if(getIconImage() != null)
g.drawImage(getIconImage(), 15, SConstants.ct.FRAME_FRAME_HEIGHT() / 2 - getIconImage().getHeight(null) / 2, null);
Graphics2D g2d = (Graphics2D) g;
g2d.setFont(SConstants.ct.FRAME_FONT());
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLACK);
g2d.drawString(getTitle(), d.width / 2 - g2d.getFontMetrics().stringWidth(getTitle()) / 2, SConstants.ct.FRAME_FRAME_HEIGHT() / 2 - g2d.getFontMetrics().getHeight() / 2 + g2d.getFontMetrics().getAscent());
getContentPane().paint(og);
og.drawImage(b, 0, 0, null);
}
I am setting the contentPane like this:
getRootPane().setLayout(null);
SPanel cp = new SPanel();
cp.setBounds(SConstants.ct.FRAME_FRAME_WIDTH(), SConstants.ct.FRAME_FRAME_HEIGHT(), getWidth() - SConstants.ct.FRAME_FRAME_WIDTH() * 2, getHeight() - SConstants.ct.FRAME_FRAME_HEIGHT() - SConstants.ct.FRAME_FRAME_WIDTH());
setContentPane(cp);
The SPanel is like this:
public void paint(Graphics g) {
paintComponent(g);
}
protected void paintComponent(Graphics g) {
//paintComponents(g);
for(Component c : getComponents())
c.paint(g);
}
I am rendering a SLabel like this:
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawString(text, getX() + SConstants.ct.LABEL_SPACING_HORIZONTAL(), getY());
}
public Dimension getPreferredSize() {
Rectangle2D d = getGraphics().getFontMetrics(SConstants.ct.LABEL_FONT()).getStringBounds(text, getGraphics());
int w = (int)(d.getWidth() + SConstants.ct.LABEL_SPACING_HORIZONTAL() * 2);
int h = (int)(d.getHeight() + SConstants.ct.LABEL_SPACING_VERTICAL() * 2);
return new Dimension(w, h);
}
Runnable example: https://drive.google.com/file/d/0B8czlxc22-xqOHlPdUZxV2dkMjQ/edit?usp=sharing
Anyone know why the SLabel is flickering?
You don't seem to have an understanding of how Swing works or how painting in Swing/AWT works...
SMain...
This...
public class SMain {
public static void main(String[] args) throws Exception {
//...
while(true){
sf.repaint();
Thread.sleep((int)(1000.0 / 24.0));
}
}
}
Is a bad idea...Swing uses a passive rendering model, meaning that updates occur when they are required, seen as you don't seem to be making changes to the state of the UI, this is just adding more pressure onto your system.
It could also, if called properly, prevent your UI from updating ever.
SFrame...
getRootPane().setLayout(null);
Why? Just why? The JRootPane has it's own layout management process that ensures that the contentPane is provided all the space within the containing frame minus space for the menu bar if provided, which you've not provided so, yeah, default implementation would allow the contentPane to occupy all the available space...
setSize may be called a number of times in a row, it would be better to simply invalidate b (setting it to null) and create it only when required, (when paint is called), but this begs the question of why?
Why not just allow the content pane to do this for you...
pack would automatically resize the contentPane, if you would actually let the JRootPane do it's job...
paint does a number of important jobs and you've circumvented most of them, this will cause you no end of problems and weridness....
Case in point, you don't translate the Graphics context before you try and paint the content pane, this means that it's possible for the contents of the content paint to paint over what you have painted (ie the title).
You would be better of using...
The content pane as it is, adding the title to the BorderLayout.NORTH position and the remaining content, wrapped in another container to the BorderLayout.CENTER position of default contentPane
Implementing your own JRootPane to provide the layout you want, using separate components for the title and content
Implementing your own Look and Feel delegate for the JFrame and painting the borders yourself
Using a EmptyBorder on the contentPane to provide space for the title...
Just about anything else then what you have done...
Beware, MouseListener and MouseMotionListener may be blocked by components higher in the container hierarchy which implement either or both of those interfaces
SPanel...
paint does a lot of important work, including, but not limited to, painting the component, border and components (as well as implementing double buffering), but you've decided to circumvent them all and do your own thing...
paintComponent is suppose to paint the background of the component, not it's children. This is already done by paint and paintChildren and paintComponents
A child component may also be painted independently of it's parent...
SComponent...
Beware, Component is a heavyweight (AWT) component and it's painting requirements are different from those of lightweight (Swing) components, and in fact, you should avoid adding heavy weight components to light weight containers....
Component already defines paint(Graphics)...
Heavyweight components are also not double buffered by default...this means that there updates can cause flickering...hence your problem...
"But I've double buffered the frame"...sorry to disappoint you, but components can be and are typically, painted independently of their parents, so they may not be painted as part of the parents update...
Conclusions...
You don't seem to have an understanding of how painting works in AWT/Swing. Take a look at Painting in AWT and Swing for more details...
You also don't seem to understand of the differences between Swing and AWT. While Swing sits on top of AWT, it has it's own framework requirements which differ from AWT and some significate ways...
You seem to be trying to implement you own framework...why, I have no idea, obviously the last 20+ years of development on Swing and AWT aren't good enough or something...
You might consider having a look at Modifying the Look and Feel and using the UI delegate API to implement what you are trying to do...
You have two choices, you can either, learn to understand how the current framework (Swing/AWT) works and work within it to achieve your goals or spend the rest of your life chasing weirdness, unexplainable problems and bugs.
You could also consider having a look at JavaFX which allows you to modify the look and feel via CSS...
I am writing a mapping application in Java, using Swing for the UI (I've included a prototype drawing at the end of this post). The map is implemented using a custom MapPanel class that extends JPanel. The map is fetched from the server as a series of 300x300 images (tiles) that are painted on the MapPanel whenever its paintMap() method is called. Due to the length and complexity (multiple classes, etc.) of the code I can't include all of it here but the paintMap() method basically works like this:
// Loop for each map tile (image, xPos, yPos)
getGraphics().drawImage(image, xPos, yPos, 300, 300, null);
I would like to have another JPanel (containing a JSlider for zoom control) overlayed on top of the map panel, but I'm having difficulties getting this to work. Whenever I paint the map, the overlayed JPanel disappears. If I repaint the overlayed JPanel in the paintMap() method, it flickers badly when the map is being dragged (and thus repainted continuously).
Any suggestions for how I could implement my UI without flickering?
getGraphics().drawImage(image, xPos, yPos, 300, 300, null); // WRONG!!
Don't call getGraphics() - instead override the paintComponent(Graphics) of a JPanel or JComponent and paint when requested to do so.
Use this as the ImageObserver
This article describes using JLayeredPane and an abstract base class to overpaint arbitrary effects on a given Swing component.
I want to draw a straight line in JFrame using Line2D.Double, also I want do it with constructor (not through method). How I must declare variable g like in example on docs.oracle.com?
public void paint (Graphics g) {
Graphics2D g2 = (Graphics2D) g;
...
}
Painting is always done in a method. In particular custom painting in Swing is done in the paintComponent() method of a Swing component like JComponent or JPanel.
Read the Swing tutorial on Custom Painting for a better explanation and examples.
1) I want to draw a straight line in JFrame using Line2D.Double, that not possible you have to put there JLabel, JPanel or plain JComponent, example here
2) if you want to paint directly to the JFrame, then you have to (use method paint()) to the GlassPane or RootPane
3) for Swing is there method painComponent(as mentioned both posters), not paint
The drawing/painting occurs event driven. That is when a part of the JFrame has to be (re-)drawn paint and paintComponent will be called.
You can merely add a component to the content pane of the JFrame in the constructor. That would then draw the line by overriding paintComponent.
You could create a JPanel, which has a:
List<Shape> shapes;
shapes.add(new Line2D.Double(...));
and then in paintComponent draw all shapes. But that is overdone.
You mean how to get a Graphics2D object from with a constructor? That is not the way to do it, Graphics2D is an interface and the implementation classes are not in the API. However, you can get a graphics object from any component with
(Graphics2D)component.getGraphics()
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.