Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is paint()/paintComponent() never called?

For the last two days I have tried to understand how Java handles graphics, but have failed miserably at just that. My main problem is understanding exactly how and when paint() (or the newer paintComponent() ) is/should be called.

In the following code I made to see when things are created, the paintComponent() is never called, unless I manually add a call to it myself or calls to JFrame.paintAll()/JFrame.paintComponents().

I renamed the paint() method to paintComponent() in hoping that would fix my problem of it never being called (even at repaint()), but no luck.

package jpanelpaint;

import java.awt.*;
import javax.imageio.*;
import javax.swing.*;
import java.io.*;
import java.util.ArrayList;

public class ImageLoadTest extends JComponent {
 ArrayList<Image> list;

 public ImageLoadTest() {
  list = new ArrayList<Image>();

  try { //create the images (a deck of 4 cards)
   for(String name : createImageFileNames(4)){
    System.err.println(name);
    list.add(ImageIO.read(new File(name)));
   }
  } catch (IOException e) {  }
 }

    protected void paintComponent(Graphics g) {
     int yOffset=0;
  System.err.println("ImageLoadTest.paintComponent()");
     for(Image img : list) {
      g.drawImage(img, 0, yOffset,  null);
      yOffset+=20;
     }
    }

 public static void main(String args[]) throws InterruptedException {
  JFrame frame = new JFrame("Empty JFrame");
  frame.setSize(new Dimension(1000, 500));
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  frame.setVisible(true);

  Thread.sleep(1000);
  frame.setTitle("Loading images");
  ImageLoadTest ilt = new ImageLoadTest();
  frame.add(ilt);
  //update the screen
  //DOESN'T WORK. only works if I call frame.paintAll(frame.getGraphics()) 
  ilt.repaint();
  frame.repaint();

  Thread.sleep(1000);
  frame.setTitle("Setting background");
  ilt.setBackground(Color.BLACK);
  //update the screen - DOESN'T WORK even if I call paintAll ..
  ilt.repaint();
  frame.repaint();

            //have to call one of these to get anything to display  
//  ilt.paintComponent(frame.getGraphics()); //works
  frame.paintComponents(frame.getGraphics()); //works
 }

 //PRIVATE HELPER FUNCTIONS

 private String[] createImageFileNames(int count){
  String[] fileNames = new String[count];
  for(int i=0; i < count; i++)
   fileNames[i] = "Cards" + File.separator + (i+1) + ".bmp";  
  return fileNames;
 }
}
like image 258
oligofren Avatar asked Nov 04 '09 19:11

oligofren


People also ask

Does repaint call paintComponent?

Answer: The repaint() method of a component is called to notify the system that the component needs to be redrawn. It does not itself do any drawing (neither directly nor by calling the paintComponent() routine).

How do I call a paintComponent method?

The paintComponent() method can also be called explicitly by the repaint() method defined in Component class. The effect of calling repaint() is that Swing automatically clears the graphic on the panel and executes the paintComponent method to redraw the graphics on this panel.

Who calls the paintComponent method of a component?

The call from JComponent. paint is probably the one you're looking for. Note that paintComponent is not called from any constructor. The paintComponent is called "on-demand" i.e. when the system decides that the component needs to be redrawn.

What is paintComponent method?

paintComponent()This method is needed to draw something on JPanel other than drawing the background color. This method already exists in a JPanel class so that we need to use the super declaration to add something to this method and takes Graphics objects as parameters.


2 Answers

One of the reasons the paintComponent() doesn't get invoked in the original code is because the component has a "zero size" and the RepaintManger is smart enough not to try and paint something with no size.

The reason the reordering of the code works is because when you add the component to the frame and then make the frame visible the layout manager is invoked to layout the component. By default a frame uses a BorderLayout and by default a component is added to the center of the BorderLayout which happens give all the space available to the component so it gets painted.

However, you change the layout manager of the content pane to be a FlowLayout, you would still have a problem because a FlowLayout respects the preferred size of the component which is zero.

So what you really need to do is assign a preferred size to you your component so layout managers can do their job.

like image 160
camickr Avatar answered Sep 23 '22 01:09

camickr


One major issue here is you are not updating your swing components on the Event Dispatch Thread (EDT). Try wrapping all the code in your main method in the following:

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            // swing code here...             
        }
    });

Also: add your ImageLoadTest to the frame before setting the frame visible. This is based on a quick cursory read of the code -- I will read it further and see what else I can find.

EDIT:

Follow my original advice above, and simplify your main method to look like the following and your paintComponent() will be called:

public static void main(String args[]) throws InterruptedException {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            JFrame frame = new JFrame("Empty JFrame");
            frame.setSize(new Dimension(1000, 500));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            PaintComponentTest ilt = new PaintComponentTest();
            frame.add(ilt);
            frame.setVisible(true);
            ilt.setBackground(Color.BLACK);
        }
    });
}

Also I would read up on using timers to perform animation, as well as general Swing event dispatching and how/when to override various paint methods.

http://java.sun.com/products/jfc/tsc/articles/painting/

http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html

http://java.sun.com/docs/books/tutorial/uiswing/concurrency/dispatch.html

like image 41
Chris B. Avatar answered Sep 22 '22 01:09

Chris B.