zoukankan      html  css  js  c++  java
  • 【转载】在Java Swing中 实现双缓冲绘图

    这篇文章言简意赅把问题讲透了。

    https://blog.csdn.net/zhliro/article/details/45789657

    全文如下,以防原文遗失。

    使用Java实现双缓冲绘图
    当我们使用AWT或Swing绘图时,如果绘制的图像刷新太快,会出现屏闪现象,如之前写的俄罗斯方块小游戏,屏闪现象就很明显。虽然这种闪烁不会给程序的效果造成太大的影响,但给程序的使用者造成了些许不便,针对这种现象,我们大都是采取双缓冲的方式来解决的。双缓冲是计算机动画处理中的传统技术,在用其他语言编程时也可以实现。

    导致屏闪的原因

    拿上一篇文章中的俄罗斯方块来说明。当创建窗体对象后,显示窗口,程序首先自动调用paint(Graphics g)方法,在窗口上绘制方块与积分信息,在绘制方块下落的线程启动后,该线程每隔一定的时间就修改一下窗口中方块的位置,然后调用repaint()方法实现重绘。

    在repaint()方法中,会对我们重写的paint(Graphics g)进行调用,而在paint(Graphics g)方法中,首先调用了父类的paint()方法(不调用则在不同时刻绘制的方块会重叠在一起形成一条线),而父类paint()方法实现了对组件的清除,即用背景色填充整个窗体,然后会继续调用重写paint()方法中后继语句实现方块绘制。这样,我们就看到了一个在新的位置绘制的方块,前面的方块都被背景色覆盖掉了,这就一帧一帧切换显示,就实现了运动的动画效果。

    正是这种用背景色填充组件再重绘图像导致了闪烁。在两次看到处于不同位置方块的中间时刻,存在一个在短时间内被绘制出来的空白画面(即用背景色填充组件)。但即使时间很短,如果重绘的面积较大的话花去的时间也是很大的,这个时间甚至可以大到足以让闪烁严重到让人无法忍受的地步。

    我们知道了产生闪烁的原因,那么就可以有针对性的来解决问题,使用双缓冲是处理这类问题的传统技术。

    双缓冲原理

    先在内存中分配一个和我们动画窗口一样大的空间(缓冲区),然后利用getGraphics()方法去获得双缓冲画笔,接着利用双缓冲画笔在缓冲区中绘制我们想要的东西,最后将缓冲区一次性的绘制到窗体中显示出来,这样在我门的动画窗口上面显示出来的图像就非常流畅了。

    在swing中,组件本身就提供了双缓冲的功能,我们只需要进行简单的方法调用就可以实现组件的双缓冲(重写组件的paintComponent()方法),在awt中却没有提供此功能。

    示例

    先创建图像缓冲区:

    // 图像缓冲区
    private Image image;
    1
    2
    在缓冲区中绘制出要显示到窗口中的内容:

    /**
    * 绘制图形缓冲区内容
    */
    private void drawBufferedImage() {
    // 创建缓冲区对象
    image = createImage(this.getWidth(), this.getHeight());
    // 获取图像上下文对象
    Graphics g = image.getGraphics();

    g.setColor(getBackground());
    g.fillRect(0, 0, image.getWidth(null), image.getHeight(null));

    /* 绘制缓冲区内容 */
    // 绘制方块
    for (int i = 0; i < rows; i++) {
    for (int j = 0; j < columns; j++) {
    if (map[i][j] == State.ACTIVE) { // 绘制活动块
    g.setColor(activeColor);
    g.fillRoundRect(j * BLOCK_SIZE, i * BLOCK_SIZE + 25,
    BLOCK_SIZE - 1, BLOCK_SIZE - 1, BLOCK_SIZE / 5,
    BLOCK_SIZE / 5);
    } else if (map[i][j] == State.STOPED) { // 绘制静止块
    g.setColor(stopedColor);
    g.fillRoundRect(j * BLOCK_SIZE, i * BLOCK_SIZE + 25,
    BLOCK_SIZE - 1, BLOCK_SIZE - 1, BLOCK_SIZE / 5,
    BLOCK_SIZE / 5);
    }
    }
    }

    /* 打印得分 */
    g.setColor(scoreColor);
    g.setFont(new Font("Times New Roman", Font.BOLD, 30));
    g.drawString("SCORE : " + totalScore, 5, 70);

    // 游戏结束,打印结束字符串
    if (!isGoingOn) {
    g.setColor(Color.RED);
    g.setFont(new Font("Times New Roman", Font.BOLD, 40));
    g.drawString("GAME OVER !", this.getWidth() / 2 - 140,
    this.getHeight() / 2);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    在paint()方法中调用:

    /**
    * 将图像缓冲区内容绘制到窗体中
    */
    @Override
    public void paint(Graphics g) {
    drawBufferedImage();
    g.drawImage(image, 0, 0, this);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    其它代码基本不变。

    运行效果:

    完整源码:http://download.csdn.net/download/zhliro/8709373
    ————————————————
    版权声明:本文为CSDN博主「江湖人称小明」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zhliro/article/details/45789657

  • 相关阅读:
    java基础循环、条件语句、switch case
    java基础抽象类、接口、枚举、包
    java基础基本数据类型、变量类型、修饰符、运算符
    Mac权限问题,operation not permitted
    【比赛游记】NOIP2021 游记
    【比赛题解】NOIP2021 题解
    把LeetCode上的Sql题刷完了会有什么收获
    分析函数之Lead()、Lag()
    QT相关(c++)
    grpc
  • 原文地址:https://www.cnblogs.com/heyang78/p/15104192.html
Copyright © 2011-2022 走看看