excuse me i search a lot to find how those 3 functions (paint, repaint, paintComponent) interact between them but i have no idea. Can you explain me exactly when they are called ( because sometimes java call it without me asking him) what they do exactly and what is the difference between them. Thank you
I am not sure about "paint", but I can explain the relationship between repaint() and paintComponent().
In my limited experience with java, the paintComponent() method is a method in the JPanel class and is a member of "swing".
The paintComponent() method handles all of the "painting". Essentially, it draws whatever you want into the JPanel usings a Graphic object.
repaint() is an inherited instance method for all JPanel objects. Calling [your_JPanel_object].repaint() calls the paintComponent() method.
Every time you wish to change the appearance of your JPanel, you must call repaint().
Certain actions automatically call the repaint() method:
Re-sizing your window
Minimizing and maximizing your window
to name a few.
IN SHORT paintComponent() is a method defined in JPanel or your own custom class that extends JPanel. repaint() is a method called in another class (such as JFrame) that eventually calls paintComponent().
here is an example:
public class MyPanel extends JPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
g.draw([whatever you want]);
...
...
}
}
public class MyFrame extends JFrame{
public MyFrame(){
MyPanel myPanel = new MyPanel();
myPanel.repaint();
}
}
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 class that extends JPanel. When the default constructor of this class is called it will set the JPanel's layout to null. An instance of this class is added to a JFrame which uses a BorderLayout. I then create another instance of this class (this time setting its size and position with setBounds) and add it to my first instance of this class, before adding the first instance to the JFrame. A Thread will periodically call repaint on the JFrame. This works fine until the JFrame is resized to it's minimum size. When I do this no components have their paintComponent method called as would be expected, but when I increase the size of the JFrame only the parent JPanel (the one whose layout is handled by the BorderLayout) has its paintComponent method called. The child JPanel's paintComponent method is not called until the JFrame reaches a considerable size (at least 300x300 pixels). Then oddly enough I can gradually resize the JFrame and both JPanels' paintComponent methods will be called until the JFrame reaches its minimum size again.
So the question is: Why is the paintComponent method of my second JPanel not called everytime it's parent JPanel's paintComponent method is called. This is paintComponent method of the class that extends JPanel, the class the implicated JPanels are instances of:
#Override protected void paintComponent(Graphics g) {
if (!scaleValid) {
scaleWidget();
}
if (this instanceof Updatable) {
((Updatable) this).onUpdate();
}
super.paintComponent(g);
g.drawImage(activeImage, getWidth(), getHeight(), this);
}
Similar problem here but didn't find an answer: Why is paintComponent() continuously and asynchronously being called without explicit repaint() call?
I have a JPanel that I am drawing onto
class DrawPanel extends JPanel {
final void paintComponent(Graphics g) {
super.paintComponent(g);
// some graphics drawing stuff
} }
and then adding this to a JScrollPane. However if I put a system.out.println() in the paintComponent method I can see its continuously being called. Any way to stop this? According to the link its possible due to the jpanel being covered
Generally paintComponent() does not paint continually. It get invoked occasionally when Swing determines it needs be painted.
If your method is being invoked continually then I can think of a couple of possible problems. You are:
manually invoking repaint()
changing a property of the component in the paintComponent() method which then automatically invokes repaint()
The paintComponent calls come from Swing's Event Dispatch Thread. It gets called everytime the component needs to be repainted.
If you resize the component or bring it back from minimized state, then it's repainted. Of course if you cover it with another component then the repainting will be called less. The other component will have a paintComponent method too though.
Nothing to worry about.
I'm currently able to draw rectangles, elipses, and lines in Java by means of adding a component that extends JComponent in which I modify a paintComponent method:
public class myComponent extends JComponent
{
public void paintComponent(Graphics g)
{
/* do simple draw stuff */
}
}
I also know how to have my class extend either JApplet or JPanel and then draw in a paint method:
public class myClass extends JPanel
{
public void paint(Graphics g)
{
/* do simple draw stuff */
}
}
But, both of these methods suffer from not allowing me to pass them parameters. In the case of a multiframe animated sprite, I could conceivably have some external variable that it reads to determine the frame number and then internally draws only the appropriate "sprite" contents based on that frame number, but I'd prefer to be able to pass the frame number to it directly. Unfortunately, not only do I not know where this is called from, I don't know where to the the Graphics g that it requires as input.
There may be a better way to accomplish what I want, to directly communicate with the draw routine to tell it to draw only what I want, whenever I desire, but I don't know how to accomplish this.
If such a method is possible, how would that be done?
If it is better to use the existing paint or paintComponent methods, how can I best pass additional information to them?
Apparently I wasn't clear in what I asked. I wish to have a component or other entity that has its own paintComponent or paint method, inside of which, based on either a frameNumber parameter that is passed to it, or apparently-more-likely, a class property such as frameNumber that it can access, the method determines which frame of a sprite to draw.
Importantly, though, I wish to be able to re-call paint or paintComponent to redraw the sprite when the frame number changes. My big confusion comes in not knowing how to re-call the method, which, to the best of my understanding, only is called when the frame is resized or otherwise redrawn.
So, how can I redraw my component/object/entity, frame-by-frame?
Firstly, don't override paint, use paintComponent.
Secondly, you need to define some kind of model which records the state of all the graphical objects. When paintComponent is called, you then need to render that state. Instead of trying to pass parameters to the paint methods, you should have a method which allows the paint methods to access the model (ie getModel) which is passed to the component at some earlier time.
Then the update engine would update the model and the component would paint that model
For an example ... Use a timer when a key is pressed
I could conceivably have some external variable that it reads to determine the frame number and then internally draws only the appropriate "sprite" contents based on that frame number
Your class would need to have some internal state, i.e. instance variables.
You can then inspect those within the paint method.
Think "member variables":
public class MyClass extends JPanel {
private final int frameNumber;
public MyClass() {
this(0);
}
public MyClass(int f) {
this.frameNumber = f;
}
public void paint(Graphics g)
{
if (this.frameNumber == x) {
/* do simple draw stuff */
}
}
}
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.