简体   繁体   English

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

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

Please pardon me if this is hard to follow, but I have a specific problem that I need help solving. 如果很难做到,请原谅我,但是我有一个特定的问题需要解决。 I have done a ton of research, and I have tried numerous solutions but none of them are working. 我已经做了很多研究,并且尝试了许多解决方案,但是都没有用。

My issue is that I have an ImagePanel class that is extending JPanel (code below), this class needs to use width and height to scale images (I am making a program where users can create custom tutorials including images). 我的问题是我有一个ImagePanel类,它扩展了JPanel (下面的代码),该类需要使用宽度和高度来缩放图像(我正在编写一个程序,用户可以在其中创建包括图像的自定义教程)。 When I instantiate this I get an error saying that the width and height must be nonzero. 当我实例化它时,我得到一个错误,说宽度和高度必须为非零。 I understand that this is because the layout manager has not yet passed the ImagePanel a preferred size, however I do not know how to get that size to the panel. 我知道这是因为布局管理器尚未将ImagePanel传递给首选大小,但是我不知道如何将该大小传递给面板。 The ImagePanel is inside a JPanel which is inside a JSplitPane inside of a JScrollPane inside of a JPanel inside of a JTabbedPane inside of a JSplitPane inside of a JFrame . 所述ImagePanel是一个内部JPanel其是内部JSplitPane一个的内部JScrollPane一个的内部JPanel一个的内部JTabbedPane一个的内部JSplitPane一个的内部JFrame A graphical representation of this in decreasing container order is as follows: 容器顺序递减的图形表示如下:

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

The code for the ImagePanel is as follows: 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);}     
    }

}

How can I get the proper size to the ImagePanel . 如何获得合适的ImagePanel大小。 I would like the image to change size with the size of the JFrame , which is why I don't just use setPreferedSize(); 我希望图像随着JFrame的大小而改变大小,这就是为什么我不只是使用setPreferedSize(); .

I would like the image to change size with the size of the JFrame 我希望图像随着JFrame的大小而改变

Instead of doing custom painting in your own component you can use Darryl's Stretch Icon . 可以使用Darryl的Stretch Icon而不是在您自己的组件中进行自定义绘制。 The Icon will automatically be resized based on the space available. 图标将根据可用空间自动调整大小。

which is why I dont just use 'setPreferedSize();'. 这就是为什么我不只是使用'setPreferedSize();'的原因。

If you do use custom painting then you should NOT use setPreferredSize(). 如果您确实使用自定义绘画,则不应使用setPreferredSize()。 You SHOULD be overriding the getPreferredSize() method to return the size of the image. 您应该重写getPreferredSize()方法以返回图像的大小。 Remember the preferred size is just a suggestion to the layout manager. 请记住,首选大小仅是布局管理器的建议。 The layout manager can use or ignore the size. 布局管理器可以使用或忽略大小。

If you want to scale the image automatically in your paintComponent() method then the code should be: 如果要在paintComponent()方法中自动缩放图像,则代码应为:

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

So the real challenge in your code (no matter which solution you choose) is to make sure you are using a layout manger that will give all available space to your component so the image can be scaled automatically. 因此,代码(无论选择哪种解决方案)中的真正挑战是,确保使用的布局管理器将为组件提供所有可用空间,以便可以自动缩放图像。

There are at least two ways this might be achieved, the first would be to allow the paintComponent to check the state of the paint and rescale the image appropriatly when it is null 至少有两种方法可以实现,第一种是允许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);
    }
}

This will work because paintComponent should never be called unless the component has a size greater than 0 and is attached to a native peer (on the screen). 这将起作用,因为除非组件的大小大于0且已附加到本地对等点(在屏幕上),否则绝对不应调用paintComponent

This is not a great idea as scaling can take time and you don't want to slow down the paint process if you can avoid it. 这不是一个好主意,因为缩放可能会花费一些时间,并且如果可以避免的话,您也不想拖慢绘制过程。

You could use a ComponentListener attached to the ImagePanel and monitor the componentResized event 您可以使用附加到ImagePanelComponentListener并监视componentResized事件

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

This might be called a number of times in quick succession, so be careful. 这可能会连续多次被调用,所以要小心。

In this case, what I tend to do is use a javax.swing.Timer set to small delay to coalse the updates down to as few a calls as possible, for example... 在这种情况下,我倾向于使用设置为小延迟的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();
}

This allows componentResized to be called a number of times in quick succession, but if the time between exceeds 250 milliseconds, the generateScaledInstance can be called, as an example... 这允许快速连续调用componentResized多次,但是例如,如果两次间隔之间的时间超过250毫秒,则可以调用generateScaledInstance ...

You should also provide a preferredSize value of a non 0 size by default (remember, the default preferred size of a panel is 0x0 ). 还应该提供一种preferredSize非的值0大小默认(记住,一个面板的默认大小优选是0x0 )。 Depending on the layout manager, this could be ignored, but is generally used as a basis for most layout managers... 取决于布局管理器,这可以忽略,但是通常用作大多数布局管理器的基础...

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

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