Paint and PaintComponent Methods in JPanel - java

I am having trouble in understanding the difference between the paint and the paintComponent methods defined in the JPanel class. To put you in context, I am trying to draw a chess table; the following is part of the code I have been working on.
package main;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Color;
public class Panell extends JPanel {
public Panell() {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(100, 100, 400, 400);
}
#Override
public void paint(Graphics g){
boolean white=true;
for(int y=0;y<8;y++){
for(int x=0;x<8;x++){
if(white){
g.setColor(new Color(235, 235, 208));
}
else{
g.setColor(new Color(119, 148, 85));
}
g.fillRect(x*15, y*15, 15, 15);
white=!white;
}
white=!white;
}
}
}
One of my first questions is Are they runned automatically? Also, since they apparently do the same thing, which is to "paint", is it recommended to merge this two methods in one? Which would be the name of this one method?
I'm pretty new to Java and so any help will be very much appeciated. Thanks!
I have been following multiple tutorials online but any of them mentioned my question. I suppose it might be a really indepth topic, but I would really like to know the answer. I have tried everything that I have said in the question and I always end up painting one thing over the other, hence I asked myself if it would be better to only have one single paint method (by merging them), but by doing that, another question arises, Why would they implement another method to do exacly the same as another method?.

Are they runned automatically?
No. They are actually called by the windowing system when the Component needs to be repainted (i.e. first shown, resized, user calls repaint())
Also, since they apparently do the same thing, which is to "paint", is
it recommended to merge this two methods in one?
The difference between paint and paintComponent is that in Swing, JComponent implements paint to call paintBorder, paintChildren, and paintComponent in the proper (expected) order. So normally if you are subclassing a J-whatever in Swing, it is better to override paintComponent, not paint

Related

Why isn't my string being displayed in the window?

