【问题标题】:Java Swing Repaint LatencyJava Swing 重绘延迟
【发布时间】:2019-11-27 23:28:32
【问题描述】:

我有一个简单的绘画程序:

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 这样的顶级容器上覆盖paint,而是使用Panel 并覆盖它的paintComponent 方法。理解绘画被认为是破坏性的,这意味着每次调用绘画时都希望您从头开始重新绘制组件状态,如果您不这样做会发生不好的事情

标签: java swing repaint


【解决方案1】:

绘画时有很多东西要学。我已经做了相当多的图形,所以我修改了你的程序来说明。

  1. 不要扩展JFrame,除非你打算覆盖某些东西。很少需要。
  2. 在覆盖paintComponent()JPanel 中进行绘画。
  3. arraylist 中收集您的积分,以便每次运行它们。这是因为调用super.paintComponent() 会清除屏幕(但必须这样做)。
  4. 不是必需的,但我发现在私有类中使用 MouseAdapter(或任何 *-Adapter)来处理事件很方便。
  5. 由于 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());
      }
   }
}

这个例子仍然有问题。例如,如果您释放鼠标按钮并定位到一个新位置然后单击,它将从最后一个点继续。可能有很多方法可以解决此问题,但最简单的方法是创建一个列表列表,其中每个列表是一组具有分离原点的单独点。

【讨论】:

  • 哇哦,这是一个很好的起点!你能解释一下你是如何固定虚线的吗?是什么原因造成的?
  • 你的想法是对的。从第一个点到下一个点画一条线,然后将下一个点设为第一个点并重复该过程。您只是没有正确更新它们。因为你没有做 super.paint() 它没有清除这条线,所以你不需要点列表。但是对于绘画,您需要保留所有内容并在每次调用绘画时重新绘制所有内容。我所做的是在 mousePressed 方法中初始化list。然后在拖动过程中获得连续的点。
  • 以下教程链接涵盖了包括绘画在内的各种主题。它最后一次更新为包含 Java 8,所以它已经过时了。 docs.oracle.com/javase/tutorial/index.html
猜你喜欢
  • 1970-01-01
  • 2013-10-14
  • 2014-07-12
  • 1970-01-01
  • 2012-01-25
  • 2017-04-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多