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

优化JFrame重绘的方法:解决移动图形时屏幕频繁闪白的问题

问题原因

Swing 直接重写JFrame的paint方法时,默认重绘流程会先清空整个窗口背景,再绘制窗口边框、组件,最后才执行你写的图形绘制逻辑,背景清空的瞬间就会出现白屏闪烁。

可行解决方案

核心思路是用自带双缓冲的JPanel作为绘图容器,替换直接在JFrame上绘图的逻辑,双缓冲会先把所有图形绘制到后台缓冲区,绘制完成后一次性替换到屏幕上,完全消除闪烁。

修改步骤

  1. 移除JFrame的paint重写逻辑,新增自定义DrawPanel类继承JPanel,所有绘图逻辑放在DrawPanel的paintComponent方法中
  2. 将DrawPanel添加到JFrame的内容窗格,JPanel默认开启双缓冲,可显式声明加强兼容性
  3. 优化冗余代码:Figure是引用类型,从数组取出修改属性后不需要再调用set放回数组,原对象已经被修改

修改后完整代码

figurestest/Figure.java(无变动)

package figurestest;
import java.awt.Graphics;

public abstract class Figure {
    public int x, y;
    public int w, h;

    public Figure (int x, int y, int w, int h) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }

    public abstract void paint (Graphics g);
    public abstract void mov (int dx, int dy);
}

figurestest/Rect.java(修正类名首字母大写,无逻辑变动,注意和文件名保持一致)

package figurestest;

import java.awt.*;

public class Rect extends Figure {

    public Rect (int x, int y, int w, int h) {
        super(x, y, w, h);
    }

    public void paint (Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.fillRect(this.x, this.y, this.w, this.h);
        g2d.drawRect(this.x,this.y, this.w,this.h);
    }

    public void mov (int dx, int dy){
        this.x += dx;
        this.y += dy;
    }
}

testapp.java(核心修改)

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Random;

import figurestest.*;

class TestApp {
    public static void main (String[] args) {
        ListFrame frame = new ListFrame();
        frame.setVisible(true);
    }
}

// 自定义绘图面板
class DrawPanel extends JPanel {
    ArrayList<Figure> figs;
    public DrawPanel(ArrayList<Figure> figs) {
        this.figs = figs;
        // 显式开启双缓冲
        setDoubleBuffered(true);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // 所有图形绘制在这里执行
        for (Figure fig: figs) {
            fig.paint(g);
        }
    }
}

class ListFrame extends JFrame {
    ArrayList<Figure> figs = new ArrayList<Figure>();
    Random rand = new Random();
    DrawPanel drawPanel;

    ListFrame () {
        // 初始化绘图面板并添加到窗口
        drawPanel = new DrawPanel(figs);
        add(drawPanel, BorderLayout.CENTER);
        // 让面板获取焦点响应键盘事件
        drawPanel.setFocusable(true);
        drawPanel.requestFocus();

        this.addWindowListener (
            new WindowAdapter() {
                public void windowClosing (WindowEvent e) {
                    System.exit(0);
                }
            }
        );

        // 键盘事件绑定到绘图面板,避免焦点丢失
        drawPanel.addKeyListener (
            new KeyAdapter() {
                public void keyPressed (KeyEvent evt) {
                    Dimension size = drawPanel.getSize();
                    
                    int x = rand.nextInt(size.width);
                    int y = rand.nextInt(size.height);
                    int w = 5+ rand.nextInt(50);
                    int h = 5+ rand.nextInt(50);
                    if (evt.getKeyChar() == 'r') {
                        figs.add(new Rect(x,y, w,h));
                    } 
                    else if (evt.getKeyCode() == KeyEvent.VK_DOWN || evt.getKeyCode() == KeyEvent.VK_LEFT || evt.getKeyCode() == KeyEvent.VK_RIGHT || evt.getKeyCode() == KeyEvent.VK_UP ) {
                        if (figs.size() > 0){
                            int dx = 0;
                            int dy = 0;
                            if(evt.getKeyCode() == KeyEvent.VK_DOWN) dy = 2;
                            else if (evt.getKeyCode() == KeyEvent.VK_UP) dy = -2;
                            else if (evt.getKeyCode() == KeyEvent.VK_RIGHT) dx = 2;
                            else if (evt.getKeyCode() == KeyEvent.VK_LEFT) dx = -2;
                            
                            // 引用类型直接修改即可,不需要set回数组
                            Figure fig = figs.get(figs.size() -1);
                            fig.mov(dx,dy);
                        }
                    }
                    // 只重绘画板,不需要重绘整个窗口
                    drawPanel.repaint(); 
                }
            }
        );

        this.setTitle("Figures");
        this.setSize(350, 350);
    }
    // 移除原JFrame的paint重写
}

额外优化建议

如果后续图形数量较多还可以进一步优化:调用repaint(Rectangle bounds)只重绘图形变化的区域,不需要重绘整个面板,进一步提升流畅度。

内容的提问来源于stack exchange,提问作者Leonardo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 12:27:05