I am trying to create a simple application that shows a red circle that when clicked displays different messages under it. I believe that this part of the code:
g.drawString("DO NOT PRESS", 100, 100);
is coded correctly but no text is displayed on the window that opens. Here is the full code so far:
import java.awt.Graphics;
import javax.swing.JFrame;
public class BigRedButton extends JFrame {
public BigRedButton() {
setTitle("Big Red Button");
setSize(500, 500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void graphics(Graphics g) {
g.drawString("DO NOT PRESS", 100, 100);
}
public static void main(String[] args){
new BigRedButton();
}
}
There is no such method graphics in JFrame, so nothing is calling it.
You should avoid painting directly to top level containers, apart from everthing else, they're not double buffered and will flicker when painted. You should, instead, create a custom component (extending from something like JPanel) and override it's paintComponent method.
You should take the time to read through Performing Custom Painting, Painting in AWT and Swing and 2D Graphics
Also, while your reading up, you should have a read through Initial Threads
Amendment
As pointed out by Andrew, you should use the #Override annotation to ensure that the method you think you are overriding is actually the method being overridden in the first place. This would stop the program from being compiled and save lots of lost time trying to figure out why things aren't working the way you expect them.

Design: extend JPanel twice or pass JPanel as parameter?

I have design issue here. There two options which sound possible to implement but I don't like neither one.
I need to provide two functionality in my JPanel.
draw several lines
draw several strings
ads
Approach extend twice
If I would need only to draw lines then I can just copy-past solution from the following answer.
Draw a line in a JPanel with button click in Java
public class LinePanel extends JPanel {
...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
But since I need to add second functionality I would need to do something like this
public class LineStringPanel extends JPanel {
which would leave LinePanel just useless intermediate class.
Create two classes LineDrawer and StringDrawer and pass JPanel as parameter to some method which would perform drawing. The problem here that I would need to call which is not recommended.
Performing Custom Painting
Graphics g = panel.getGraphics();
What do you think and can recommend?
UPD:
Inside the JPanel there are numerious JLabels as child which represent units and traits.
The lines are relation between some traits of units and Strings are kind of notifications for some actions.
So I want to show String on the top(front) of child JLabels.
Also JPanel is inside JScrollPane.
If you need to draw strings, you can do so directly in the paintComponent method (and assuming you don't need anything too fancy, it's pretty easy):
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
g.drawLine(p1.x, p1.y, p2.x, p2.y);
...
g.setColor(Color.black);
g.drawString("My Text", 0, 20);
}
Then you could use your trashgod's solution with hardly any changes.
Remember that the coordinates are of the baseline of the text - so if you used 0,0 it wouldn't display on screen.

Why does swing draw simple component twice?

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.

Threading with Swing

basically, I have a program that has a class to create a basic GUI, and another class that extends Canvas. This Canvas class is added to a JPanel in the usual fashion myPanel.add(object). Now, in this class, I want to have methods to move objects such as rectangles.
My questions are these; is there a way to essentially have this JPanel or its added object (myCanvas) running on a separate thread?
Also, in the following method:
public void paint(Graphics g){
g.setColor(Color.black);
g.drawRect(0, 0, 50, 50);
}
Is there a way to have these operations split into multiple methods? I.e. multiple methods that draw to the Canvas?
Thanks in advance.
Don't use a Canvas is a Swing applicaton. Use a JPanel or JComponent and override the paintComponent() method. Also don't forget the super.paintComponent(g) at the start of the method.
See the section in the Swing tutorial on Performing Custom Painting for more information.
Store a list of drawable objects somewhere (perhaps your Canvas class, but I'd advise that being external to your logic...) and use your other thread(s) to update this list of objects.
Your drawing loop can simply clear your canvas (or at least areas that need to be redrawn) and draw those. Ideally your screen-render should be fast enough to facilitate a complete redraw, caching sub-sections as necessary.

How do I initialize a Graphics object in Java?

this is the code:
import java.awt.*;
import java.applet.*;
public class anim1 extends Applet{
public void paint (Graphics g)
{
g.drawString("",400,300);
}
public static void main(String ad[])
{
anim1 a=new anim1();
Graphics g1;
a.paint(g1);
}
}
It says that g1 is not initialized. But how do I initialize an abstract class?
Well there are two issues here 1:
Graphics g1;
a.paint(g1);
And you are getting the error that G1 is not initialized. That's because the variable g1 is never set to anything, and that causes a compile error. To get the code to compile, you would need to, at the very least do this:
Graphics g1 = null;
a.paint(g1);
However, that obviously won't help you out too much. You'll get a NullPointerException when you try to run your code. In order to actually cause your graphics to draw you need to this:
anim1 a=new anim1();
Graphics g1 = anim1.getGraphics();
a.paint(g1);
However, that still won't work because Anim1 will not appear on the screen. To get it to appear on the screen you need something like:
import java.awt.*;
import javax.swing.*;
import java.applet.*;
public class So1 extends Applet{
public void paint (Graphics g)
{
g.drawString("hello",40,30);
}
public static void main(String ad[])
{
JFrame jp1 = new JFrame();
So1 a=new So1 ();
jp1.getContentPane().add(a, BorderLayout.CENTER);
jp1.setSize(new Dimension(500,500));
jp1.setVisible(true);
}
}
Now notice, we don't actually call the paint() function ourselves. That's handled by the awt, which actually picks the graphics context, and calls our paint function for us. If you want, though, you can pass in any graphics object you want and ask it to draw on to that. (so if you want to draw your component onto an image, you can do it)
(note, I changed the classname from anim1 to So1)
An applet doesn't need a main method like a regular Java application does. I'd recommend starting with Sun's Applets Tutorial. In particular you may want to skip ahead to the section Life Cycle of an Applet to see how the Graphics object is handled within the applet.
All you need to do is simply remove the main method like so:
import java.awt.*;
import java.applet.*;
public class anim1 extends Applet {
public void paint (Graphics g) {
g.drawString("Hello",100,100);
}
}
instead of a call to paint(Graphics g) you need to invoke the repaint or update method. But for this your class must belong to the hierarchy in the java.awt.Container.
For your class you have overriden the Paint method and in the main you are trying to invoke the paint method. Instead of paint you need to invoke the repaint or update method (if your class is in the hierarchy of java.awt.Container) and the event dispatching system of java invokes your overridden paint method itself.
You should manipulate the graphics of a component within the paint method and invoke repaint() or update(), but not the paint method directly.
Start here for some more information.
You dont initialize a Graphics object.
You get the graphics object from a component via Component#getGraphics() method.
In your particular case I think repaint() is all you need!!
You don't, you use getGraphics, but, if you really want to initialize it, then type new Graphics(){}; And have fun filling all the abstract methods. Most of the times just putting code in paint(g) should do, you need to remember to set your applet visible, and usually, it should be the last line in your constructor or even outside it, I have made a mistake once where I made visible and then initialized a bunch of variables, it showed a black screen for a while.

Categories

Resources