aspose file tools*
The moose likes Swing / AWT / SWT and the fly likes Beginners question (Head First - TwoButtons) Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Soft Skills this week in the Jobs Discussion forum!
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Beginners question (Head First - TwoButtons)" Watch "Beginners question (Head First - TwoButtons)" New topic
Author

Beginners question (Head First - TwoButtons)

Vincent Hendriks
Greenhorn

Joined: Oct 19, 2010
Posts: 16
Hi, I'm currently trying to learn Java with the Head First book.
I made the TwoButtons program and it compiles and runs, but it does something odd... and I can't figure out why

It's pretty simple: there's a colored circle and a label.
There's a button to change the label and a button to change the color.

When I press the change color button, it changes color I can repeat that.
But when I press the change label button, it changes the label (only 1 time which is correct ) BUT... it also changes the color the first time I press the change label button (the time it actually changes the label)!?!?

I'm probably missing something... can you guys help me out?

pete stein
Bartender

Joined: Feb 23, 2007
Posts: 1561
The problem as I see it is with the code you're not showing us. I have a feeling that your MyDrawPanel component changes a circle's color whenever it is redrawn -- possibly whenever it's paintComponent method is called, and by changing the size of the JLabel, you're making the JFrame and its components repaint themselves. The solution is not to change color in paintComponent as you do not have full control on when this is called, but rather elsewhere. Give your MyDrawPanel a public method called changeColor() where it changes the color of the circle and then call repaint() on the MyDrawPanel.


Edit: Oh, and welcome to the Ranch!!!
Vincent Hendriks
Greenhorn

Joined: Oct 19, 2010
Posts: 16
Hi Pete,

I see what you mean and you are indeed correct. I'm not sure on how I should proceed though. This is the missing code (sorry about that...):



I don't think I can just toss out the paintComponent() because the program wouldn't make a circle to change the color of
As you said, I can add a changeColor(), but when I call repaint() on the MyDrawPanel, how does it know that it should execute the changeColor() instead of the paintComponent()?
Suhrid Karthik
Ranch Hand

Joined: Aug 31, 2008
Posts: 58

Alternatively you could use a flag variable to indicate that the panel must be repainted only when the user clicks on the change color button.

Something like this:



Vincent Hendriks
Greenhorn

Joined: Oct 19, 2010
Posts: 16
Suhrid Karthik: Alternatively you could use a flag variable to indicate that the panel must be repainted only when the user clicks on the change color button.


Unfortunately, that didn't work, since the thing was repainted everytime because the size of the WEST part of the panel changed due to the change of length of the text on the label. I didn't realise that in the beginning.

pete stein: The problem as I see it is with the code you're not showing us. I have a feeling that your MyDrawPanel component changes a circle's color whenever it is redrawn -- possibly whenever it's paintComponent method is called, and by changing the size of the JLabel, you're making the JFrame and its components repaint themselves. The solution is not to change color in paintComponent as you do not have full control on when this is called, but rather elsewhere. Give your MyDrawPanel a public method called changeColor() where it changes the color of the circle and then call repaint() on the MyDrawPanel.


I've tried a bunch of things and it didn't work out. I'm not familiar enough yet with the properties of JPanel, paintComponent(), changeColor(), Graphics etc. etc.
I'm still learning though, so hopefully one day ...

In the end I smuggled a bit and switched the SOUTH and WEST parts so that the size of the parts would not be affected by the text on the label
Both buttons worked correct then!
So, I didn't exactly solve the problem, but I understand where, why and how it went wrong. With that, my question is answered, so thanks to both of you!
pete stein
Bartender

Joined: Feb 23, 2007
Posts: 1561
Vincent Hendriks wrote:I see what you mean and you are indeed correct. I'm not sure on how I should proceed though. This is the missing code (sorry about that...):

I don't think I can just toss out the paintComponent() because the program wouldn't make a circle to change the color of
As you said, I can add a changeColor(), but when I call repaint() on the MyDrawPanel, how does it know that it should execute the changeColor() instead of the paintComponent()?


There's no need to throw out paintComponent, but since you can't absolutely control when it's called or not called, you should almost never put program logic in this method. What I would do would be to give MyDrawPanel a public void method, say changeColor() that creates a random color, just as you do in paintComponent, and then sets a private Color field, say Color circleColor to this new color, then calls repaint on itself. Then in the paintComponent method you set the color of Graphic object, in your method called "g" to circleColor via g.setColor(circleColor) and draw your oval. This way the randomization logic is outside of paintComponent. Then when you want to change the MyDrawPanel circle's color, simply call changeColor on the MyDrawPanel object.
Vincent Hendriks
Greenhorn

Joined: Oct 19, 2010
Posts: 16
I'm sorry Pete... I'm afraid, I'm gonna need a little more help on this one (it's a big leap from reading and understanding code to actually writing code from scratch).

I've taken the random part out of paintComponent and created changeColor, which I'm calling under the "change-the-color-of-the-circle-button".
Because the circleColor should be available to both methods, I made an instance variable of it.

It's not working though and I keep struggling with assigning a circleColor in paintComponent when it's painted initially and not when it's only changed because of the button event.

Can you point out the flaw in lines 48-64 (apart from the fact that this code is not doing the trick )?

pete stein
Bartender

Joined: Feb 23, 2007
Posts: 1561
Vincent Hendriks wrote:I'm sorry Pete... I'm afraid, I'm gonna need a little more help on this one (it's a big leap from reading and understanding code to actually writing code from scratch).

But it's in making that leap where you learn!

I've taken the random part out of paintComponent and created changeColor, which I'm calling under the "change-the-color-of-the-circle-button".
Because the circleColor should be available to both methods, I made an instance variable of it.

