JPanel的宽度和高度为0(具体情况)
如果这很难理解,请原谅我,但我有一个特定的问题,我需要帮助解决。 我已经做了大量的研究,我尝试了很多解决方案,但没有一个是有效的。
我的问题是我有一个扩展JPanel
的ImagePanel
类(下面的代码),这个类需要使用宽度和高度来缩放图像(我正在创建一个程序,用户可以创建自定义教程,包括图像)。 当我实例化这个时,我得到一个错误,说宽度和高度必须非零。 我知道这是因为布局管理器尚未将ImagePanel
传递给首选大小,但我不知道如何将该大小传递给面板。 ImagePanel
位于JPanel
内部,该JTabbedPane
内部位于JFrame
内部JSplitPane
内部的JTabbedPane
内部的JPanel
内部的JScrollPane
内部的JSplitPane
内部。 递减容器顺序的图形表示如下:
- JFrame(GridLayout)
- JSplitPane(默认SplitPane布局)
- JTabbedPane(Deault JTabbedPane布局)
- JPanel(GridLayout)
- JScrollPane(默认ScrollPane布局)
- JSplitPane(默认SplitPane布局)
- JPanel(GridLayout);
- ImagePanel
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
并监视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
,作为示例……
您还应该默认提供非0
大小的preferredSize
值(请记住,面板的默认首选大小是0x0
)。 根据布局管理器的不同,这可以忽略,但通常用作大多数布局管理器的基础……
我想图像改变大小与JFrame的大小
您可以使用Darryl的Stretch Icon,而不是在自己的组件中进行自定义绘画。 图标将根据可用空间自动resize。
这就是为什么我不使用’setPreferedSize();’。
如果你确实使用自定义绘画,那么你不应该使用setPreferredSize()。 您应该重写getPreferredSize()
方法以返回图像的大小。 请记住,首选大小只是对布局管理器的建议。 布局管理器可以使用或忽略大小。
如果要在paintComponent()方法中自动缩放图像,则代码应为:
Dimension d = getSize(); g.drawImage(paint, 0, 0, d.width, d.height, this);
因此,代码中的真正挑战(无论您选择哪种解决方案)都是为了确保您使用的布局管理器将为您的组件提供所有可用空间,以便可以自动缩放图像。