You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Applet:如何为JButton添加双缓冲以消除闪烁

解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:20:10