【问题标题】:Why this Simple Loop is Causing Problematic Behavior of my JFrame为什么这个简单的循环会导致我的 JFrame 出现问题
【发布时间】:2017-03-23 22:31:49
【问题描述】:

我正在完成 2007 年编写的 Java Exposure 教科书的作业。这本书包含一些我通常会更新以使用一些更新的功能(只是基本内容)的代码。然而,在这个我遇到了一个问题。我所做的只是将show 替换为setVisible(true) 并将Frame 更改为JFrame 并添加gfx.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);。但是,我注意到这实际上不会导致窗口关闭。如果我点击了很多次,也许 1/30 次尝试它就会关闭。如果我将延迟从 10 减少到 1,它通常会在 2 次尝试内关闭。这当然让我相信 delay 方法导致了这种不稳定的行为。我试过Thread.sleep,但当然没用。 是否有任何简单的方法来获取此代码,以便在我点击关闭按钮时框架将关闭?如果没有,那么更简单的方法是什么?

代码如下:

// Lab30st.java
// The Screen Saver Program
// Student Version


import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import javax.swing.JOptionPane;


public class Lab30st
{    
    public static void main(String args[])  
    {
        GfxApp gfx = new GfxApp();
        gfx.setSize(800,600);
        gfx.addWindowListener(new WindowAdapter() {public void
            windowClosing(WindowEvent e) {System.exit(0);}});
        gfx.show();
    }
}


class GfxApp extends Frame
{

    private int circleCount, circleSize;

    public GfxApp()
    {
        circleCount = 50;
        circleSize  = 30;
    }


    class Coord
    {
        private int xPos;
        private int yPos;

        public Coord(int x, int y) 
        {
            xPos = x;
            yPos = y;
        }
    }

    public void paint(Graphics g)
    {
        int incX = 5;
        int incY = 5;
        int diameter = 30;
        int timeDelay = 10;
        Circle c = new Circle(g,diameter,incX,incY,timeDelay);
        for (int k = 1; k <= 2000; k++)
        {   
            c.drawCircle(g);
            c.hitEdge();
        }

    } 
}



class Circle
{
    private int tlX;        // top-left X coordinate
    private int tlY;        // top-left Y coordinate
    private int incX;       // increment movement of X coordinate
    private int incY;       // increment movement of Y coordinate
    private boolean addX;   // flag to determine add/subtract of increment for X
    private boolean addY;   // flag to determine add/subtract of increment for Y
    private int size;       // diameter of the circle
    private int timeDelay;  // time delay until next circle is drawn



    public Circle(Graphics g, int s, int x, int y, int td)
    {
        incX = x;
        incY = y;
        size = s;
        addX = true;
        addY = false;
        tlX = 400;
        tlY = 300;
        timeDelay = td;
    }

    public void delay(int n)
    {
        long startDelay = System.currentTimeMillis();
        long endDelay = 0;
        while (endDelay - startDelay < n)
            endDelay = System.currentTimeMillis();  
    }

    public void drawCircle(Graphics g)
    {
        g.setColor(Color.blue);
        g.drawOval(tlX,tlY,size,size);
        delay(timeDelay);
        if (addX)
            tlX+=incX;
        else
            tlX-=incX;
        if (addY)
            tlY+=incY;
        else
            tlY-=incY;
    }


    public void newData()
    {
        incX = (int) Math.round(Math.random() * 7 + 5);
        incY = (int) Math.round(Math.random() * 7 + 5);
    }

    public void hitEdge()
    {
        boolean flag = false;
        if (tlX < incX)
        {
            addX = true;
            flag = true;
        }
        if (tlX > 800 - (30 + incX))  
        {
            addX = false;
            flag = true;
        }
        if (tlY < incY + 30)  // The +30 is due to the fact that the title bar covers the top 30 pixels of the window
        {
            addY = true;
            flag = true;
        }
        if (tlY > 600 - (30 + incY))  
        {
            addY = false;
            flag = true;
        }
        if (flag)
            newData();
    }

}

【问题讨论】:

  • gfx.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 是正确的。您的“延迟”导致问题也是正确的。不要阻塞 awt paint() 线程,也不要通过旋转 CPU 来实现延迟。

标签: java swing graphics jframe awt


【解决方案1】:

您正在使用

“冻结”事件调度线程
public void delay(int n)
    {
        long startDelay = System.currentTimeMillis();
        long endDelay = 0;
        while (endDelay - startDelay < n)
            endDelay = System.currentTimeMillis();  
    }

这意味着所有其他试图发生的事情(比如关闭窗口)必须等到线程从“睡眠”中出来。 基本上你不应该在 EDT 中做延迟,它应该在不同的线程上,然后要求 EDT 线程更新。

您的“忙等待”延迟也可能导致其他问题。您可以使用Thread.sleep() 来改善行为

Java Event-Dispatching Thread explanation

【讨论】:

    【解决方案2】:

    这太可怕了。
    你需要重构整个代码。

    让我们从真正糟糕的开始: delay (几乎)是一个忙碌的等待,自从 BASIC 是现代的以来,我还没有看到忙碌的等待。它基本上将 CPU 作为线程的人质,不仅它什么都不做,没有其他线程(几乎)可以使用时间片。我说差不多的原因是调用系统时间函数会导致上下文切换,可以允许其他线程运行,但它仍然很糟糕。

    仍然很糟糕: 替换为Thread.sleep。更好的是,没有忙碌的等待,但你仍然持有唯一的 UI 线程。这意味着在关闭主窗口之前不会发生其他 UI 工作。

    需要做什么:

    获取一个外部计时器(例如javax.swing.Timer)来触发绘图事件并执行下一部分动画。

    搜索“Java 平滑动画”有很多例子说明如何做到这一点,双缓冲等等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-06
      • 2013-04-30
      • 2012-09-27
      • 1970-01-01
      • 2013-06-17
      相关资源
      最近更新 更多