可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a simple applet that animates a rectangle along the x-axis of the canvas. The problem is that it flickers. I have tried to Google this problem, but I didn't come up with anything useful or anything that I understood.
I am relatively new to Java.
Thanks!
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class simpleAnimation extends JApplet implements ActionListener {
Timer tm = new Timer(10, this);
int x = 0, velX = 2;
public void actionPerformed(ActionEvent event) {
if (x < 0 || x > 550){
velX = -velX;
}
x = x + velX;
repaint();
}
public void paint ( Graphics g ) {
super.paint(g);
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 30);
tm.start();
}
}
**********UPDATED CODE WITHOUT FLICKER**********
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
public class simpleAnimation extends JApplet implements ActionListener
{
Graphics bufferGraphics;
Image offscreen;
Dimension dim;
int x = 3, velX = 2;
Timer tm = new Timer(10, this);
public void init()
{
dim = getSize();
offscreen = createImage(dim.width,dim.height);
bufferGraphics = offscreen.getGraphics();
}
public void paint(Graphics g)
{
bufferGraphics.clearRect(0,0,dim.width,dim.width);
bufferGraphics.setColor(Color.red);
bufferGraphics.fillRect(x,50,50,20);
g.drawImage(offscreen,0,0,this);
tm.start();
}
public void update(Graphics g)
{
paint(g);
}
public void actionPerformed(ActionEvent evt)
{
if ( x < 0 || x > 550){
velX = -velX;
}
x = x + velX;
repaint();
}
}
I used this applet as a template.
回答1:
The problem is, top level containers like JApplet
aren't double buffered. This means that when it's updated, the screen flickers as each action is done directly onto the screen.
Instead, you should create a custom component, using something like a JPanel
, and override its paintComponent
method and perform your custom painting actions there.
Because Swing components are double buffered the results of the paint action are buffered before they are painted to the screen, making it a single action
Take a look at Performing Custom Painting for more details
回答2:
I always struggle with this concept of double buffering.
Here is my example that overrides paint() of the JApplet and a paintComponent() of a JPanel, which uses double buffering by default.
I don't see any difference in the apparent flickering.
//<applet code="SimpleAnimation.class" width="600" height="300"></applet>
import java.awt.*;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class SimpleAnimation extends JApplet implements ActionListener {
Timer tm = new Timer(10, this);
int x = 0, velX = 2;
JPanel panel;
public void init()
{
panel = new JPanel()
{
@Override
public Dimension getPreferredSize()
{
return new Dimension(50, 100);
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 30);
}
};
add(panel, BorderLayout.SOUTH);
tm.start();
}
public void actionPerformed(ActionEvent event) {
if (x < 0 || x > 550){
velX = -velX;
}
x = x + velX;
repaint();
// panel.repaint();
}
public void paint ( Graphics g ) {
super.paint(g);
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 30);
}
}
I test the code using: appletviewer SimpleAnimation.java
Is my concept of double buffering flawed, or my implementation, or both?
回答3:
What you're doing now works like this:
public void paint ( Graphics g ) {
// draw the entire area white
super.paint(g);
g.setColor(Color.RED);
// draw a rectangle at the new position
g.fillRect(x, 30, 50, 30);
}
So with every step, you first wipe out your rectangle, and then draw it fresh. Thus the flickering - the pixels under the rectangle keep changing from white to red to white to red to white to red...
Now observe that the smallest amount of painting you need to do is (supposing rectangle moves to the right) this:
- draw
velx
pixels on the left WHITE
- draw
velx
pixes on the right RED
If you do that, your animation will be smooth.
Computing that can be quite challenging though, especially when your shape is more complicated than just a square / your movement is more complex. That's where double buffering comes in.
With double buffering, you create an in-memory image that is the same size as your screen. You paint your entire theme there. Then you paint that image on your screen all at once.
When doing that, there won't be an intermediate step of "entire screen is WHITE"; thus no flickering. But note that you end up re-painting the entire screen rather than just the area that changed, which isn't ideal. Consider using clipping - a technique where you repaint a pre-defined area of the image and not the entire thing.