问题:
在已经可见的JFrame上调用setVisible(true)有什么用?我正在挖掘JFrame的源代码,最终归结为this function in Component,如果已经可见,它对框架没有任何作用。为什么它像revalidate(); repaint();
一样? (见下面的SSCCE)
动机:
我正在开发一个java应用程序,为此我编写了一个扩展JImagePanel
的类JPanel
,并允许用户将图像设置为背景(请参阅SSCCE)。我发现在编辑了面板的背景后,我遇到了将背景重新绘制为正确尺寸的问题。在浏览互联网后,我发现以下工作:
if(frame.isVisible()) frame.setVisible(true);
最终,我使用
解决了这个问题panel.revalidate();
panel.repaint();
,我认为这是更好的解决方案,但它让我思考setVisible(true)
在已经可见的框架上实际做了什么。从我的观点来看,它不应该起作用 - 但事实上确实如此。
SSCCE
这是一个说明我的问题的例子。如果不出意外的话,希望你能在将来发现这门课非常有用。
注意:此文件的更新源可在项目主页上找到,该项目是为此项目创建的项目的GitHub。
享受!
package com.dberm22.utils;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class JImagePanel extends JPanel {
private static final long serialVersionUID = 6841876236948317038L;
private Image img = null;
private Position position = Position.CENTER;
public enum Position{
STRETCH,
CENTER,
FIT,
FILL,
NONE;
}
public JImagePanel() {
}
public JImagePanel(String img) {
this(new ImageIcon(img).getImage());
}
public JImagePanel(Image img) {
setBackgroundImage(img);
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(getBackground());
g2.fillRect(0, 0, getWidth(), getHeight());
if (this.position.equals(Position.STRETCH))
{
if(this.img != null) g2.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}
else if (this.position.equals(Position.FILL) || this.position.equals(Position.FIT))
{
if(this.img != null)
{
double scaleFactor = getScaleFactor(new Dimension(img.getWidth(null), img.getHeight(null)), getSize());
int scaleWidth = (int) Math.round(img.getWidth(null) * scaleFactor);
int scaleHeight = (int) Math.round(img.getHeight(null) * scaleFactor);
//Image img_scaled = img.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
g2.drawImage(scaleImage(img, scaleWidth, scaleHeight, getBackground()), (getWidth() - scaleWidth)/2, (getHeight() - scaleHeight)/2, scaleWidth, scaleHeight, null);
}
}
else if (this.position.equals(Position.CENTER)) { if(this.img != null) g2.drawImage(img, (getWidth() - img.getWidth(null))/2, (getHeight() - img.getHeight(null))/2, null); }
}
public void setBackgroundImage(String img)
{
setBackgroundImage(new ImageIcon(img).getImage());
}
public void setBackgroundImage(Image img)
{
this.img = img;
Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
repaint();
}
public static double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = 1;
if (iMasterSize > iTargetSize) {
dScale = (double) iTargetSize / (double) iMasterSize;
} else {
dScale = (double) iTargetSize / (double) iMasterSize;
}
return dScale;
}
public double getScaleFactor(Dimension original, Dimension targetSize) {
double dScale = 1d;
if (original != null && targetSize != null) {
double dScaleWidth = getScaleFactor(original.width, targetSize.width);
double dScaleHeight = getScaleFactor(original.height, targetSize.height);
if (this.position.equals(Position.FIT)) dScale = Math.min(dScaleHeight, dScaleWidth);
else if(this.position.equals(Position.FILL)) dScale = Math.max(dScaleHeight, dScaleWidth);
}
return dScale;
}
public BufferedImage scaleImage(Image img, int width, int height, Color background) {
BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = newImage.createGraphics();
try {
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setBackground(background);
g.clearRect(0, 0, width, height);
g.drawImage(img, 0, 0, width, height, null);
} finally {
g.dispose();
}
return newImage;
}
public void setBackgroundImagePosition(String pos)
{
if("Stretch".equals(pos)) setBackgroundImagePosition(Position.STRETCH);
else if("Center".equals(pos)) setBackgroundImagePosition(Position.CENTER);
else if("Fit".equals(pos)) setBackgroundImagePosition(Position.FIT);
else if("Fill".equals(pos)) setBackgroundImagePosition(Position.FILL);
else if("None".equals(pos)) setBackgroundImagePosition(Position.NONE);
}
public void setBackgroundImagePosition(Position pos)
{
this.position = pos;
repaint();
}
public static void main(String[] args)
{
JFrame frame = new JFrame("JImagePanel Test");
frame.setSize( Toolkit.getDefaultToolkit().getScreenSize());
frame.setPreferredSize( Toolkit.getDefaultToolkit().getScreenSize());
frame.setExtendedState(JFrame.MAXIMIZED_BOTH); //sets appropriate size for frame
JImagePanel panel = new JImagePanel();
frame.add(panel);
frame.setVisible(true);
try {Thread.sleep(2000);} catch (InterruptedException e) {}
panel.setBackgroundImage("C:\\Users\\David\\Pictures\\Wood.jpg");
panel.setBackgroundImagePosition(JImagePanel.Position.STRETCH);
panel.revalidate(); // need to revalidate()
panel.repaint(); //doesnt work by itself
try {Thread.sleep(2000);} catch (InterruptedException e) {}
panel.setBackgroundImage("C:\\Users\\David\\Pictures\\Wood.jpg");
panel.setBackgroundImagePosition(JImagePanel.Position.FIT);
frame.setVisible(true); //also works --why?
}
}
答案 0 :(得分:2)
在已经可见的setVisible(true)
上拨打JFrame
对您有效,因为这最终会在内部调用validate()
,从而重新验证帧中的所有子组件。
要了解原因,请参阅Component.setVisible(boolean b)
:
public void setVisible(boolean b) {
show(b);
}
public void show(boolean b) {
if (b) {
show();
} else {
hide();
}
}
但show()
方法在Window
中被覆盖(其中JFrame
是子类)。因此,这最终会调用Window.show()
:
public void show() {
if (peer == null) {
addNotify();
}
validate();
[...]
希望这能解释你所看到的行为。
答案 1 :(得分:2)
假设您正在执行imagePanel的中途清理:
这会更改您的setter并覆盖getXXSize。请注意,仅仅根据图像大小设置大小提示是不您想要在现实代码中执行的操作,您可能也想考虑超级提示(如果面板有子节点,则为fi)
@Override
public Dimension getPreferredSize() {
if (img != null) {
return new Dimension(img.getWidth(this), img.getHeight(this));
}
return super.getPreferredSize();
}
public void setBackgroundImage(Image img) {
this.img = img;
// need to revalidate as our sizing hints might have changed
revalidate();
repaint();
}
public void setBackgroundImagePosition(Position pos) {
this.position = pos;
repaint();
}
现在使用它的应用程序代码只调用setter,而不是其他:
JFrame frame = new JFrame("JImagePanel Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setLayout(new FlowLayout()); // just to see the effect of a pref-respecting layout
final JImagePanel panel = new JImagePanel();
frame.add(panel);
final Image[] images = new Image[]{
XTestUtils.loadDefaultImage(), XTestUtils.loadDefaultImage("500by500.png"), null};
Action toggleImage = new AbstractAction("toggle image") {
int index = 0;
@Override
public void actionPerformed(ActionEvent e) {
panel.setBackgroundImage(images[index]);
index = (index +1) % images.length;
}
};
Action togglePosition = new AbstractAction("toggle position") {
int index = 0;
@Override
public void actionPerformed(ActionEvent e) {
panel.setBackgroundImagePosition(Position.values()[index]);
index = (index +1) % Position.values().length;
}
};
frame.add(new JButton(toggleImage), BorderLayout.NORTH);
frame.add(new JButton(togglePosition), BorderLayout.SOUTH);
// size for frame
//frame.setSize(800, 800);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH); //sets appropriate
frame.setVisible(true);