Java 为什么从未调用paint()/paintComponent()?

Java 为什么从未调用paint()/paintComponent()?,java,swing,awt,paint,Java,Swing,Awt,Paint,在过去的两天里,我一直试图理解Java是如何处理图形的,但在这一点上失败得很惨。我的主要问题是准确理解如何以及何时调用paint()(或更新的paintComponent()) 在下面的代码中,我创建了一些东西,以查看何时创建这些东西,除非我自己手动添加对它的调用或调用JFrame.paintAll()/JFrame.paintComponents(),否则永远不会调用paintComponent() 我将paint()方法重命名为paintComponent(),希望这能解决我的问题,即它从未

在过去的两天里,我一直试图理解Java是如何处理图形的,但在这一点上失败得很惨。我的主要问题是准确理解如何以及何时调用paint()(或更新的paintComponent())

在下面的代码中,我创建了一些东西,以查看何时创建这些东西,除非我自己手动添加对它的调用或调用JFrame.paintAll()/JFrame.paintComponents(),否则永远不会调用paintComponent()

我将paint()方法重命名为paintComponent(),希望这能解决我的问题,即它从未被调用(即使在repaint()中),但运气不佳

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;
 }
}
包jpanelpoint;
导入java.awt.*;
导入javax.imageio.*;
导入javax.swing.*;
导入java.io.*;
导入java.util.ArrayList;
公共类ImageLoadTest扩展了JComponent{
数组列表;
公共ImageLoadTest(){
列表=新的ArrayList();
尝试{//创建图像(一副4张卡)
用于(字符串名称:createImageFileNames(4)){
System.err.println(名称);
添加(ImageIO.read(新文件(名称));
}
}捕获(IOE){}
}
受保护组件(图形g){
int-yOffset=0;
System.err.println(“ImageLoadTest.paintComponent()”;
用于(图像img:列表){
g、 drawImage(img,0,yOffset,null);
yOffset+=20;
}
}
公共静态void main(字符串args[])引发InterruptedException{
JFrame=新JFrame(“空JFrame”);
框架设置尺寸(新尺寸(1000500));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
睡眠(1000);
frame.setTitle(“加载图像”);
ImageLoadTest ilt=新ImageLoadTest();
帧。添加(ilt);
//更新屏幕
//不起作用。只有在调用frame.paintAll(frame.getGraphics())时才起作用
ilt.repaint();
frame.repaint();
睡眠(1000);
frame.setTitle(“设置背景”);
倾斜背景(颜色为黑色);
//更新屏幕-即使我调用paintAll也不起作用。。
ilt.repaint();
frame.repaint();
//必须打电话给其中一个才能看到任何东西
//ilt.paintComponent(frame.getGraphics());//有效
frame.paintComponents(frame.getGraphics());//有效
}
//私人助手功能
私有字符串[]CreateImageFileName(整数计数){
字符串[]文件名=新字符串[计数];
for(int i=0;i
这里的一个主要问题是您没有在服务器上更新swing组件。尝试将所有代码包装到主方法中,如下所示:

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            // swing code here...             
        }
    });
另外:在设置帧可见之前,将ImageLoadTest添加到帧中。这是基于对代码的快速粗略阅读——我将进一步阅读,看看还能找到什么

编辑:

按照上面我最初的建议,将主要方法简化为如下所示,然后将调用paintComponent():

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);
        }
    });
}
此外,我还将了解如何使用计时器执行动画,以及一般Swing事件调度以及如何/何时覆盖各种绘制方法


我建议阅读“肮脏的富客户机”的前几章。我已经使用Swing多年了,但直到读了这本书之后,我才最终完全理解Java的绘画机制是如何工作的。

在原始代码中没有调用paintComponent()的原因之一是该组件的大小为“零”而重新粉刷经理足够聪明,不会尝试粉刷没有尺寸的东西

代码重新排序之所以有效,是因为当您将组件添加到框架中,然后使框架可见时,会调用布局管理器来布局组件。默认情况下,框架使用BorderLayout,默认情况下,会将构件添加到BorderLayout的中心,这会为构件提供所有可用空间,以便对其进行绘制

但是,如果将内容窗格的布局管理器更改为FlowLayout,则仍然会有问题,因为FlowLayout尊重组件的首选大小,即零

因此,您真正需要做的是为您的组件指定一个首选尺寸,以便布局经理能够完成他们的工作。

让Tom Hawtin-tackline感到高兴。我又重写了一遍

我更改了几项内容(检查带有
//new
注释的行)

