I have a JPanel with a paintComponent() function. I'll call it once, then when the user clicks a different JButton, I'll set some flag and want to call this function again as it will do something slightly different after the flag is set.
So here's what I'm wondering: how do I clear the existing stuff from paintComponent? And to redraw, do I just call paintComponent again?
Currently I'm trying the following:
flag2 = true;
repaint(); //I expect (want) paintComponent to be called again
In paint component, I do stuff like:
if (flag2==true) {
g.drawRect(...);
} else {
g.drawLine(...);
}
But through testing it seems like there is something wrong with what I'm doing.
Thanks for any help.
When you change a property of the panel then you need to invoke:
panel.repaint();
to cause the component to be repainted.
Then the first statement in the paintComponent() method should be:
super.paintComponent(g);
This will paint the background so you can now do your custom painting.
If you need more help then post your SSCCE that demonstrates the problem.
To clear all previously drawn graphics, invoke g.clearRect(0, 0, getWidth(), getHeight()).
First, why not use an enum instead of a boolean?
enum Enum {
RECTANGLE,
LINE,
CIRCLE
}
Enum choice = RECTANGLE; //default to RECTANGLE
switch(choice) {
// case RECTANGLE, LINE, CIRCLE
}
With regards to your issue, can you answer my comments in your question?
I would suggest calling revalidate(); instead of repaint(). revalidate() needs to be called when changing the size / layout or when you add/remove objects onto your jpanel and will update all of it's children. From what I can tell, you're still using the same paint object tho but changing it's layout.
Related
JPanel.setBackground method does nothing (although opaque attribute is true) if super.paintComponent father method isn't being called.
I have read a lot of similar questions here about this issue, and in each one of them I've only found solutions without explanation that helped me to understand why setBackground method when written before adding the JPanel to JFrame changes the JPanel background color, while when the setBackground is written inside paintComponent nothing is changed (only when calling father's paintComponent method as already mentioned).
Is it somehow related to Graphics object?
I have tried to change JPanel's opaque attribute to true and using setBackground(COLOR.BLACK) in paintComponent() method I had overriden in a class that extends JPanel
paintComponent(Graphics g)
{
this.setOpaque(true);
this.setBackground(COLOR.BLACK);
}
I expect that the JPanel background color will be black
Instead, background color is the default one
Well, first of all if you're using paintComponent(Graphics g) method, the first line you need to have inside is: super.paintComponent(g) otherwise you're breaking the paint-chain.
This will allow the parent component to draw the default component, before any customizations you do to it. If you don't do it, well, is like having a drawing in a piece of paper, imagine a circle, then cutting that circle and then trying to paint the outside.
Here's a more in-depth answer to How does super.paintComponent(g) works
However I wouldn't write
this.setOpaque(true);
this.setBackground(COLOR.BLACK);
inside the paintComponent(...) method, as it gets called several times and you can't control when it will ever get called. I would put those lines in a constructor, unless you want to change it later in your program while it's being painted depending on the state of your program or a gradient maybe.
For this part:
why setBackground method when written before adding the JPanel to JFrame changes the JPanel background color
Honestly, I don't understand what you mean.
Why do you say that if i won't call super.paintComponent(),it will break the chain? It's still drawing all the shapes and lines i want using graphics object.
From the docs:
The JPanel has a UI delegate which performs the background painting for itself. You call it by using super.paintComponent(g) and we pass the Graphics component to prevent irrevocable changes such as Graphics.translate
Your JPanel knows how to paint its children, but requires some help to paint itself, and this help comes from its parent.
When I mentioned "break the paint chain" I didn't mean nothing would paint, but that you would get strange behaviors such as the one of the JPanel's background disappearing or not being set.
In addition,something weird happens if the argument i'm sending to setBackground method is a random color(using Random object). JPanel changing color very quickly although i'm not doing anything(not minimizing,not resizing,etc).Can you consider why?
As I said before, the paintComponent gets called several times and you don't have control over when it will be called, even moving your mouse or something else will trigger the panel to repaint.
I have a jPanel, which contains a number of sub-panels that can be dragged around. What I want to do, is to draw lines connecting some of those sub-panels together.
However, while it seems like this should be simple, it's proven very frustrating. The best I've gotten, is to override the paintComponent function in the original jPanel as such:
panCharDisplay = new javax.swing.JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintLines(g);
}
};
And then draw the lines as such:
public void paintLines(Graphics g) {
g.setColor(Color.BLUE);
for (Character c : characters) {
if (c.female && c.spouse != null) {
g.drawLine(c.display.getX(), c.display.getY(), c.spouse.display.getX(), c.spouse.display.getY());
}
}
}
This works in a sense, in that it does technically draw the lines, in the right place, the right color, and so on, but only if I scroll away from where the line should be, and then scroll back. Whenever I drag a component around it causes weird graphics errors, as it draws only parts of the line and doesn't erase the ones before. The lines also show up below the sub-panels instead of over them, making them hidden a lot of the time.
I assume the reason for this is that I'm drawing the lines at the wrong time, and need to draw them after drawing the sub-panels, and also make sure that they are re-drawn every time the panels are dragged around.
Is there another place I can put in an override to make the lines show up more consistently? Another method I tried, was to make a class that extends JPanel, and try to use that to handle the drawing, but I couldn't get it to work at all.
Whenever I drag a component around it causes weird graphics errors, as it draws only parts of the line and doesn't erase the ones before.
You probably need to invoke repaint() on the panel's parent as you drag the panel around.
The lines also show up below the sub-panels instead of over them, making them hidden a lot of the time.
You should be able to override either the paint() or paintChildren() method instead of the paintComponent() method. Whichever method you override make sure to invoke super.XXX() first so that the default painting is done before you attempt to draw your lines.
Personally I like the lines painted below the component as is demonstrated in trashgod's GraphPanel example. The example does custom painting for the shapes, but I would guess the logic would be similar for the components.
You can get this JConnector project and use as is or adapt the sources as you need.
I am sorry about the non-descriptive title but I am not sure how to communicate the problems. For starters the JButtons every now and again are creating themselves multiple times in the same order that the loop should create them. Another issue I am having is that when I reposition them using the setLocation() method it creates new JButtons where I want them but also leaves the old ones where they are. I don't know if I just have to refresh the graphics or what is going on. Thanks for the help.
the array playerHand()is defined in the Player class and is 5 in length.
public void paintComponent(java.awt.Graphics g){
setBackground(Color.GREEN);
// create a Graphics2D object from the graphics object for drawing shape
Graphics2D gr=(Graphics2D) g;
for(int x=0;x<Player.hand.size();x++){
Card c = Player.hand.get(x); //c = current element in array
c.XCenter = 30 + 140*x;
c.XCord = c.XCenter - 30;
c.YCord = 0;
//5 pixel thick pen
gr.setStroke(new java.awt.BasicStroke(3));
gr.setColor(Color.black); //sets pen color to black
gr.drawRect(c.XCord, c.YCord, c.cardWidth-1, c.cardHeight-1); //draws card outline
gr.setFont(new Font("Serif", Font.PLAIN, 18));
gr.drawString(c.name, c.XCord+10, c.YCord+20);
gr.drawString("Atk: ", c.XCord+10, c.YCord+60);
gr.drawString(""+c.attack, c.XCord+60, c.YCord+60);
gr.drawString("Def: ", c.XCord+10, c.YCord+80);
gr.drawString(""+c.defence, c.XCord+60, c.YCord+80);
gr.drawString("HP: ", c.XCord+10, c.YCord+100);
gr.drawString(""+c.health, c.XCord+60, c.YCord+100);
JButton button = new JButton(c.name);
button.setSize(c.cardWidth, c.cardHeight);
//button.setLocation(c.XCord, c.YCord);
this.add(button);
repaint();
}
} //end of paintComponent
There are several problems (marked by "!!!!" comments) in your method below:
public void paintComponent(java.awt.Graphics g){
setBackground(Color.GREEN); // !!!!
Graphics2D gr=(Graphics2D) g;
for(int x=0;x<Player.hand.size();x++){
// .... etc ....
JButton button = new JButton(c.name); // !!!! yikes !!!!
button.setSize(c.cardWidth, c.cardHeight);
//button.setLocation(c.XCord, c.YCord);
this.add(button); // !!!! yikes !!!!
repaint(); // !!!!
}
}
Do not add components to GUI's in paintComponent(...). Never do this. Ever.
This method should be for drawing and drawing only and never for program logic or GUI building.
You do not have full control over when or if it will be called.
It needs to be as fast as possible so as not to make your program poorly responsive.
Avoid object creation within paintComponent(...) if possible.
Avoid file I/O in this method (though not a problem with your code above).
Don't call setBackground(...) in this method. Do that in the constructor or other method.
Never call repaint() from within this method.
Don't forget to call the super's paintComponent(g) method in your override.
To go over your problems:
For starters the JButtons every now and again are creating themselves multiple times in the same order that the loop should create them.
This is because you do not have control over when or how often paintComponent is called. The JVM may call it in response to information from the operating system about a dirty region, or the program may request a repaint, but for this reason, program logic and gui building should never be in this method.
Another issue I am having is that when I reposition them using the setLocation() method it creates new JButtons where I want them but also leaves the old ones where they are.
Quite likely you're seeing image remnants from your not calling the super's paintComponent method. By not doing this, you don't have the JPanel repaint itself and remove old image pixels that need to be replaced or refreshed.
I don't know if I just have to refresh the graphics or what is going on.
No, you need to change just about everything.
It looks like you have to re-think your GUI's program flow and logic and re-write a bit of code.
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 have a simple animation program which moves some basic shapes on the Jframe. However, the program does not really move the shapes, but creates more instead. In other words, I need to force the Jframe to clean up the previous object. How to do so?
Have a look at this previous post.
Custom painting is done by overriding the paintComponent() method of a JPanel. The basic code is:
protected void paintComponent(Graphics g)
{
super.paintComponent(g); // this is what clears the screen.
// paint your shapes here
}
Then you add the panel to the frame.
Read the section from the Swing tutorial on "Custom Painting" for more information.