繁体   English   中英

JPanel的宽度和高度为0(特定情况)

[英]Width and height of a JPanel are 0 (Specific Situation)

如果很难做到,请原谅我,但是我有一个特定的问题需要解决。 我已经做了很多研究,并且尝试了许多解决方案,但是都没有用。

我的问题是我有一个ImagePanel类,它扩展了JPanel (下面的代码),该类需要使用宽度和高度来缩放图像(我正在编写一个程序,用户可以在其中创建包括图像的自定义教程)。 当我实例化它时,我得到一个错误,说宽度和高度必须为非零。 我知道这是因为布局管理器尚未将ImagePanel传递给首选大小,但是我不知道如何将该大小传递给面板。 所述ImagePanel是一个内部JPanel其是内部JSplitPane一个的内部JScrollPane一个的内部JPanel一个的内部JTabbedPane一个的内部JSplitPane一个的内部JFrame 容器顺序递减的图形表示如下:

  1. JFrame(GridLayout)
  2. JSplitPane(默认SplitPane布局)
  3. JTabbedPane(Deault JTabbedPane布局)
  4. JPanel(GridLayout)
  5. JScrollPane(默认ScrollPane布局)
  6. JSplitPane(默认SplitPane布局)
  7. JPanel(GridLayout);
  8. 图像面板

ImagePanel的代码如下:

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

public class ImagePanel extends JPanel{

    private BufferedImage i;
    private ImageIcon miniature;
    private Image paint = null;

    public void createImage(String path){
        try {                
            i = ImageIO.read(new File(path));
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        if(i != null){
            int width = (int)((double)i.getWidth() * ((double)getWidth()/i.getWidth()));
            int height = (int)((double)i.getHeight()*i.getHeight()/i.getWidth()*((double)this.getHeight()/i.getHeight()));
            miniature = new ImageIcon(i.getScaledInstance(width, height, Image.SCALE_SMOOTH));
            paint = miniature.getImage();
        }

    }


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (paint!=null){
            g.drawImage(paint, 0, 0, null);}     
    }

}

如何获得合适的ImagePanel大小。 我希望图像随着JFrame的大小而改变大小,这就是为什么我不只是使用setPreferedSize();

我希望图像随着JFrame的大小而改变

可以使用Darryl的Stretch Icon而不是在您自己的组件中进行自定义绘制。 图标将根据可用空间自动调整大小。

这就是为什么我不只是使用'setPreferedSize();'的原因。

如果您确实使用自定义绘画,则不应使用setPreferredSize()。 您应该重写getPreferredSize()方法以返回图像的大小。 请记住,首选大小仅是布局管理器的建议。 布局管理器可以使用或忽略大小。

如果要在paintComponent()方法中自动缩放图像,则代码应为:

Dimension d = getSize();
g.drawImage(paint, 0, 0, d.width, d.height, this);

因此,代码(无论选择哪种解决方案)中的真正挑战是,确保使用的布局管理器将为组件提供所有可用空间,以便可以自动缩放图像。

至少有两种方法可以实现,第一种是允许paintComponent检查paint的状态并在图像为null时适当地重新缩放图像。

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    if (i != null && paint == null){
        generateScaledInstance();
    }
    if (paint != null) {
        g.drawImage(paint, 0, 0, this);
    }
}

这将起作用,因为除非组件的大小大于0且已附加到本地对等点(在屏幕上),否则绝对不应调用paintComponent

这不是一个好主意,因为缩放可能会花费一些时间,并且如果可以避免的话,您也不想拖慢绘制过程。

您可以使用附加到ImagePanelComponentListener并监视componentResized事件

addComponentListener(new ComponentAdapter() {
    @Override
    public void componentResized(ComponentEvent e) {
        if (i != null) {
            generateScaledInstance();
        }
    }
});

这可能会连续多次被调用,所以要小心。

在这种情况下,我倾向于使用设置为小延迟的javax.swing.Timer将更新合并到尽可能少的调用,例如。

private Timer resizeTimer;
//...
// Probably in you classes constructor
resizeTimer = new Timer(250, new ActionListener() {
    public void actionPerformed(ActionEvent evt) {
        // Actually perform the resizing of the image...
        generateScaledInstance();
    }
});
// Don't want a repeating event...
resizeTimer.setRepeats(false);

//...
public void componentResized(ComponentEvent evt) {
    resizeTimer.restart();
}

这允许快速连续调用componentResized多次,但是例如,如果两次间隔之间的时间超过250毫秒,则可以调用generateScaledInstance ...

还应该提供一种preferredSize非的值0大小默认(记住,一个面板的默认大小优选是0x0 )。 取决于布局管理器,这可以忽略,但是通常用作大多数布局管理器的基础...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM