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

Java JPanel的宽度和高度为0(特定情况),java,swing,layout,jpanel,Java,Swing,Layout,Jpanel,请原谅我,如果这是很难遵循,但我有一个具体的问题,我需要帮助解决。我做了大量的研究,尝试了许多解决方案,但都没有奏效 我的问题是,我有一个ImagePanel类正在扩展JPanel(下面的代码),这个类需要使用宽度和高度来缩放图像(我正在制作一个程序,用户可以在其中创建自定义教程,包括图像)。当我实例化它时,我得到一个错误,即宽度和高度必须为非零。我知道这是因为布局管理器尚未将首选尺寸传递给ImagePanel,但我不知道如何将该尺寸传递给面板。ImagePanel位于JPanel内部,而JSp

请原谅我,如果这是很难遵循,但我有一个具体的问题,我需要帮助解决。我做了大量的研究,尝试了许多解决方案,但都没有奏效

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

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);}     
    }

}
  • JFrame(网格布局)
  • JSplitPane(默认拆分窗格布局)
  • JTabbedPane(Deault JTabbedPane布局)
  • JPanel(网格布局)
  • JScrollPane(默认滚动窗格布局)
  • JSplitPane(默认拆分窗格布局)
  • JPanel(网格布局)
  • 图像面板
  • 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()

    至少有两种方法可以实现,第一种方法是允许
    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

    这不是一个好主意,因为缩放可能需要时间,如果可以避免的话,您不想减慢绘制过程

    您可以使用连接到
    ImagePanel
    ComponentListener
    监视
    componentresisted
    事件

    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();
    }
    
    这允许快速连续多次调用已恢复组件属性的
    generateScaledInstance
    ,但如果两者之间的时间超过250毫秒,则可以调用
    generateScaledInstance
    ,例如

    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();
    }
    

    默认情况下,还应提供非
    0
    大小的
    preferredSize
    值(请记住,面板的默认首选大小为
    0x0
    )。根据布局管理器的不同,这可以忽略,但通常用作大多数布局管理器的基础…

    至少有两种方法可以实现,第一种方法是允许
    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

    这不是一个好主意,因为缩放可能需要时间,如果可以避免的话,您不想减慢绘制过程

    您可以使用连接到
    ImagePanel
    ComponentListener
    监视
    componentresisted
    事件

    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();
    }
    
    这允许快速连续多次调用已恢复组件属性的
    generateScaledInstance
    ,但如果两者之间的时间超过250毫秒,则可以调用
    generateScaledInstance
    ,例如

    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();
    }
    
    默认情况下,还应提供非
    0
    大小的
    preferredSize
    值(请记住,面板的默认首选大小为
    0x0
    )。根据布局管理器的不同,这可以忽略,但通常用作大多数布局管理器的基础

    我想改变图像的大小与JFrame的大小

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

    这就是为什么我不使用“setPreferedSize();”

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

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

    Dimension d = getSize();
    g.drawImage(paint, 0, 0, d.width, d.height, this);
    
    因此,代码中的真正挑战(无论您选择哪种解决方案)是确保您使用的布局管理器将为组件提供所有可用空间,以便图像可以自动缩放

    我想改变图像的大小与JFrame的大小

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

    这就是为什么我不使用“setPreferedSize();”

    如果使用自定义绘制,则不应使用setPreferredSize()。您应该重写
    getPreferredSize()
    方法以返回大小o