JButtons acting up in paintComponent() - java

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.

Related

Java - When designing a JFrame that will have a Canvas,

After calling a method with these two lines of code:
final Graphics canvasGraphics = screenCanvas.getGraphics();
canvasGraphics.fill3DRect(rectangleX, rectangleY, 500, 100, true);
The rectangle flashes when the program runs, and then disappears. However, when I put this same line in an anonymous inner class with my MouseListener:
screenCanvas.addMouseListener(new java.awt.event.MouseAdapter(){
public void mousePressed(MouseEvent event){
canvasGraphics.fill3DRect(rectangleX, rectangleY, 500, 100, true);
}
}
The rectangle stays there. What is causing this behavior?
first you have to know that the Graphics of a JComponent is changed (repainted) on every change (resizing, minimizing, moving ...), and this happen inside the method public void paintComponent(Graphics g).
in the first scenario the two lines of code are executed just once and they have an effect (you noticed the flash), but the the Component is painted very often on every change, so when launching your application the canvasGraphics is filled by the two lines of code, but just after that the method paintComponent is called , so it repaints the Graphics again and you changes are lost.
in the second scenario you are not drawing on the Graphics directly, the Graphics is changed once the mouse is clicked so the paintComponent get executed when the program launches and the window is shown, once the mouse is Pressed inside the Canvas your reimplemented method is called and the Graphics is changed but this time the paintComponent method will not ommit your changes because nothing is happening to the element.
a solution is to reimplement the paintComponent method on the wanted element to be sure that your changes is performed each time the elements get repainted.

Java - Flicker when using repaint()

Hello I i'm having random flicker every couple of seconds when running my program, it has a single image moving across screen. I'm using Graphics paint() method and repaint() in the thread's run() method. Here are the relevant parts of the code, i'll post entire code if necessary. Btw, pawns is an arraylist loaded with pawn objects, originally I had 5 threads for 5 images moving across but I tried with only 1 image and it still flickers so it's not that.
private BufferedImage helicopter;
helicopter = ImageIO.read(new File("white.png"));
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
for(count=0; count<pawns.size(); count++){
g.drawImage(helicopter, pawns.get(count).getX(), pawns.get(count).getY(), null);
}
g.setColor(Color.BLACK);
g.drawLine(350, 0, 350, 600);
}
public void run() {
while(true) {
randSleep = (int)(Math.random()*100);
randMove = (int)(Math.random()*2);
pawn.setX(pawn.getX()+randMove);
try{
Thread.sleep(40);
}
catch(Exception e) {
e.printStackTrace();
}
repaint();
}
}
On components with complex output, repaint() should be invoked with
arguments which define only the rectangle that needs updating, rather
than the no-arg version, which causes the entire component to be
repainted.
Swing's implementation of paint() factors the call into 3 separate
callbacks: paintComponent() paintBorder() paintChildren() Extensions
of Swing components which wish to implement their own paint code
should place this code within the scope of the paintComponent() method
( not within paint()).
source: Painting in AWT and Swing: Good Painting Code Is the Key to App Performance
You should notice in the source quoted and linked that repaint() (no arguments) will call update(), which by default clears the background before drawing. I suspect that this is the source of the flicker, when the component is cleared before calling paint().
If you are using Swing components you should not implement your own double buffering but instead use the functionality provided by Swing.
First try, calling a repaint with arguments to avoid update clearing the entire background. Or write an override for the update method. If that does not solve the problem next try putting your drawing code in the paintComponent method of a Swing component.

How do you update this custom paint in Java?

I have a GUI based game that is real time and uses a JPanel and JFrame. I currently have a overridden paintComponent which paints based on certain conditions in my code. For example:
protected void paintComponent(Graphics gfx)
{
super.paintComponent(gfx);
gfx.setFont(new Font("default", Font.BOLD, 18));
if (one)
{
//do something
}
else
{
// do something else
}
}
This works once depending on the condition. I have implemented a mouse listener that I'd like to use for the statements so that if someone clicks on a certain part it shows something else. I'd like to go back and run the paintComponent again so that it paints over top of the old one. Does paintComponent() already loop? If not, how can you run the method in a loop?
You can repaint by calling repaint(). You can do this in a loop by creating a timer to repaint it, or just call it when it needs to be updated (probably better if your code is simple enough)
EDIT: About when to use a timer:
The timer is not necessary. I used it in a game once because I was custom painting the entire window and there were dozens of always-changing things to paint, and calling render() every time would not make my CPU happy. For something simple, though, it is better to call repaint() only when something changes, because if nothing changed, you are wasting that much time repainting it.

Panels/Buttons and repainting headache