完全重写

  • 拆分为一个干净的新组件文件(
    ImageLoadTest.java
    )和一个测试它的文件(
    Tester.java
改善原有海报守则

  • ImageLoadTest
    构造函数(
    super()
    )中调用父级构造函数
  • 提供了第二个构造函数来设置应显示哪个组件的图像列表
  • 重要提示:调用构造函数中组件的
    setPreferredSize()
    。如果未设置尺寸,swing当然不会绘制组件。首选尺寸基于所有图像的最大宽度和所有图像高度之和
  • 调用覆盖的
    paintComponent()中的
    super.paintComponent(g)
  • paintComponent
    更改为根据所绘制图像的高度自动设置
    yOffset

  • 在EDT上完成GUI初始化

  • 由于原始代码基于使用
    sleep()
    来说明图像的加载和加载可能需要很长时间,因此使用了
    SwingWorker
  • worker
    等待,然后设置新标题,然后加载图像
  • 完成后,
    done()
    中的
    worker
    最后将组件添加到
    JFrame
    并显示它。将组件添加到
    JFrame
    的内容窗格中,如api中所述。如javadoc所述,在调用
    add()
    之后,对
    JFrame
    上的
    validate()
    进行了必要的调用,因为
    JFrame
    已经是可见的
    package jpanelpaint;
    
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Image;
    import javax.swing.JPanel;
    import java.util.List;
    
    public class ImageLoadTest extends JPanel {
      private List<Image> list;
    
      public ImageLoadTest() {
        super();
      }
    
      public ImageLoadTest(List<Image> list) {
        this();
        this.list = list;
        int height = 0;
        int width = 0;
        for (Image img : list) {
          height += img.getHeight(this);
          width = img.getWidth(this) > width ? img.getWidth(this) : width;
          setPreferredSize(new Dimension(width, height));
        }
      }
    
      @Override
      protected void paintComponent(Graphics g) {
        int yOffset=0;
        super.paintComponent(g);
        System.err.println("ImageLoadTest.paintComponent()");
        for(Image img : list) {
          g.drawImage(img, 0, yOffset, null);
          yOffset+=img.getHeight(this);
        }
      }
    }
    
    import java.awt.Dimension;
    import java.awt.Color;
    import java.awt.Image;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.SwingWorker;
    import javax.swing.SwingUtilities;
    import java.util.List;
    import java.util.ArrayList;
    import java.util.concurrent.ExecutionException;
    import jpanelpaint.ImageLoadTest;
    
    public class Tester {
    
      private JFrame frame;
      private ImageLoadTest ilt;
      private final int NUMBEROFFILES = 4;
      private List<Image> list;
    
      //will load the images
      SwingWorker worker = new SwingWorker<List<Image>, Void>() {
        @Override
        public List<Image> doInBackground() throws InterruptedException {
          //sleep at start so user is able to see empty jframe
          Thread.sleep(1000);
          //let Event-Dispatch-Thread (EDT) handle this
          SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              frame.setTitle("Loading images");
            }
          });
          //sleep again so user is able to see loading has started
          Thread.sleep(1000);
          //loads the images and returns list<image>
          return loadImages();
        }
    
        @Override
        public void done() {
          //this is run on the EDT anyway
          try {
            //get result from doInBackground
            list = get();
            frame.setTitle("Done loading images");
            ilt = new ImageLoadTest(list);
            frame.getContentPane().add(ilt);
            frame.getContentPane().validate();
            //start second worker of background stuff
            worker2.execute();
          } catch (InterruptedException ignore) {}
          catch (ExecutionException e) {
            String why = null;
            Throwable cause = e.getCause();
            if (cause != null) {
              why = cause.getMessage();
            } else {
              why = e.getMessage();
            }
            System.err.println("Error retrieving file: " + why);
          }
        }
      };
    
      //just delay a little then set background
      SwingWorker worker2 = new SwingWorker<Object, Void>() {
        @Override
        public List<Image> doInBackground() throws InterruptedException {
          Thread.sleep(1000);
          SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              frame.setTitle("Setting background");
            }
          });
          Thread.sleep(1000);
          return null;
        }
    
        @Override
        public void done() {
          ilt.setBackground(Color.BLACK);
          frame.setTitle("Done!");
        }
      };
    
      public static void main(String args[]) {
        new Tester();
      }
    
      public Tester() {
        //setupGUI
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            frame = new JFrame("Empty JFrame");
            frame.setSize(new Dimension(1000, 500));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
          }
        });
    
        //start the swingworker which loads the images
        worker.execute();
      }
    
      //create image names
      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;
      }
    
      //load images
      private List<Image> loadImages() {
        List<Image> tmpA = new ArrayList<Image>();
        try {
          for(String name : createImageFileNames(NUMBEROFFILES)){
            System.err.println(name);
            tmpA.add(ImageIO.read(new File(name)));
          }
        } catch (IOException e) { }
    
        return tmpA;
      }
    }