睡眠java后图像没有绘制

时间:2013-10-23 21:34:19

标签: java swing paint event-dispatch-thread thread-sleep

我正在尝试绘制背景图像,然后在其上绘制一个角色。我的代码工作,直到我加入睡​​眠,我没有得到1500 fps。

package sylvyrfysh.screen;

import javax.swing.*;
import java.awt.*;
import game.infos.Information;

public class ImageLoadDraw extends JFrame{
/**
 * 
 */
private static final long serialVersionUID = 1L;
public static void main(){
    DisplayMode dm=new DisplayMode(Information.sX,Information.sY,16,DisplayMode.REFRESH_RATE_UNKNOWN);
    ImageLoadDraw i = new ImageLoadDraw();
    i.run(dm);
}
public void run(DisplayMode dm){
    setBackground(Color.PINK);
    setForeground(Color.WHITE);
    setFont(new Font("Arial",Font.PLAIN,24));
    s=new Screen();
    try{
        loadpics();
        s.setFullScreen(dm,this);
        try{
            Thread.sleep(10000);
        }finally{
            doRun=false;
            s.restoreScreen();
        }
    }catch(Exception e){
        e.printStackTrace();
    }
}
private void loadpics() {
    bg=new ImageIcon("src/sylvyrfysh/screen/maze_icon.png").getImage();
    chara=new ImageIcon("src/sylvyrfysh/screen/char.png").getImage();
    repaint();
}
public void paint(Graphics g){
    if(g instanceof Graphics2D){
        Graphics2D g2=(Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    }
    while(true&&doRun){
        g.drawImage(bg,0,0,null);
        g.drawImage(bg,0,480,null);
        g.drawImage(bg,360,0,null);
        g.drawImage(bg,360,480,null);
        g.drawImage(bg,720,0,null);
        g.drawImage(bg,720,480,null);
        g.drawImage(chara,imgX,imgY,null);
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
private Screen s;
public static int imgX=0;
private int imgY=525;
private Image bg,chara;
private Boolean doRun=true;
}

关于导致它的原因和解决方法的任何想法?就像我说的那样,没有睡觉就行得很好。

3 个答案:

答案 0 :(得分:1)

Swing是一个单线程框架。这就是所有UI交互和修改都应该在事件调度线程的内容中执行,包括重绘请求。

任何停止,阻止或以其他方式阻止此线程运行的内容都将阻止EDT处理新事件,包括重绘请求。从本质上讲,这将使程序看起来已挂起(或停止响应),因为它有。

run方法中,您呼叫Thread.sleep。这可能会阻止EDT处理新事件,但是因为你实际上已经从“主”线程调用了该方法,所以它实际上可以工作,但是...

paint方法中,您有一个无限循环和一个Thread.sleep调用。这些将阻止EDT运行,因为在EDT的上下文中调用了paint

在某些系统上,绘图并不总是立即发生,直到paint方法返回,它可能不会被推送到设备进行输出,所以,即使是在paint内循环的想法无论它会给EDT带来什么问题,无论如何都是一个坏主意。

与其他一些UI框架不同,您不需要实现“主循环”,Swing会为您解决此问题。

相反,在paint方法中,您应该简单地描绘您为该周期绘制所需的内容并退出。

相反,你应该做点像......

public void paint(Graphics g){
    // Painting is a complex series of chained methods, failing to call super.paint
    // to cause significant issues
    super.paint(g);
    // Graphics is guaranteed to be an instance of Graphics2D since I think 1.4
    // You should create a copy, so any changes you make are not carried onto the
    // next component, Graphics is shared between all the components being painted
    // in this paint cycle.
    Graphics2D g2=(Graphics2D)g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    g2.drawImage(bg,0,0,null);
    g2.drawImage(bg,0,480,null);
    g2.drawImage(bg,360,0,null);
    g2.drawImage(bg,360,480,null);
    g2.drawImage(bg,720,0,null);
    g2.drawImage(bg,720,480,null);
    g2.drawImage(chara,imgX,imgY,null);
    // If you create, you should dispose of it...
    g2.dispose();
}

...代替

而不是Thread.sleep你应该使用像javax.swing.Timer这样的东西,例如......

s=new Screen();
try{
    loadpics();
    s.setFullScreen(dm,this);
    Timer timer = new Timer(10000, new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
            s.restoreScreen();
        }
    });
    timer.setRepeats(false);
    timer.start();
}catch(Exception e){
    e.printStackTrace();
}

请查看Concurrency in Swing了解详情。

您还应该避免覆盖顶级容器,尤其是覆盖paint。绘制一系列复杂的链式方法调用,每个调用执行一个特定的工作,相互叠加以产生最终结果。

相反,您应该从某种自定义组件开始,例如从JPanel延伸,并覆盖它的paintComponent方法,确保在执行任何操作之前调用super.paintComponent你自己画画。

请查看Performing Custom Painting了解详情

答案 1 :(得分:0)

这一个Thread.sleep(10000);关闭一切10秒钟。这不是你想要的。即使是30毫秒(约30帧/秒)也不是你想要的,因为它甚至会停止输入等。

你应该使用Timer。它在另一个线程中运行计时,并且它会在给定的milisec数量下自动休眠和唤醒该线程,因此它不会影响您的程序,并且只能在30milisec之后调用它。

仍然要有良好的应用程序,这个计时器应该具有较低的值,你应该计算通过System.nanoTime()多长时间,例如每30milisec重新绘制一次,但是每个5milisec读取输入等。

答案 2 :(得分:0)

我认为 - >这是因为你需要调用loadpics()并行,试试这个

public void run(DisplayMode dm){
    setBackground(Color.PINK);
    setForeground(Color.WHITE);
    setFont(new Font("Arial",Font.PLAIN,24));
    s=new Screen();
        loadpics();
        s.setFullScreen(dm,this);
        try{
            new Thread(new Runnable() {
      @Override
      public void run() {try {Thread.sleep(1000);doRun=false;s.restoreScreen();} catch (Exception e) {}
      }
    }).start();
}

还将doRun设置为易失性。 private Boolean doRun=true;