我尝试在JPanel中将图像设置为背景并将其调整为所需大小。
这是MyPanel,我选择图像并将其设置为backgorund:
public class MyPanel extends JPanel {
Image img;
public MyPanel(LayoutManager l) {
super(l);
JFileChooser fc = new JFileChooser();
int result = fc.showOpenDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
String sname = file.getAbsolutePath();
img = new ImageIcon(sname).getImage();
double xRatio = img.getWidth(null) / 400;
double yRatio = img.getHeight(null) / 400;
double ratio = (xRatio + yRatio) / 2;
img = img.getScaledInstance((int)(img.getWidth(null) / ratio), (int)(img.getHeight(null) / ratio), Image.SCALE_SMOOTH);
}
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, Color.WHITE, null);
}
}
这是我的框架:
public class MyFrame extends JFrame {
public MyFrame () {
initUI();
}
private void initUI() {
MyPanel pnl = new MyPanel(null);
add(pnl);
setSize(600, 600);
setTitle("My component");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
MyFrame ex = new MyFrame ();
ex.setVisible(true);
}
});
}
}
问题是图像最初没有显示。它显示当我例如稍微改变帧大小时。
就像那样:
答案 0 :(得分:1)
您必须调用enum
:
setVisible(true)
答案 1 :(得分:1)
您正在异步加载图片。
ImageIcon(String)构造函数在内部使用Toolkit.getImage,这是20世纪90年代的一个延续,当时许多家庭Internet连接速度太慢,以至于始终在后台线程中加载图像是有意义的。
由于图片是在后台加载的,img.getWidth(null)
可能会返回图片的大小,或可能返回-1。 documentation解释了这一点。
那么,您如何等待图像加载到后台线程中?
通常,您会使用ImageObserver观察图像的进度。事实上,所有AWT组件以及所有Swing JComponents都实现了ImageObserver。所以你有一个ImageObserver对象:你的MyPanel实例。
因此,不是将null
传递给所有这些方法,而是传递MyPanel实例 - 即this
:
double xRatio = img.getWidth(this) / 400;
double yRatio = img.getHeight(this) / 400;
和
g.drawImage(img, 0, 0, Color.WHITE, this);
然而......这将正确跟踪图片的加载进度,但仍不能保证img.getWidth
在您调用时会返回正值。
为确保图像立即完全加载,您可以将ImageIcon替换为ImageIO.read:
File file = fc.getSelectedFile();
try {
img = ImageIO.read(file);
} catch (IOException e) {
throw new RuntimeException("Could not load \"" + file + "\"", e);
}
这与使用ImageIcon和Toolkit不同,因为它不仅返回一个Image,它返回一个BufferedImage,这是一种Image,保证完全加载并存在于内存中 - 无需担心后台加载约。
由于已经加载了BufferedImage,因此它有一些不需要ImageObserver的其他方法,特别是getWidth
和getHeight
方法,它们不需要参数:
BufferedImage bufferedImg = (BufferedImage) img;
double xRatio = bufferedImg.getWidth() / 400.0;
double yRatio = bufferedImg.getHeight() / 400.0;
注意我将400更改为400.0。这是因为将一个int除以另一个int会导致Java中的整数运算。例如,200 / 400
返回零,因为200和400都是int值。 5 / 2
生成int值2。
但是,如果其中一个或两个数字都是双精度数,Java会将整个数据视为双精度表达式。因此,(double) 200 / 400
返回0.5。数字序列中存在小数点表示双精度值,因此200.0 / 400
和200 / 400.0
(当然还有200.0 / 400.0
)也会返回0.5。
最后,存在缩放问题。我建议阅读MadProgrammer的答案,特别是他的答案所链接的java.net文章The Perils of Image.getScaledInstance()。
简短版本是Image.getScaledInstance是20世纪90年代的另一个延续,并没有很好地扩展。有两个更好的选择:
方法1:
BufferedImage scaledImage = new BufferedImage(
img.getColorModel(),
img.getRaster().createCompatibleWritableRaster(newWidth, newHeight),
false, new Properties());
Graphics g = scaledImage.createGraphics();
g.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.drawImage(img, 0, 0, newWidth, newHeight, null);
g.dispose();
方法2:
RenderingHints hints = new RenderingHints();
hints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
hints.put(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
AffineTransform transform = AffineTransform.getScaleInstance(
(double) newWidth / img.getWidth(),
(double) newHeight / img.getHeight());
BufferedImageOp op = new AffineTransformOp(transform, hints);
BufferedImage scaledImage = op.filter(img, null);
您可能希望根据自己喜欢的速度与质量之间的权衡来更改RenderingHints值。它们都记录在RenderingHints类中。