为什么 Paint()/paintComponent() 从未被调用?
题
在过去的两天里我一直在努力 理解 Java 如何处理图形,但在这方面却惨遭失败。我的主要问题是准确理解paint()(或较新的paintComponent())如何以及何时被调用。
在下面的代码中,我查看了创建事物的时间,永远不会调用paintComponent(),除非我自己手动添加对它的调用或对JFrame.paintAll()/JFrame.paintComponents()的调用。
我将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;
}
}
解决方案 4
这些是导致其无法工作的原始代码的主要问题:
- 在 add() 操作后不调用 validate()
- 没有设置组件的首选大小。
- 覆盖它时不要调用super.paintcomponent()(这使setbackground()调用不起作用)
- 我需要继承 JPanel 才能绘制它。Component 和 JComponent 都不足以让 setBackground() 调用正常工作,即使修复了第 3 点也是如此。
完成上述操作后,调用 PaintComponent 或 Paint 方法实际上并不重要,只要我记得在开始时调用超级构造函数,两者似乎都可以工作。
此信息是根据 @jitter、@tackline 和 @camickr 撰写的内容汇总的,非常值得称赞!
附:不知道回答你自己的问题是否被认为是不好的形式,但由于我需要的信息是由几个答案组装而成的,我认为最好的方法是更新其他答案并写出这样的总结。
其他提示
之一的paintComponent()没有得到原代码中调用的原因是因为组件具有“零尺寸”和RepaintManger足够聪明,不尝试和油漆的东西,没有大小。
代码的重新排序工作的原因是因为当你的组件添加到帧,然后使框架可见的布局管理器调用来布局组件。默认情况下,一帧使用的BorderLayout并且默认的组分被加入到BorderLayout的中心恰好给所有可用的空间的部件,以便它被涂漆。
但是,改变内容窗格的布局管理器是一个的FlowLayout,则仍然有一个问题,因为一个的FlowLayout尊重它是一个零组件的最佳尺寸。
所以,你真正需要做的是指定一个首选的大小到您的组件,以便布局管理器可以做他们的工作。
在这里一个主要问题是你是不是对的事件分派线程(EDT)。尝试包裹所有的代码在主方法如下所示:
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);
}
});
}
此外,我将在使用定时器来执行动画,以及一般的摇摆事件分派读取向上和如何/何时覆盖各种油漆的方法。
http://java.sun.com/products/jfc/ TSC /物品/油漆/
http://java.sun.com/docs /books/tutorial/uiswing/misc/timer.html
http://java.sun.com/docs /books/tutorial/uiswing/concurrency/dispatch.html
使 汤姆·霍廷 (Tom Hawtin) - 战术线 快乐的。我又重写了一遍
我改变了几件事(检查带有 //new
评论)
完全重写了它
- 拆分成一个干净的新组件文件(
ImageLoadTest.java
)和一个测试它的文件(Tester.java
)
对原始海报代码的改进
- 调用父级的构造函数
ImageLoadTest
构造函数(super()
) - 提供了第二个构造函数来设置组件应显示的图像列表
- 重要的:拨电至
setPreferredSize()
构造函数中的组件。如果未设置尺寸,那么 swing 当然不会绘制您的组件。首选尺寸基于最大尺寸。所有图像的宽度以及所有图像高度的总和 - 拨电至
super.paintComponent(g)
被覆盖paintComponent()
改变了
paintComponent
自动建立基础yOffset
所绘制图像的高度GUI 初始化在 EDT 上完成
- 作为基于使用的原始代码
sleep()
说明加载和加载图像可能需要很长时间SwingWorker
被使用 worker
等待然后设置新标题然后加载图像- 完成后
worker
在done()
最后将组件添加到JFrame
并显示它。将组件添加到内容窗格JFrame
如中所述 J框架 API。正如 javadoc 中所述,进行了必要的调用validate()
在JFrame
打电话后add()
, ,作为JFrame
是一个已经可见的容器,其子级已更改。
javdoc 引用自
validate()
验证方法用于使容器再次布置其子组件。显示该容器的子组件(从容器中添加或从容器中添加或删除,或与布局相关的信息更改)时,应调用它。
- 第二个工作人员只是做了更多等待,然后将背景颜色设置为黑色
- 用过的
JPanel
作为基类ImageLoadTest
修理setBackground()
我无法使用它JComponent
.
所以你的主要问题是你没有设置组件的首选大小并且你没有调用 validate()
于 JFrame
添加一些东西到已经可见的容器之后。
这应该有效
jpanelpaint/ImageLoadTest.java
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);
}
}
}
测试器.java
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;
}
}
我建议你阅读第一夫妇“巨富客户端”的章节。我一直在使用多年摇摆,但读这本书我才终于完全准确地理解Java的绘画机制的工作原理后而已。