【问题标题】:The repaint method stops working for short delaysrepaint 方法会因短暂的延迟而停止工作
【发布时间】:2016-01-30 18:46:34
【问题描述】:

我正在尝试创建一个简单的面板,其中二维球上下弹跳。我无法让它工作,因为出于某种原因,我每秒不能多次调用 repaint 方法。设计基本上是有一个对象可以用move()的方法给予“冲动”。每次调用evaluatePosition 方法时,都会通过经过的时间、速度和加速度来计算当前位置。面板的代码是:

public class Display extends JPanel {
  private MovableObject object = new MovableObject(new Ellipse2D.Double(5,5,50,50));
  private static final int DELAY = 1000;

  public Display(){
    object.move(50,50);
    Timer timer = new Timer(DELAY, new ActionListener(){
      @Override
      public void actionPerformed(ActionEvent e){
        object.evaluatePosition();
        repaint();
      }
    });
    timer.start();
  }
}

@Override
public void paintComponent(Graphics g){
  super.paintComponent(g);
  Graphics2D g2 = (Graphics2D)g;
  g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  g.drawOval((int)object.getPosition().getX(), (int)object.getPosition.getY()
     (int)object.getShape().getWidth(), object.getShape().getHeight());
}

此代码适用于 DELAY=1000,但不适用于 DELAY=100 或 DELAY=10 等。我在这里阅读了一些示例代码,但在我看来它们都像我已经做过的一样。那么为什么我的代码不起作用?

编辑(2016-01-30): 由于这似乎确实是一个性能问题,这里是 MovableObject 的代码(我只是认为它无关紧要,你可能会明白为什么):

public class MovableObject {
  // I would really like to use Shape instead of Ellipse2D so that 
  // objects of any shape can be created
  private Ellipse2D.Double shape; 
  private Point position;
  // Vector is my own class. I want to have some easy vector addition and
  // multiplication and magnitude methods
  private Vector velocity = new Vector(0, 0);
  private Vector acceleration = new Vector(0, 0); 
  private Date lastEvaluation = new Date();

  public MovableObject(Ellipse2D.Double objectShape){
    shape = objectShape;
  }

  public void evaluatePosition(){
    Date currentTime = new Date();
    long deltaTInS = (currentTime.getTime()-lastEvaluation.getTime())/1000;
    // s = s_0 + v*t + 0.5*a*t^2
    position = new Point((int)position.getX()+ (int)(velocity.getX()*deltaTInS) + (int)(0.5*acceleration.getX()*deltaTInS*deltaTInS),
                        (int)position.getY()+ (int)(velocity.getY()*deltaTInS) + (int)(0.5*acceleration.getY()*deltaTInS*deltaTInS));
    lastEvaluation = currentTime;
  }
}

public void move(Vector vector){
    velocity = velocity.add(vector);
    evaluatePosition(); 
}

public Point getPosition(){
    return position;
}

public Ellipse2D.Double getShape(){
    return shape;
}

我的 move 方法不会改变位置而是改变速度。 请注意,我刚刚将形状对象从 Shape 更改为 Ellipse2D 以测试我的代码是否因为附加代码而出现性能问题。所以如果你认为这比它需要的更复杂:我实际上想增加一些复杂性,以便 MovableObject 可以具有任何形状子类的形状。我见过很多对我来说似乎更复杂且渲染速度更快的代码。所以我想知道这有什么问题(除了渲染椭圆有点过于复杂)。

【问题讨论】:

标签: java swing


【解决方案1】:

