Java JPanel的getSize()方法 有时候得到的结果为0 的原因(涉及语句顺序、paint方法的调用)

本文探讨了在Java Swing中如何正确获取JPanel组件的实际尺寸。重点分析了getSize()和getPreferredSize()方法的使用时机,并通过实例代码展示了这些方法在不同情况下的行为表现。

废话少说,放码上来

public class MarioFrame extends JFrame {
	public MarioFrame() {
		this.initFrame();
	}

	public void initFrame() {
		this.setTitle("Mario");
		this.setSize(600, 480);
		this.setDefaultCloseOperation(3);
		this.setLocationRelativeTo(null);
		GamePanel gp = new GamePanel();
		this.add(gp);
		this.setJMenuBar(new Menu());
		<span style="color:#ff0000;">System.out.println(gp.getSize());</span>
		this.setVisible(true);
	}
}

在上面窗体类的代码中,给窗体对象添加了一个JPanel类的对象gp,之后调用getSize()方法并打印到控制台。添加了面板对象,之后得到它的大小并且打印,看似没错,然而实际的运行结果是 

 java.awt.Dimension[width=0,height=0]

窗体正常显示,但得到的JPanel大小为0.当然,调用getPreferredSize()结果也是一样。

为什么呢?

原因是 此方法的调用在窗体显示之前

在以上代码中,虽然成功给Frame窗体添加了JPanel面板,但是代码没有运行到

this.setVisible(true);

语句,窗体并没有显现,面板也就没有显现,此时面板的大小当然为0;

就算已经给JPanel设置了大小,由于没有显示的原因,getSize()方法还是不能得到面板的数据。

由此得到一个结论,getSize()和getPreferredSize()方法的执行,是从闪存中调用数据的。就算已经加载到了内存里面,在代码加载到闪存显示画面之前还是不能得到数据。

因此,只要把上面代码中的getSize()语句放到setVisible(true)语句之后,就能正常得到面板大小。



那么,问题来了,为什么getSize()能够在JPanel的paint()方法里得到JPanel的大小的?

比如下面的代码

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JPanel;

public class GamePanel extends JPanel{
	public static ArrayList<Pipe> pipeList = new ArrayList<Pipe>();
	Background background = new Background(2, 0, 2);
	Mario mario = new Mario(150, 0, 28, 34, 4);
	MarioMoveListener mml = new MarioMoveListener(mario,this,background);

	public GamePanel() {
		this.addGamePanel();
	}
	
	public void addGamePanel(){
		//设置焦点
		this.setFocusable(true);
		//添加监听
		this.addKeyListener(mml);
		//添加管道
		this.addPipe();
		//添加管道
	}

	public void paint(Graphics g) {
		<span style="color:#ff0000;">System.out.println(this.getSize());</span>
		//准备一张缓冲背景图片
		BufferedImage bi = (BufferedImage) this.createImage(this.getWidth(),this.getHeight());
		//获取缓冲图片画笔
		Graphics g_buffer = bi.getGraphics();
		//将要绘制的内容,绘制到缓冲图片上
		g_buffer.drawImage(background.image.getImage(), background.x, 0,10292,(int)this.getSize().getHeight(), null);
		g_buffer.drawImage(mario.image.getImage(), mario.x, (int)(this.getSize().getHeight() * 0.8),mario.width,mario.height, null);
		for(int i = 0;i < pipeList.size();i++){
			Pipe pipe = pipeList.get(i);
			g_buffer.drawImage(pipe.image.getImage(), pipe.x, pipe.y, pipe.width, pipe.height, null);
		}
		//将缓冲图片绘制到窗体
		g.drawImage(bi, 0, 0, null);
	}
	
	public void addPipe(){
		for(int i = 0; i < 100; i++){
			Random ran = new Random();
			int speed = 4;
			int height = 100;
			int width = 40;
			Pipe pipe = new Pipe(400 + 200 * i , 200,width,height,4);
			pipeList.add(pipe);
		}
	}
	
}
此时运行代码就能在控制台打印JPanel的大小

原因就是在窗口显示之后,系统自动调用了一次paint方法进行重绘。此时就涉及到paint方法的调用时机。

一般javax.swing包里的组件大部分是轻量级组件,所以查API可知,JPanel是轻量级组件。(轻重量级组件的具体内容见博客下另一篇)




轻量级组件处理系统触发的请求有两种方法


一种是系统触发的请求,由本地系统发起,例如轻量级组件的重量级祖先组件第一次显示,将会调用paint()方法;

一种是系统触发的请求,由轻量级框架发起,例如轻量级组件大小被调整,将会调用update(),这个方法默认调用paint()方法。


因此,JPanel面板显示之后,调用了一次paint方法,此时就能得到面板的大小了。


这也间接解释了 为什么必须把要绘制的内容放在paint方法内(直接画在面板上会在显示一次之后被调用的paint方法覆盖掉,显示结果就是 闪一下就消失)





