Java Applet:如何为JButton添加双缓冲以消除闪烁
这个问题的核心在于你把手动AWT双缓冲和Swing组件的绘制机制混在一起了,而且还有个关键错误:你在draw方法里重复添加按钮——这个方法会被频繁调用,每次调用都add按钮会导致界面上堆积大量重复组件,既浪费资源又加剧闪烁。下面给你两种解决方案,优先推荐第一种,因为它能保留按钮的完整交互性:
方案一:用Swing内置双缓冲重构代码(推荐)
Swing的组件(比如JPanel)默认已经开启了双缓冲,完全不需要你手动创建离屏Image。我们可以把游戏绘制逻辑放到一个自定义JPanel中,再把按钮添加到这个面板里,让Swing统一处理所有绘制的双缓冲。
步骤1:创建带双缓冲的游戏面板
这个面板负责游戏内容的绘制,Swing会自动帮我们处理双缓冲:
import javax.swing.*; import java.awt.*; class GamePanel extends JPanel implements Runnable { private Thread gameThread; private static final int WIDTH = 700, HEIGHT = 500; public GamePanel() { setPreferredSize(new Dimension(WIDTH, HEIGHT)); setDoubleBuffered(true); // 显式开启双缓冲(Swing默认已开启,此步可选但更保险) setLayout(null); // 和你之前的布局方式保持一致 } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 必须调用,确保面板正确清空背景 // 这里写你的游戏绘制逻辑 g.setColor(Color.BLACK); g.fillRect(0, 0, WIDTH, HEIGHT); g.setColor(Color.WHITE); g.fillRect(50, 50, 100, 100); g.setFont(new Font("Century", Font.BOLD, 30)); g.drawString("I feel good sometimes I don't", 200, 200); } public void startGameLoop() { gameThread = new Thread(this); gameThread.start(); } @Override public void run() { while (true) { repaint(); // 触发面板重绘 try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } }
步骤2:修改Applet类,整合面板和按钮
改用JApplet(Swing版本的Applet),把游戏面板设为内容面板,然后一次性添加按钮(只在init里做一次):
import javax.swing.*; import java.awt.*; public class AppletTest extends JApplet { @Override public void init() { GamePanel gamePanel = new GamePanel(); setContentPane(gamePanel); // 初始化按钮,只做一次! JButton button1 = new JButton("Your Button Text"); button1.setBounds(225, 400, 250, 50); button1.setFont(new Font("Courier", Font.PLAIN, 17)); button1.setForeground(Color.WHITE); button1.setBackground(Color.DARK_GRAY); gamePanel.add(button1); // 启动游戏循环 gamePanel.startGameLoop(); } }
为什么这个方案有效?
- Swing的
JPanel内置双缓冲,paintComponent里的绘制不会闪烁; - 按钮作为Swing组件,会被Swing用双缓冲机制统一绘制,和游戏内容同步;
- 按钮只初始化添加一次,避免了重复创建组件的问题。
方案二:把按钮绘制到手动离屏缓冲区(不推荐)
如果你坚持要用自己的手动双缓冲,也可以把按钮“画”到离屏Image里,但这样按钮会失去所有交互性(不能点击、没有悬停效果等),因为它只是一张静态图片:
import java.applet.Applet; import java.awt.*; import javax.swing.JButton; public class AppletTest extends Applet implements Runnable { Thread thread; Image img; Graphics gfx; public final int WIDTH = 700, HEIGHT = 500; JButton button1; public void init() { this.resize(WIDTH, HEIGHT); thread = new Thread(this); thread.start(); img = createImage(WIDTH, HEIGHT); gfx = img.getGraphics(); // 初始化按钮,但不要添加到Applet button1 = new JButton("Static Button"); button1.setBounds(225, 400, 250, 50); button1.setFont(new Font("Courier", Font.PLAIN, 17)); button1.setForeground(Color.WHITE); button1.setBackground(Color.DARK_GRAY); } public void draw(Graphics g) { gfx.setColor(Color.BLACK); gfx.fillRect(0, 0, WIDTH, HEIGHT); gfx.setColor(Color.WHITE); gfx.fillRect(50, 50, 100, 100); gfx.setFont(new Font("Century", Font.BOLD, 30)); gfx.drawString("I feel good sometimes I don't", 200, 200); // 把按钮绘制到离屏缓冲区 button1.paint(gfx); g.drawImage(img, 0, 0, this); } public void update(Graphics g) { draw(g); } public void run() { while(true) { repaint(); try { Thread.sleep(5); } catch(InterruptedException e) { e.printStackTrace(); } } } }
注意
这种方式下按钮只是一张静态图像,无法响应用户的点击、悬停等操作,所以只适合不需要交互的“按钮样式”元素。
内容的提问来源于stack exchange,提问作者MattGotJava
相关产品推荐
相关产品推荐