好的,这是一个简单的示例,基于您留下的脱离上下文的代码 sn-p 似乎没有任何问题。它具有由一个简单的滑块控制的变速...

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Display extends JPanel {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new Display());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    private MovableObject object = new MovableObject(new Ellipse2D.Double(5, 5, 50, 50));
    private int delay = 40;

    private Timer timer = new Timer(40, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            object.evaluatePosition(getSize());
            repaint();
        }
    });

    private JSlider slider = new JSlider(5, 1000);

    public Display() {
        object.move(50, 50);

        slider = new JSlider(5, 1000);
        slider.setSnapToTicks(true);
        slider.setMajorTickSpacing(10);
        slider.setMinorTickSpacing(5);
        setLayout(new BorderLayout());
        add(slider, BorderLayout.SOUTH);

        // This is simply designed to put an artificate delay between the
        // change listener and the time the update takes place, the intention
        // is to stop it from pausing the "main" timer...
        Timer delay = new Timer(250, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (timer != null) {
                    timer.stop();
                }
                timer.setDelay(slider.getValue());
                timer.start();
            }
        });

        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                delay.restart();
                repaint();
            }
        });

        slider.setValue(40);
    }

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

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.draw(object.getTranslatedShape());
        FontMetrics fm = g2.getFontMetrics();
        String text = Integer.toString(slider.getValue());
        g2.drawString(text, 0, fm.getAscent());
        g2.dispose();
    }

    public class MovableObject {

        private Shape shape;
        private Point location;

        private int xDelta, yDelta;

        public MovableObject(Shape shape) {
            this.shape = shape;
            location = shape.getBounds().getLocation();
            Random rnd = new Random();
            xDelta = rnd.nextInt(8);
            yDelta = rnd.nextInt(8);

            if (rnd.nextBoolean()) {
                xDelta *= -1;
            }
            if (rnd.nextBoolean()) {
                yDelta *= -1;
            }
        }

        public void move(int x, int y) {
            location.setLocation(x, y);
        }

        public void evaluatePosition(Dimension bounds) {
            int x = location.x + xDelta;
            int y = location.y + yDelta;

            if (x < 0) {
                x = 0;
                xDelta *= -1;
            } else if (x + shape.getBounds().width > bounds.width) {
                x = bounds.width - shape.getBounds().width;
                xDelta *= -1;
            }
            if (y < 0) {
                y = 0;
                yDelta *= -1;
            } else if (y + shape.getBounds().height > bounds.height) {
                y = bounds.height - shape.getBounds().height;
                yDelta *= -1;
            }

            location.setLocation(x, y);
        }

        public Shape getTranslatedShape() {
            PathIterator pi = shape.getPathIterator(AffineTransform.getTranslateInstance(location.x, location.y));
            GeneralPath path = new GeneralPath();
            path.append(pi, true);
            return path;
        }

    }
}

你也可以看看

更多示例...

【讨论】:

  • 问题是我的代码没有脱离上下文。我知道如何使用计时器,它可以延迟 1000 毫秒。它停止工作 100 毫秒的延迟,也只是 repaint() 方法。我看过你贴的例子。据我了解,这是一个性能问题,因为 paint() 方法在调用下一个 repaint() 之前没有完成。但我的代码不是以 25fps 渲染的 10,000 个对象,而是以 1fps 渲染的一个椭圆。我加了一些MovableObject的代码,但是真的很简单,所以我一开始没有加。
  • 好的,你已经提供了一些代码,它不完整,它不可编译和不可运行,它缺少重要的上下文,所以,它是脱离上下文的。我有一个可运行的示例,运行时间从 5 毫秒到 1000 毫秒,这比您提供给我们的要多。我已经看到重绘工作低至 1 毫秒。您是否尝试过运行我的示例代码?它奏效了吗?是否重复了您的问题?
  • 事件加上你的MovableObject,代码仍然无法运行,所以它仍然是脱离上下文的。考虑提供一个runnable example 来证明您的问题。这不是代码转储,而是您正在做的事情的一个例子,它突出了您遇到的问题。这将减少混乱并获得更好的响应
  • 我测试了long deltaTInS = (currentTime.getTime() - lastEvaluation.getTime()) / 1000;,它在 1000 毫秒时会产生 1,但在 100 毫秒时会产生 0,这意味着您的对象很可能(我无法实际测试)永远不会移动除非时间是 1000 毫秒,这对我来说,这不是绘画子系统的问题,而是你的代码,你似乎没有尝试调试(一个简单的 System.out.println 会突出显示这个问题)
  • 天哪,就是这样。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2021-02-28
  • 1970-01-01
  • 2017-11-01
  • 2013-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多