<think> 首先,用户的问题是:“java JPanel 怎么调用其他的jPanel动画”,这是在问如何在Java中使用JPanel调用其他JPanel的动画。 我需要用中文回答这个问题。用户指定了“请使用中文回答问题”,所以我的整个回答都应该用中文。 关键部分是:在回答完问题后,必须提出3-5个 §§相关问题§§。相关的输出规则: - 第一行必须完整输出 "§§相关问题§§",不能换行或分散。 - 然后列出3-5个相关的问题。 - 不能换行分散,意思是问题列表应该连续输出,不能有其他内容干扰。 示例格式显示: - 先给出答案。 - 然后输出代码段(如果有)。 - 最后是 "§§相关问题§§",接着是编号列表的问题。 用户的问题是关于Java Swing的JPanel调用其他JPanel的动画。所以,我应该解释如何在Swing中实现一个JPanel触发另一个JPanel的动画。 核心概念:在Java Swing中,JPanel是一个容器。要调用其他JPanel的动画,通常涉及事件监听器或控制器来管理不同面板之间的交互。动画可能涉及重绘或使用Swing Timer来控制动画帧。 可能的解决方案: 1. 使用事件监听器:当一个JPanel中的事件(如按钮点击)发生时,触发另一个JPanel的动画方法。 2. 使用控制器类:创建一个中央控制器来控制所有面板的动画。 3. 直接调用方法:如果面板是可访问的,可以直接调用其动画方法,但这可能涉及线程问题(Swing事件调度线程)。 4. 使用SwingUtilities.invokeLater来确保UI更新在EDT上执行。 对于动画,通常使用javax.swing.Timer来定期更新UI。 我会给出一个简单的示例代码。既然用户提到了"调用其他的jPanel动画",我应该提供一个基本的代码示例来演示。 回答结构: - 先解释概念。 - 提供代码示例。 - 确保代码是Java代码。 - 然后结束回答,添加"§§相关问题§§"和3-5个相关问题。 问题:"java JPanel 怎么调用其他的jPanel动画"翻译为"如何在Java调用其他JPanel的动画"。 回答步骤: 1. 简要解释如何在Swing中实现交互。 2. 给出一个例子:比如,一个主JPanel有一个按钮,点击按钮时触发另一个JPanel的动画。 3. 提供代码片段。 代码示例: 假设有两个JPanel:MainPanel和AnimationPanel。 在MainPanel中,有一个按钮。当按钮点击时,它调用AnimationPanel的startAnimation方法。 AnimationPanel有自己的动画逻辑,可能使用Timer。 我会用中文注释来解释。 最后,添加相关问题。 相关问题必须基于用户的问题和我的回答。用户的问题是调用其他面板的动画,所以相关问題可以是: 1. 如何在Java Swing中实现动画效果? 2. 如何管理多个JPanel之间的通信? 3. 使用Swing Timer的优点是什么? 4. 如何确保动画在事件调度线程上运行? 5. 如何在JPanel中添加事件监听器? 我需要3-5个问题。 列出它们: 1. 如何创建一个基本的动画在JPanel中? 2. 在Java Swing中,如何将一个JPanel添加到另一个JPanel中? 3. 如何使用Swing Timer来控制动画速度? 4. 如何处理多线程在Swing动画中的应用? 5. 如何优化JPanel的动画性能? 确保问题数量在3到5之间。 现在,构建回答。 回答内容: 首先,解释:在Java Swing中,调用其他JPanel的动画通常通过在事件监听器中触发动画方法来实现。例如,一个面板中的按钮点击可以启动另一个面板的动画。 然后,提供代码示例: </think> 在Java Swing中,调用其他JPanel的动画通常通过事件监听器和控制器机制实现。核心思想是:当某个事件(如按钮点击)发生时,触发目标JPanel的动画方法。动画实现依赖于javax.swing.Timer来定期重绘UI,确保在事件调度线程(EDT)上执行以避免线程安全问题。下面是一个完整示例: 假设有两个JPanel:`MainPanel`(包含一个按钮用于触发动画)和`AnimationPanel`(实现动画逻辑)。通过按钮监听器调用`AnimationPanel`的`startAnimation()`方法。 ```java import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; // 动画面板类,实现一个简单的移动方块动画 class AnimationPanel extends JPanel { private int xPos = 50; private Timer timer; public AnimationPanel() { setPreferredSize(new Dimension(200, 200)); setBackground(Color.WHITE); } // 启动动画的方法 public void startAnimation() { if (timer != null && timer.isRunning()) { timer.stop(); } timer = new Timer(100, new ActionListener() { // 每100ms更新一次 @Override public void actionPerformed(ActionEvent e) { xPos += 5; // 向右移动 if (xPos > getWidth()) xPos = 0; // 循环移动 repaint(); // 触发重绘 } }); timer.start(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.BLUE); g.fillRect(xPos, 50, 50, 50); // 绘制蓝色方块 } } // 主面板类,包含按钮用于调用动画 class MainPanel extends JPanel { public MainPanel(AnimationPanel animPanel) { JButton startButton = new JButton("启动动画"); startButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { animPanel.startAnimation(); // 调用动画面板的动画方法 } }); add(startButton); } } // 主框架类,整合所有面板 public class MainFrame extends JFrame { public MainFrame() { setTitle("JPanel动画调用示例"); setSize(400, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); AnimationPanel animPanel = new AnimationPanel(); MainPanel mainPanel = new MainPanel(animPanel); add(mainPanel, BorderLayout.NORTH); // 主面板在上方 add(animPanel, BorderLayout.CENTER); // 动画面板在中间 } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { // 确保在EDT上运行 @Override public void run() { new MainFrame().setVisible(true); } }); } } ``` ### 关键步骤说明: 1. **事件触发**:在`MainPanel`中,按钮的`ActionListener`调用`AnimationPanel`的`startAnimation()`方法。 2. **动画实现**:`AnimationPanel`使用`Timer`周期性地更新位置并调用`repaint()`。 3. **线程安全**:所有UI操作通过`SwingUtilities.invokeLater()`确保在EDT上执行。 4. **面板布局**:使用`BorderLayout`或其他布局管理器在父容器中添加多个JPanel
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值