As a coursework piece for school, we had to come up with a self-defined project, which I chose a game idea I was originally going to write on actionscript but decided to push for it on Java. This was initially going well, with the game code working, the sprite animations were being handled fine and everything was being maintained within a game loop. That was until i began to try and add components such as buttons and labels to it.
Currently the structure of my game is as this( GameSystem[handles loop and redrawing] -- GameMenu [Simple intro screen with start game ] <---swaps between ---> GameContainer [holds information about the game] )
My loop inside the GameSystem updates all necessary objects, repaints and then tells all the objects to repaint after.
Game System:
/**/for(int i = 0; i < UpdateList.size();i++){
/**/UpdateList.get(i).Update(delta * 1.1); // tell list the element is an object that extends an interface
/**/}
/**/Game.repaint();
/**/#Override
/**/public void paint(Graphics g) {
/**/super.paint(g);
/**/Graphics2D g2d = (Graphics2D) g;
/**/g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
/**/ RenderingHints.VALUE_ANTIALIAS_ON);
/**/for(int i = 0; i < UpdateList.size();i++){
/**/UpdateList.get(i).paint(g2d); // tell list the element is an object that extends an interface
/**/}
/**/}
This is where it starts getting iffy, because the game system repaints it's self each loop, clearing the frame of previous stuff, the buttons which I add in my gamemenu which is created inside the game system get cleared and only reappear briefly when you mouse over them.
Game Menu:
//////////////////
// Start Button //
//////////////////
Buttons = new JPanel();
Buttons.add(Start_game = new JButton("Start Game"));
this.add(Buttons);
Start_game.addActionListener(new Load());
/**/}
///////////////////
//////////////////
//Paint sprite //
//////////////////
#Override
/**/public void paintComponent(Graphics g) {
/**///super.paintComponent(g);
/**/Buttons.paintComponents(g);
/**/}
////////////////////////////////////
At this point I've tried paint, paint component, revalidate() and a variety of other stuff I've gleamed from other posts but none have helped. The only way I've had the button appear visible is if I remove the call to repaint in the game loop. However this then impacts the game as none of the objects are repainted.
TL DR: How can I Handle repainting the various objects in my update list and refreshing the Gamesystem panel without making any components such buttons disappear? Sorry if this is a noobish question but this is my actual first time working with Java as I wanted to expand outwards from actionscript, if any more info is required i will gladly add to the post, thank you for your help.
The problem is that you are trying to handle the painting of the buttons differently than the painting of your game.
Game System
{
paint loop
{
painting Game
}
}
Components //managing the painting itself through paintComponent, trying to run at same time as Game System.
{
paintComponent
}
So in effect, you are wanting to switch one to the other. You could either use paintComponent() in the GameSystem instead of paint(), which manages disposing of the graphics and repainting for you (if it is already extending a jComponent)...
or you need to start managing the buttons' painting similar to how you do it for your game, instead of relying on paintComponent()

Java paint() Only Called Once From paintAll()

I have the following code being called:
while(true){
view.onTick();
trySleep(55);
}
The onTick() method is described as such:
public void onTick() {
mainFrame.paintAll(mainFrame.getGraphics());
}
Here is where I set up my JFrame and JPanels etc (mainFrame is a JFrame):
private void runProgramSetup(){
JPanel canvas = new JPanel();
canvas.setLayout(new BoxLayout(canvas, BoxLayout.Y_AXIS));
mainFrame.getContentPane().add(canvas);
//create the main game panel
mapPanel = new MapPanel(model, this);
mapPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MAP_PANEL_HEIGHT));
mapPanel.setBackground(Color.cyan);
//create the menu panel
menuPanel = new MenuPanel(model, this);
menuPanel.setLayout(new BoxLayout(menuPanel, 0));
menuPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MENUS_PANEL_HEIGHT));
//add the panels to the window
canvas.add(mapPanel);
canvas.add(menuPanel);
//make both panels visible
mapPanel.setVisible(true);
menuPanel.setVisible(true);
}
Now here is my problem. Everything repaints when repaintAll() is called EXCEPT mapPanel's overridden paint(Graphics g) method:
#Override
public void paint(Graphics g) {
transformedImages.transformAndStoreImages(model);
paintGrid(g);
paintScenery(g);
paintElements(g);
paintDraggedElement(g);
paintUIOverlay(g);
}
It is only called once. That is it. However, every other component continues to repaint. It is only mapPanel that paints once. Here is what is even more strange. I am running on Ubuntu and get this problem. The rest of my team is running on Macs and they do not have this problem. The only way I have been able to solve this is to replace onTick() with two paint calls:
public void onTick() {
mainFrame.repaint();
mainFrame.paintAll(mainFrame.getGraphics());
}
This is all that has worked for me. I need both calls. Neither works alone. I don't like doing this though obviously because of inefficiency.. :/
Any ideas?
Thanks!
you should be overriding JPanel's
paintComponent(Graphics g)
not paint
The reason mainFrame.repaint() forces the map to refresh is because repaint() calls repaint(0, 0, 0, width, height), which marks the entire mainFrame's area to be marked as "dirty" for the RepaintManager. This is designed this way on purpose, because you usually do not want to repaint every pixel in the JFrame just to update a component. So, in onTick(), when mainFrame.paintAll() is being called, my guess is that the mapPanel's area has not been marked dirty, so the RepaintManager skips it, to save processing time. If you are very sure that you want to repaint the whole mapPanel every time onTick() is called, the simplest way would be to call mapPanel.repaint() inside of your onTick() method. This will mark the whole mapPanel as dirty so it will be redrawn asap. Also, if your menuPanel is just using regular swing JComponents, you don't need to manually cause them to repaint, they will be repainted when their values change, if you are using the API correctly.
I know this is a kind of old question, but I figured I'd answer in case anyone else runs into something similar.

Categories

Resources