JPanel's child JPanel is not always repainted with its parent? - java

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);
}

Related

my gui is taking too much resources in run time

I have a JFrame which contains a single panel.
In the panel I use the paintComponent method to resize its elements according the size of Jframe. The elements of the JPanel are an image as a background and 4 JLabel that cointains 4 ImageIcon and work like buttons. The method paintComponent of Jpanel is like below
public class MyPanel extends JPanel
{
//Declarations
private BufferedImage backGround;
public MyPanel()
{
//Some code here
}
public void paintComponent(Graphics graphics)
{
super.paintComponent(graphics);
Graphics2D graphics2d = (Graphics2D) graphics;
if(backGround != null)
{
graphics2d.drawImage(backGround, 0, 0, getWidth(), getHeight(), this);
}
/* This code is repeated 4 times because I have 4 labels */
label1.setSize(getWidth()/7 , getHeight()/10);
label1.setLocation(getWidth()/2 - getWidth()/14 , getHeight()/3 );
image1 = button1.getScaledInstance(label1.getWidth(), label1.getHeight(),
Image.SCALE_SMOOTH);
label1.setIcon(new ImageIcon(image1));
}
}
The frame has just a simple method , add(myPanel) so I did not write it here.
When I run the application , it takes me around 300 MB of ram and around 30% of CPU (Inter core i5-6200U) , which is quite unsual for me , expecially the amount of CPU. What is causing my application to take so much resources and is there any way I can reduce it ?
Whenever you repaint your component you change your labels' dimensions and create resources (the Image and the ImageIcon derived from it) and assign them as a new icon. These are changes to visible parts of your application and hence must cause repainting the components in question. Basically your paintComponent method
causes a repaint every time it is called effectively creating an endless loop and
is very heavyweight because it allocates expensive resources.
Both of these points are pretty bad ideas. Your paintComponent method should do just what the name suggests, i.e. painting the component. All actions that cause a repaint (changing icons or text, adding or removing components from the tree etc.) must not occur in it.
See also:
The API documentation on paintComponent(Graphics)
Painting in AWT and Swing
EDIT: When you want to resize components dependent on the size of other components create a ComponentListener and add it to the component you want to depend on by calling addComponentListener(ComponentListener). The ComponentListener instance will then have its componentResized(ComponentEvent) method called whenever the size changes.

Passing a JPanel to a method to draw rectangles

I have a class with a draw method that accepts a JPanel as an argument.
The first line in the draw method is:
Graphics g = p.getGraphics();
Where (p is the the jPanel passed as an argument to the draw(Jpanel P) method).
I want to draw different rectangles in different colors (based on conditional statements). So i use the
g.setColor(Color.RED) // or another color
Then i draw the rectangle using
g.fillrect(x,y,xsize,ysize).
When i do
System.out.println("color is " + g.getColor().toString());
I see the colors changing, but i don't see the rectangles appearing on the JPanel. This JPanel is an object inside my class that extends from JFrame. What do i need to do to see the rectangles on my JPanel?
You should not paint by obtaining the Graphics object from the JPanel. What you should do is to subclass JPanel and override paintComponent, check this.
The reason why is because the Graphics instance is created everytime your Panel is painted, and you have no control about it, because its parent (the JFrame) may decide when to do so. So you should never make any assumption about the Graphics instance, and you must include your painting logic in the paintComponent method.
The reason why you are not seeing your rectangles is because you are painting them with either an old Graphics instance, or because in the next rePaint they are getting erased because is not the order it is supposed to be (those calls must be in the paintComponent method).
JPanel doesn't know that its canvas is updated, so you see nothing on the screen. The proper way is to override JPanel's method onPaintComponent and draw inside this method. This way, after you call JPanel.repaint() you'll see the stuff you've drawn.

what is the purpose of JFrame setBackground

When you create JFrame instance, you have setBackground method available from this instance. However, regardless to what color you try to put there, you`ll receive gray background color.
This happens (as i understood), because default JPanel instance is automatically created inside JFrame and lays over it . So, to get color set, you need to call
JFrame.getContentPane().setBackground(Color.RED);
that actually calls setBackground of default JPanel , that exists inside JFrame.
I also tried to do next :
JFrame jf = new JFrame();
//I expect this will set size of JFrame and JPanel
jf.setSize(300, 500);
//I expect this to color JFrame background yellow
jf.setBackground(Color.yellow);
//I expect this to shrink default JPanel to 100 pixels high,
//so 400 pixels of JFrame should became visible
jf.getContentPane().setSize(300, 100);
//This will make JPanel red
jf.getContentPane().setBackground(Color.RED);
After this set of code, I am having solid red square of JFrame size , i.e. 300 x 500.
Questions :
Why jf.getContentPane().setSize(300, 100); does not resize default JPanel , revealing JFrame background?
Why JFrame has setBackground method if anyway you cannot see it and it is covered by default JPanel all the time?
As per the class hierarchies of JFrame as shown below:
java.lang.Object
java.awt.Component
java.awt.Container
java.awt.Window
java.awt.Frame
javax.swing.JFrame
The method Frame#setBackground() is inherited from Frame and JFrame doesn't do override it.
What JFrame states:
The JFrame class is slightly incompatible with Frame. Like all other JFC/Swing top-level containers, a JFrame contains a JRootPane as its only child. The content pane provided by the root pane should, as a rule, contain all the non-menu components displayed by the JFrame. This is different from the AWT Frame case.
You can override default setBackground() of JFrame as shown below:
#Override
public void setBackground(Color color){
super.setBackground(color);
getContentPane().setBackground(color);
}

Paint, repaint , paintComponent

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();
}
}

Clearing JPanel

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.

Categories

Resources