No you didn't. You made a static variable which you shouldn't do as it is also forcing you to make the changeColor method static. Do you understand why it is important for these to be true instance variables? For one thing, there's no "this" in a static method, so there's no way you can tell the JPanel to repaint from within the method as I have suggested.

It's not working though and I keep struggling with assigning a circleColor in paintComponent when it's painted initially and not when it's only changed because of the button event.

Can you point out the flaw in lines 48-64 (apart from the fact that this code is not doing the trick )?

It's because you're using static variables and methods in places where you should have instance variables and methods. Then you will need to call this method on the displayed object, not on the class. This isn't really a GUI problem but a basic OOPs programming issue.

Luck
Vincent Hendriks
Greenhorn

Joined: Oct 19, 2010
Posts: 16
No you didn't. You made a static variable which you shouldn't do as it is also forcing you to make the changeColor method static. Do you understand why it is important for these to be true instance variables? For one thing, there's no "this" in a static method, so there's no way you can tell the JPanel to repaint from within the method as I have suggested.


Actually, I had both variable and method not static first, but then the compiler told me that in line 42...

...the non-static method changeColor() could not be referenced from a static context...
I guess I reacted in the opposite direction by making the method (and therefore also the variable) static to please the compiler

...but a basic OOPs programming issue.


Haha, yep, but I'll just look at it from a positive point of view: the more OO mistakes I make in the beginning, the steeper the learning curve should become
I'm calling it a day now, tomorrow I'll have a fresh look.

Thanks!

Vincent
pete stein
Bartender

Joined: Feb 23, 2007
Posts: 1561
Vincent Hendriks wrote:
Actually, I had both variable and method not static first, but then the compiler told me that in line 42...

...the non-static method changeColor() could not be referenced from a static context...
I guess I reacted in the opposite direction by making the method (and therefore also the variable) static to please the compiler

Yep, you are trying to call the method from the class when you should be trying to call the method from an object of the class. And in fact, your TwoButtons class needs to have a MyDrawPanel variable that is initialized, displayed in the GUI, and used in the actionPerformed method for the method call that we're talking about.
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 40028
    
  28
Moving to our GUIs forum where we usually discuss painting.
Vincent Hendriks
Greenhorn

Joined: Oct 19, 2010
Posts: 16
Yep, you are trying to call the method from the class when you should be trying to call the method from an object of the class. And in fact, your TwoButtons class needs to have a MyDrawPanel variable that is initialized, displayed in the GUI, and used in the actionPerformed method for the method call that we're talking about.


Pete, I succeeded in writing a code that works!!!

I did what you said (I think) and to proove I actually understand it, I also randomized the text on the label.

Then, when that also worked (took me about an hour and a half, but definitely an hour and a half well spent), I compiled and ran my programme about 10 times and kept clicking the buttons.

(where's a proud-as-a-peacock Emoticon when you need one )

Would you mind taking a last look to see if I accidentally violated any coding conventions or best practices?

pete stein
Bartender

Joined: Feb 23, 2007
Posts: 1561
Vincent Hendriks wrote:
Pete, I succeeded in writing a code that works!!!

Congrats!


Would you mind taking a last look to see if I accidentally violated any coding conventions or best practices?


Yes, I do see one problem and have a few suggestions:
1) Your MyDrawPanel class's paintComponent method should probably not have that fillRect method call but instead should have a call to super.paintComponent(g). This will allow the underlying JPanel code to repaint itself and erase any previously rendered images.
2) If you are going to want your geometric images to look nice, in the paintCompnent method create a Graphics2D object by casting the Graphics object to Graphics2D and then call setRenderingHints(...) to turn on anti-aliasing. The constants to use for this are in the RenderingHints API, and the method definition is in the Graphics2D API. This will smooth out the "jaggies" in your oval.
3) I avoid calling setSize(...) or doing anything that sets absolute size or position of a JFrame or component. Rather, consider using setPreferredSize and call pack on the JFrame before setting it visible. I usually call pack, then setLocationRelativeTo(null), then setVisible(true)
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 40028
    
  28
Beware of long lines in code tags; they make the code hard to read. I have broken one long line in the last but one posting.
Vincent Hendriks
Greenhorn

Joined: Oct 19, 2010
Posts: 16
Yes, I do see one problem and have a few suggestions:
1) Your MyDrawPanel class's paintComponent method should probably not have that fillRect method call but instead should have a call to super.paintComponent(g). This will allow the underlying JPanel code to repaint itself and erase any previously rendered images.
2) If you are going to want your geometric images to look nice, in the paintCompnent method create a Graphics2D object by casting the Graphics object to Graphics2D and then call setRenderingHints(...) to turn on anti-aliasing. The constants to use for this are in the RenderingHints API, and the method definition is in the Graphics2D API. This will smooth out the "jaggies" in your oval.
3) I avoid calling setSize(...) or doing anything that sets absolute size or position of a JFrame or component. Rather, consider using setPreferredSize and call pack on the JFrame before setting it visible. I usually call pack, then setLocationRelativeTo(null), then setVisible(true)


Those are all sort of API related tips. As a beginner, I think I have to gradually expand my knowledge of the API, which is limited to only a few classes/methods right now.
I've only had a few glimpses in it until now but from what I've seen I think in a certain way it performs the function that a dictionary has in a "normal" language (well, not exactly, but sort of ).
I trust that knowledge of the API will come with experience.
For now, I'm starting Head First chapter 14 (about Swing). If there are Graphics in the code that is treated, I'll remember your tips

Thanks Pete!

@ Campbell Ritchie: I'll try to avoid the long stuff. Thanks for correcting.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Beginners question (Head First - TwoButtons)