Java 为什么从未调用paint()/paintComponent()?
在过去的两天里,我一直试图理解Java是如何处理图形的,但在这一点上失败得很惨。我的主要问题是准确理解如何以及何时调用paint()(或更新的paintComponent()) 在下面的代码中,我创建了一些东西,以查看何时创建这些东西,除非我自己手动添加对它的调用或调用JFrame.paintAll()/JFrame.paintComponents(),否则永远不会调用paintComponent() 我将paint()方法重命名为paintComponent(),希望这能解决我的问题,即它从未被调用(即使在repaint()中),但运气不佳Java 为什么从未调用paint()/paintComponent()?,java,swing,awt,paint,Java,Swing,Awt,Paint,在过去的两天里,我一直试图理解Java是如何处理图形的,但在这一点上失败得很惨。我的主要问题是准确理解如何以及何时调用paint()(或更新的paintComponent()) 在下面的代码中,我创建了一些东西,以查看何时创建这些东西,除非我自己手动添加对它的调用或调用JFrame.paintAll()/JFrame.paintComponents(),否则永远不会调用paintComponent() 我将paint()方法重命名为paintComponent(),希望这能解决我的问题,即它从未
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()
- 提供了第二个构造函数来设置应显示哪个组件的图像列表
- 重要提示:调用构造函数中组件的
。如果未设置尺寸,swing当然不会绘制组件。首选尺寸基于所有图像的最大宽度和所有图像高度之和setPreferredSize()
- 调用覆盖的
paintComponent()中的
super.paintComponent(g)
- 将
更改为根据所绘制图像的高度自动设置paintComponent
yOffset
- 在EDT上完成GUI初始化
- 由于原始代码基于使用
来说明图像的加载和加载可能需要很长时间,因此使用了sleep()
SwingWorker
等待,然后设置新标题,然后加载图像worker
- 完成后,
中的done()
最后将组件添加到worker
并显示它。将组件添加到JFrame
的内容窗格中,如api中所述。如javadoc所述,在调用JFrame
之后,对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; } }