
本文旨在帮助开发者理解为什么`repaint()`方法可能无法按预期调用`paintComponent()`方法,并提供使用Swing Timer的解决方案,确保UI的正确更新和渲染。我们将深入探讨Swing的线程安全性和被动渲染机制,并通过代码示例展示如何避免常见错误,实现流畅的动画效果。
在Swing应用开发中,repaint()和paintComponent()方法是实现自定义绘制和动画效果的关键。然而,初学者经常遇到repaint()调用后paintComponent()没有被执行的问题。这通常与Swing的线程模型和渲染机制有关。
Swing的线程安全性和事件分发线程(Event Dispatching Thread)
Swing是单线程的,这意味着所有UI更新都应该在事件分发线程(EDT)上执行。长时间运行或阻塞的操作在EDT上会导致UI冻结。因此,避免在EDT上执行耗时操作至关重要。
Swing不是线程安全的,这意味着从EDT之外更新UI或UI所依赖的状态会导致不可预测的结果。
Swing的被动渲染机制
Swing使用被动渲染引擎。这意味着你不能直接控制绘画过程。相反,你需要通过调用repaint()来请求重绘,Swing系统会在适当的时候调用paintComponent()方法。
解决方案:使用Swing Timer
解决repaint()不调用paintComponent()问题的常用方法是使用javax.swing.Timer。Timer类允许你以指定的时间间隔在EDT上执行代码。这可以确保UI更新是线程安全的,并且不会阻塞EDT。
企业网站通用源码1.0
企业网站通用源码是以aspcms作为核心进行开发的asp企业网站源码。企业网站通用源码是一套界面设计非常漂亮的企业网站源码,是2016年下半年的又一力作,适合大部分的企业在制作网站是参考或使用,源码亲测完整可用,没有任何功能限制,程序内核使用的是aspcms,如果有不懂的地方或者有不会用的地方可以搜索aspcms的相关技术问题来解决。网站UI虽然不是特别细腻,但是网站整体格调非常立体,尤其是通观全
0 查看详情
以下是一个使用Swing Timer的示例:
import java.awt.Color;import java.awt.Dimension;import java.awt.EventQueue;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.Timer;public class Main { public static void main(String[] args) { new Main(); } public Main() { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame frame = new JFrame(); frame.add(new GamePane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class GamePane extends JPanel { final int ORIGINAL_TILE_SIZE = 16; final int SCALE = 3; final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE; final int MAX_SCREEN_COLUMNS = 16; final int MAX_SCREEN_ROWS = 12; final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS; final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS; private Timer timer; @Override public Dimension getPreferredSize() { return new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT); } @Override public void addNotify() { super.addNotify(); if (timer != null) { timer.stop(); } timer = new Timer(5, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { update(); } }); timer.start(); } @Override public void removeNotify() { super.removeNotify(); if (timer != null) { timer.stop(); } } public void update() { System.out.println("Updatey update"); repaint(); } public void paintComponent(final Graphics g) { super.paintComponent(g); System.out.println("Painty paint paint"); Graphics2D g2 = (Graphics2D) g.create(); g2.setColor(Color.white); g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE); g2.dispose(); } }}
在这个例子中,Timer以5毫秒的间隔触发ActionListener,其中调用了update()方法。update()方法然后调用repaint(),这反过来触发paintComponent()方法。
代码解释:
EventQueue.invokeLater(…): 确保JFrame的创建和显示在EDT上进行。GamePane类继承自JPanel,负责绘制内容。getPreferredSize(): 设置JPanel的首选大小,确保JFrame能够正确显示。addNotify(): 当JPanel被添加到容器时调用,在这里启动Timer。removeNotify(): 当JPanel从容器中移除时调用,在这里停止Timer。Timer timer = new Timer(5, …): 创建一个Timer,每隔5毫秒触发一次ActionListener。update(): 更新游戏逻辑,并调用repaint()请求重绘。paintComponent(): 执行实际的绘制操作。
注意事项
不要阻塞EDT: 避免在EDT上执行耗时操作。使用SwingWorker或其他线程机制来执行后台任务。线程安全: 确保所有UI更新都在EDT上执行。正确使用repaint(): 在需要更新UI时调用repaint()。Swing系统会处理实际的重绘过程。资源释放: 在paintComponent()方法中,使用g.create()创建Graphics2D对象后,务必使用g2.dispose()释放资源。
总结
理解Swing的线程模型和渲染机制是编写健壮的Swing应用程序的关键。通过使用Swing Timer和遵循线程安全的最佳实践,你可以避免repaint()不调用paintComponent()的问题,并创建流畅的动画效果。记住,始终在EDT上执行UI更新,并避免阻塞EDT。
以上就是解决repaint()方法不调用paintComponent()的问题的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/716262.html
微信扫一扫
支付宝扫一扫