简体   繁体   English

如何消除paint() 方法中的闪烁

[英]How do you remove the flickering in the paint() method

I've had the flickering problem for a while and I would like to know if there is a simple way to solve the problem.我有一段时间的闪烁问题,我想知道是否有一种简单的方法可以解决这个问题。 I know you can use the BufferStrategy but I can not find a good, updated source that work for.png images.我知道您可以使用 BufferStrategy,但我找不到适用于 .png 图像的良好更新源。 The flickering occures when I move my character or when the repaint() method is called.当我移动我的角色或调用 repaint() 方法时会发生闪烁。

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class Game extends JFrame implements Runnable, KeyListener {

    private static final long serialVersionUID = 1L;

    Image back = new ImageIcon(Game.class.getResource("LEVEL1.png")).getImage();
    Image player = new ImageIcon(Game.class.getResource("freddyD.png")).getImage();
    Image studio = new ImageIcon(Game.class.getResource("studio0.png")).getImage();

    public int xPos = 100;
    public int yPos = 300;
    public boolean D = false;
    public boolean Q = false;

    public Game() {

        addKeyListener(this);
        setSize(900, 700);
        setLocationRelativeTo(null);
        setVisible(true);

    }

    public void paint(Graphics g) {

        g.drawImage(back, 0, 0, 6000, 6000, this);
        g.drawImage(studio, 100, 100, 340, 310, this);
        g.drawImage(player, xPos, yPos, 60, 104, this);

    }

    public void keyTyped(KeyEvent e) {

    }

    public void keyPressed(KeyEvent e) {

        int k = e.getKeyCode();

        if (k == KeyEvent.VK_D) {
            D = true;
        }

        if (k == KeyEvent.VK_Q) {
            Q = true;
        }
    }

    public void keyReleased(KeyEvent e) {

        int k = e.getKeyCode();

        if (k == KeyEvent.VK_D) {
            D = false;
        }

        if (k == KeyEvent.VK_Q) {
            Q = false;
        }

    }

    public void run() {

        while (true) {

            if (D) {

                xPos += 2;
                repaint();
            }

            if (Q) {

                xPos -= 2;
                repaint();
            }

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String args[]) {
        new Game().start();
    }

    public void start() {
        new Thread(this).start();
    }
}

This is a relative common issue.这是一个相对普遍的问题。 Top level containers, like JFrame , are not double buffered.顶级容器,例如JFrame ,不是双缓冲的。 This means that each change made to the graphics context will be applied immediately, which is causing the flickering.这意味着对图形上下文所做的每个更改都将立即应用,这会导致闪烁。

As a general rule of thumb you shouldn't:作为一般经验法则,您不应该:

  • Override paint of any component覆盖任何组件的paint
  • Paint directly to top level containers直接绘制到顶级容器

JFrame is actually a compound component, mean it has several other child components which are used to generate the general layout, and, because of the way the painting sub system works, these child components can be painted independently (or without notification to) the frame. JFrame实际上是一个复合组件,意味着它还有其他几个用于生成总体布局的子组件,并且由于绘制子系统的工作方式,这些子组件可以独立绘制(或不通知)框架.

Start by reading through Performing Custom Painting and Painting in AWT and Swing for some more details about how painting in Swing works and how you should work with it.首先阅读在 AWT 和 Swing执行自定义绘画和绘画,了解有关 Swing 中的绘画如何工作以及如何使用它的更多详细信息。

As a general recommendation, you should start with a JPanel and override it's paintComponent method (don't forget to call super.paintComponent )作为一般建议,您应该从JPanel开始并覆盖它的paintComponent方法(不要忘记调用super.paintComponent

Remember, Swing is not thread safe, so you need to take that into consideration.请记住,Swing 不是线程安全的,因此您需要考虑到这一点。 You might consider using something like aSwing Timer instead of a Thread , if the frame rate is not super critical.如果帧速率不是超级关键,您可以考虑使用类似Swing Timer而不是Thread的东西。 A Swing Timer will ensure that all "tick" events take place in the Event Dispatching Thread, making it safer to update the UI (and the state the UI depends on). Swing Timer将确保所有“滴答”事件都发生在事件调度线程中,从而更安全地更新 UI(以及 UI 所依赖的 state)。

If you need more direct control, then you will need to consider using a BufferStrategy , which will give you direct control over when something is painted, allowing you to better control the painting process.如果您需要更直接的控制,那么您将需要考虑使用BufferStrategy ,它可以让您直接控制何时绘制某些东西,从而让您更好地控制绘制过程。

KeyListener is generally a poor choice for this type of task, for a number of reasons.出于多种原因,对于此类任务, KeyListener通常是一个糟糕的选择。 Instead you should be using Key Bindings API which solves these short comings.相反,您应该使用Key Bindings API来解决这些缺点。

For example...例如...

This is just a simple paint example to get you started...这只是一个简单的绘画示例,可帮助您入门...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Game {

    public static void main(String[] args) {
        new Game();
    }

    public Game() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Rectangle hBox;
        private Rectangle vBox;

        private int hDelta = 1;
        private int vDelta = 1;

        public TestPane() {
            hBox = new Rectangle(0, 0, 10, 10);
            vBox = new Rectangle(0, 0, 10, 10);
            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    hBox.x += hDelta;
                    hBox.y = (getHeight() - 10) / 2;

                    if (hBox.x + hBox.width >= getWidth()) {
                        hBox.x = getWidth() - hBox.width;
                        hDelta *= -1;
                    } else if (hBox.x <= 0) {
                        hBox.x = 0;
                        hDelta *= -1;
                    }

                    vBox.y += vDelta;
                    vBox.x = (getWidth() - vBox.width) / 2;
                    if (vBox.y + vBox.height >= getHeight()) {
                        vBox.y = getHeight() - vBox.height;
                        vDelta *= -1;
                    } else if (vBox.y <= 0) {
                        vBox.y = 0;
                        vDelta *= -1;
                    }
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.RED);
            g2d.fill(hBox);
            g2d.setColor(Color.BLUE);
            g2d.fill(vBox);
            g2d.dispose();
        }

    }
}

The thing I always do that doesn't take that much time is just increase the time in Thread.sleep() .我总是做的不需要那么多时间的事情就是增加Thread.sleep()的时间。 The golden number that I found to work while being low is 15 millis;我发现在低谷时工作的黄金数字是 15 毫; anything lower than that starts to flicker.低于该值的任何东西都会开始闪烁。 In your code, you use 10 millis, which might be the issue.在您的代码中,您使用 10 毫秒,这可能是问题所在。 If you don't want to do that (for speed reasons or whatever), use BufferedImage .如果您不想这样做(出于速度原因或其他原因),请使用BufferedImage It's pretty complicated, but if you want to know more here is a good site .这很复杂,但是如果您想了解更多信息,这里是一个很好的网站 The main thing is that BufferedImage .最主要的是BufferedImage

This kind of issues can happen when you use single buffer for drawing.当您使用单个缓冲区进行绘图时,可能会发生此类问题。 Check double buffering, it should solve your problem: https://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html检查双缓冲,它应该可以解决您的问题: https://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.ZFC35FDC70D5FC69D269883A822C7A53

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM