![](/img/trans.png)
[英]Why does Swing in my Java Applet flicker on fast mouse over?
[英]Java Swing painting & Mouse event flicker
我有一个扩展JPanel的类(如下),该面板位于JScrollPane中。 它侦听(自己)鼠标事件,并尝试重新定位自身(在拖动时)和重新缩放自身(在滚轮上)以模拟鼠标的移动和缩放。 该面板还负责我的应用程序的主要视觉输出。 它存储一个呈现在JScrollPane的可视区域(但在面板的图形上)的BufferedImage。 图像的大小和形状将保持与可视区域匹配。
我的问题就是这样。
1)在发生鼠标事件时,会出现大量闪烁,并且性能会下降2)如果我用自己的绘画方法覆盖了paint或paintComponent方法,这是摆脱闪烁和其他绘画问题的理想方法,那么我仍然会得到相同的闪烁从具有透明区域的已加载图像绘制的效果和图形,然后将该区域涂成黑色。 当我手动调用我的paint方法而不覆盖paint和paintComponent方法时,仍然出现闪烁,但透明区域显示正确。
我是Swing绘画的新手,显然做错了什么,有人可以指出正确的方向来解决此问题吗?
谢谢
import jSim.simulation.Simulation;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.event.MouseInputListener;
public class SimPanel extends JPanel implements MouseWheelListener, MouseInputListener {
//Simulation
Simulation sim;
//Viewer
JViewport viewport;
Dimension viewSize;
BufferStrategy strat;
//Drawing
Image renderImage;
Graphics2D g2d;
boolean draw = true;
double scale = 1.0;
Object drawLock = new Object();
//Mouse events
int m_XDifference, m_YDifference;
public SimPanel(JViewport viewport) {
this.viewport = viewport;
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.addMouseWheelListener(this);
//this.setup();
}
public SimPanel(Simulation sim, JViewport viewport) {
this.sim = sim;
this.viewport = viewport;
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.addMouseWheelListener(this);
//this.setup();
}
//Used to initialise the buffered image once drawing begins
private void setup() {
synchronized (drawLock) {
viewSize = viewport.getExtentSize();
renderImage = new BufferedImage(viewSize.width, viewSize.height, BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) renderImage.getGraphics();
}
}
// @Override
// public void paint(Graphics g)
// {
// synchronized(drawLock) {
// //super.paintComponent(g);
// paintSimulation();
// }
// }
//Paint the screen for a specific simulation
public void paintSimulation(Simulation sim) {
synchronized (drawLock) {
setSimulation(sim);
paintSimulation();
}
}
//Paint the screen with the panels simulation
public void paintSimulation() {
synchronized (drawLock) {
//if no image, then init
if (renderImage == null) {
setup();
}
//clear the screen
resetScreen();
//draw the simulation if not null, to the image
if (sim != null) {
sim.draw(this);
}
//paint the screen with the image
paintScreen();
}
}
private void resetScreen() {
Dimension newSize = viewport.getExtentSize();
if (viewSize.height != newSize.height || viewSize.width != newSize.width || renderImage == null) {
//System.out.println("Screen Size Changed: " + viewSize + " " + newSize);
viewSize = newSize;
renderImage = new BufferedImage(viewSize.width, viewSize.height, BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) renderImage.getGraphics();
} else {
g2d.setBackground(Color.DARK_GRAY);
g2d.clearRect(0, 0, (int) (viewSize.width), (int) (viewSize.height));
}
}
private void paintScreen() {
Graphics g;
Graphics2D g2;
try {
//g = viewport.getGraphics();
g = this.getGraphics();
g2 = (Graphics2D) g;
if ((g != null) && (renderImage != null)) {
g2.drawImage(renderImage, (int) viewport.getViewPosition().getX(), (int) viewport.getViewPosition().getY(), null);
}
Toolkit.getDefaultToolkit().sync(); // sync the display on some systems
g.dispose();
g2.dispose();
this.revalidate();
} catch (Exception e) {
System.out.println("Graphics context error: " + e);
}
}
//Simulation makes calls to this method to draw items on the image
public void draw(BufferedImage image, int x, int y, Color colour) {
synchronized (drawLock) {
Rectangle r = viewport.getViewRect();
if (g2d != null && draw) {
Point p = new Point((int) (x * scale), (int) (y * scale));
if (r.contains(p)) {
if (scale < 1) {
Graphics2D g2 = (Graphics2D) image.getGraphics();
Image test = image.getScaledInstance((int) (image.getWidth(null) * scale), (int) (image.getHeight(null) * scale), Image.SCALE_FAST);
g2d.drawImage(test, (int) ((x * scale - r.x)), (int) ((y * scale - r.y)), null);
} else {
g2d.drawImage(image, x - r.x, y - r.y, null);
}
}
}
}
}
public void setDraw(boolean draw) {
this.draw = draw;
}
public void setSimulation(Simulation sim) {
synchronized (drawLock) {
if (!(this.sim == sim)) {
this.sim = sim;
}
}
}
public void mouseWheelMoved(MouseWheelEvent e) {
synchronized (drawLock) {
updatePreferredSize(e.getWheelRotation(), e.getPoint());
}
}
private void updatePreferredSize(int wheelRotation, Point stablePoint) {
double scaleFactor = findScaleFactor(wheelRotation);
if (scale * scaleFactor < 1 && scale * scaleFactor > 0.05) {
scaleBy(scaleFactor);
Point offset = findOffset(stablePoint, scaleFactor);
offsetBy(offset);
this.getParent().doLayout();
}
}
private double findScaleFactor(int wheelRotation) {
double d = wheelRotation * 1.08;
return (d > 0) ? 1 / d : -d;
}
private void scaleBy(double scaleFactor) {
int w = (int) (this.getWidth() * scaleFactor);
int h = (int) (this.getHeight() * scaleFactor);
this.setPreferredSize(new Dimension(w, h));
this.scale = this.scale * scaleFactor;
}
private Point findOffset(Point stablePoint, double scaleFactor) {
int x = (int) (stablePoint.x * scaleFactor) - stablePoint.x;
int y = (int) (stablePoint.y * scaleFactor) - stablePoint.y;
return new Point(x, y);
}
private void offsetBy(Point offset) {
Point location = viewport.getViewPosition();
//this.setLocation(location.x - offset.x, location.y - offset.y);
viewport.setViewPosition(new Point(location.x + offset.x, location.y + offset.y));
}
public void mouseDragged(MouseEvent e) {
synchronized (drawLock) {
//Point p = this.getLocation();
Point p = viewport.getViewPosition();
int newX = p.x - (e.getX() - m_XDifference);
int newY = p.y - (e.getY() - m_YDifference);
//this.setLocation(newX, newY);
viewport.setViewPosition(new Point(newX, newY));
//this.getParent().doLayout();
}
}
public void mousePressed(MouseEvent e) {
synchronized (drawLock) {
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
m_XDifference = e.getX();
m_YDifference = e.getY();
}
}
public void mouseReleased(MouseEvent e) {
synchronized (drawLock) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
public void mouseClicked(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet.");
}
public void mouseEntered(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet.");
}
public void mouseExited(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet.");
}
public void mouseMoved(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet.");
}
}
简而言之,查找双重缓冲。
更长的答案...
覆盖paintComponent。 创建一个屏幕外的图形对象。 在该物体上绘画。 将其复制到传递到paint方法的图形对象中。
哦,摆脱所有同步。 不用了
如果在视口上添加setOpaque(true),则通知Swing您将自己完成所有绘画(尤其是背景)。 这可能已经有所帮助。
编辑
我环顾了四周,并认为您应该重写paintComponent。
您可能有2张图片和4个参考:
您可以重写paintComponent来绘制背景,然后绘制drawImage(imageToPaint)(如果它不为null,则不应为null)
您将拥有一个执行imageToWriteTo的自定义绘制的线程。 最后,它交换imageToPaint和imageToWriteTo。
然后,您调用repaint()。 这要求重新绘制,其附加优点是将Swing队列上的所有重新绘制请求放在一起,并产生单个绘制。 请不要重新验证或同步。 重新绘制是在您的第二个线程“事件调度线程”上自动完成的。
这样,更新仿真图像就与实际绘画分离了,并且绘画更新无需等待模拟完成绘制。 这可能是由于图像有些过时(在使用缓冲时有些隐含),但应产生更好的结果。
简而言之,对imageToWriteTo进行了昂贵的编写。 使用imageToPaint完成绘制。 昂贵的写入以交换imageToWriteTo和imageToPaint结尾。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.