在JPanel中调整图像大小

时间:2015-08-03 13:23:21

标签: java swing jcomponent

我尝试在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);
            }
        });
    }
}

问题是图像最初没有显示。它显示当我例如稍微改变帧大小时。

就像那样:

enter image description here

2 个答案:

答案 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的其他方法,特别是getWidthgetHeight方法,它们不需要参数:

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 / 400200 / 400.0(当然还有200.0 / 400.0)也会返回0.5。

最后,存在缩放问题。我建议阅读MadProgrammer的答案,特别是他的答案所链接的java.net文章The Perils of Image.getScaledInstance()

简短版本是Image.getScaledInstance是20世纪90年代的另一个延续,并没有很好地扩展。有两个更好的选择:

  1. 将图像绘制成新图像,然后让drawImage方法使用Graphics2D对象的RenderingHints处理缩放。
  2. 在图片上使用AffineTransformOp创建新的缩放图片。
  3. 方法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类中。