Java Swing 重绘延迟
Java Swing Repaint Latency
我有一个简单的绘画程序:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class CanvasPanel extends JFrame implements MouseMotionListener
{
private int x1, y1, x2, y2;
public CanvasPanel()
{
addMouseMotionListener(this);
setBounds(50,50,400,250);
setVisible(true);
}
public static void main(String[] argv)
{
new CanvasPanel();
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
g.setColor(Color.black);
g.drawLine(x1, y1, x2, y2);
}
public void mouseDragged(MouseEvent mouseEvent)
{
mouseEvent.consume();
int x = mouseEvent.getX();
int y = mouseEvent.getY();
if ( x1 == 0 )
{
x1 = x;
}
if ( y1 == 0 )
{
y1 = y;
}
x2 = x;
y2 = y;
repaint();
x1 = x2;
y1 = y2;
}
public void mouseMoved(MouseEvent me)
{
}
}
如果你 运行 它,你会发现用鼠标绘图时会有延迟,你画得越快,绘图就会分解成点。我该如何解决?我在想如果重绘速度超快,那么点应该形成直线和曲线。
画画要学的东西还是挺多的。我已经做了相当多的图形,所以我修改了你的程序来说明。
- 不要扩展
JFrame
除非您打算覆盖某些内容。很少需要。
- 在覆盖
paintComponent()
的 JPanel
中绘画。
- 每次通过
array
或 list
到 运行 收集您的积分。这是因为调用 super.paintComponent()
会清除屏幕(但必须这样做)。
- 不需要,但我发现在私有 class 中使用
MouseAdapter
(或任何 *-Adapter)来处理事件很方便。
- 由于 Swing 不是线程安全的,因此最好在 EDT 中启动它。只需确保在
EDT
期间没有进行长时间处理,否则您的程序将锁定并变得无响应。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CanvasPanel extends JPanel {
List<Point> points = new ArrayList<>();
// Use compositon over inheritance.
// Don't extend JFrame unless you plan to override something.
JFrame frame = new JFrame();
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new CanvasPanel());
}
public CanvasPanel() {
MyMouseListener ml = new MyMouseListener();
addMouseMotionListener(ml);
addMouseListener(ml);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(500, 500));
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
if (points.size() >= 2) {
Iterator<Point> it = points.iterator();
Point p1 = it.next();
while (it.hasNext()) {
Point p2 = it.next();
g.drawLine(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
}
// MouseAdapter provides dummy implementations
private class MyMouseListener extends MouseAdapter {
public void mouseDragged(MouseEvent me) {
points.add(me.getPoint());
repaint();
}
public void mousePressed(MouseEvent me) {
// initialize first point in list.
points.add(me.getPoint());
}
}
}
这个例子还有问题。例如松开鼠标按键,定位到新的位置再点击,就会从最后一点继续。可能有很多方法可以解决这个问题,但最简单的方法是拥有一个列表列表,其中每个列表都是一组独立的点,具有分离的起源。
我有一个简单的绘画程序:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class CanvasPanel extends JFrame implements MouseMotionListener
{
private int x1, y1, x2, y2;
public CanvasPanel()
{
addMouseMotionListener(this);
setBounds(50,50,400,250);
setVisible(true);
}
public static void main(String[] argv)
{
new CanvasPanel();
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
g.setColor(Color.black);
g.drawLine(x1, y1, x2, y2);
}
public void mouseDragged(MouseEvent mouseEvent)
{
mouseEvent.consume();
int x = mouseEvent.getX();
int y = mouseEvent.getY();
if ( x1 == 0 )
{
x1 = x;
}
if ( y1 == 0 )
{
y1 = y;
}
x2 = x;
y2 = y;
repaint();
x1 = x2;
y1 = y2;
}
public void mouseMoved(MouseEvent me)
{
}
}
如果你 运行 它,你会发现用鼠标绘图时会有延迟,你画得越快,绘图就会分解成点。我该如何解决?我在想如果重绘速度超快,那么点应该形成直线和曲线。
画画要学的东西还是挺多的。我已经做了相当多的图形,所以我修改了你的程序来说明。
- 不要扩展
JFrame
除非您打算覆盖某些内容。很少需要。 - 在覆盖
paintComponent()
的JPanel
中绘画。 - 每次通过
array
或list
到 运行 收集您的积分。这是因为调用super.paintComponent()
会清除屏幕(但必须这样做)。 - 不需要,但我发现在私有 class 中使用
MouseAdapter
(或任何 *-Adapter)来处理事件很方便。 - 由于 Swing 不是线程安全的,因此最好在 EDT 中启动它。只需确保在
EDT
期间没有进行长时间处理,否则您的程序将锁定并变得无响应。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CanvasPanel extends JPanel {
List<Point> points = new ArrayList<>();
// Use compositon over inheritance.
// Don't extend JFrame unless you plan to override something.
JFrame frame = new JFrame();
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new CanvasPanel());
}
public CanvasPanel() {
MyMouseListener ml = new MyMouseListener();
addMouseMotionListener(ml);
addMouseListener(ml);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(500, 500));
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
if (points.size() >= 2) {
Iterator<Point> it = points.iterator();
Point p1 = it.next();
while (it.hasNext()) {
Point p2 = it.next();
g.drawLine(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
}
// MouseAdapter provides dummy implementations
private class MyMouseListener extends MouseAdapter {
public void mouseDragged(MouseEvent me) {
points.add(me.getPoint());
repaint();
}
public void mousePressed(MouseEvent me) {
// initialize first point in list.
points.add(me.getPoint());
}
}
}
这个例子还有问题。例如松开鼠标按键,定位到新的位置再点击,就会从最后一点继续。可能有很多方法可以解决这个问题,但最简单的方法是拥有一个列表列表,其中每个列表都是一组独立的点,具有分离的起源。