线程中的实例化组件是';t在Java中重新绘制到JFrame中

线程中的实例化组件是';t在Java中重新绘制到JFrame中,java,multithreading,components,jframe,paint,Java,Multithreading,Components,Jframe,Paint,我只有一门课像这样 public class BlockSpawner implements Runnable{ public static long timeToSpawn; private GtrisJFrame frame; public BlockSpawner(GtrisJFrame frame) { this.frame = frame; timeToSpawn = 2000; } public void run() { while(true)

我只有一门课像这样

public class BlockSpawner implements Runnable{

public static long timeToSpawn;
private GtrisJFrame frame;

public BlockSpawner(GtrisJFrame frame)
{

    this.frame = frame;
    timeToSpawn = 2000;
}

public void run()
{
    while(true)
    {
        try
        {
            Thread.sleep(timeToSpawn);
        }
        catch(InterruptedException e)
        {
            //Unhandled exception
        }

        //After awake, instanciate 2 blocks
        //get the position of the first one
        int index = Block.getRandomStartPosition();
        new Block(frame, index);
        new Block(frame, index+1);
    }
}
}

我在JFrame main类中引用了这个类,并像这样启动它的线程:

private void initBlockSpawner()
{
    spawner = new BlockSpawner(this);
    new Thread(spawner).start();
}
public static void main(String args[])
{
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new GtrisJFrame().setVisible(true);
        }
    });

}
public void run()
{
    while(true)
    {
        System.out.println("SPAWN");
        int index = Block.getRandomStartPosition();
        new Thread(new Block(frame, index)).start();
        new Thread(new Block(frame, index+1)).start();

        try
        {
            Thread.sleep(timeToSpawn);
        }
        catch(InterruptedException e)
        {
            //Unhandled exception
        }
    }
}
我从JFrame构造函数中调用这个initBlockSpawner()函数。Block类确实有点大,但简而言之,它实现了runnable,并在构造函数末尾调用其run()方法。run()方法只能使块以一定的速度下落。我曾尝试在JFrame构造函数中手动实例化新块,它们工作、重新绘制并掉落。但是,每当我想实例化来自其他线程的块时,它们似乎会下降(即,它的属性会更新每个循环),但它们不会在JFrame中绘制

作为补充信息,我使用的是NetBeans,由于应用程序入口点位于JFrame类上,因此主方法如下所示:

private void initBlockSpawner()
{
    spawner = new BlockSpawner(this);
    new Thread(spawner).start();
}
public static void main(String args[])
{
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new GtrisJFrame().setVisible(true);
        }
    });

}
public void run()
{
    while(true)
    {
        System.out.println("SPAWN");
        int index = Block.getRandomStartPosition();
        new Thread(new Block(frame, index)).start();
        new Thread(new Block(frame, index+1)).start();

        try
        {
            Thread.sleep(timeToSpawn);
        }
        catch(InterruptedException e)
        {
            //Unhandled exception
        }
    }
}
我对Java线程、awt事件和swing组件没有太多经验。但是我读到的一些东西让我觉得我的问题是只有一个线程可以控制swing组件,或者其他什么。。。有什么办法解决我的问题吗

提前谢谢

编辑:其他信息,每当我检查线程实例化多维数据集上的toString方法时,它们都会给我这个[,0,0,0x0],但当我在同一JFrame类中实例化它们时,它们会给我这个结果[,0,0328x552],并显示在框架上。此328x552值与getPreferredSize()返回的组件维度相同。。。我试图通过如下方式实例化它们,将它们强制到该维度:

new Block(this, index).setPreferredSize(new Dimension(328, 552));
但它不起作用,有人知道这个[,0,0328x552]值可能意味着什么吗

谢谢大家,我想我们快到了

编辑2: 我意识到组件的大小是x:0y:0,这是为什么?我将BlockSpawner的run()方法更改为如下内容:

private void initBlockSpawner()
{
    spawner = new BlockSpawner(this);
    new Thread(spawner).start();
}
public static void main(String args[])
{
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new GtrisJFrame().setVisible(true);
        }
    });

}
public void run()
{
    while(true)
    {
        System.out.println("SPAWN");
        int index = Block.getRandomStartPosition();
        new Thread(new Block(frame, index)).start();
        new Thread(new Block(frame, index+1)).start();

        try
        {
            Thread.sleep(timeToSpawn);
        }
        catch(InterruptedException e)
        {
            //Unhandled exception
        }
    }
}
第一次跑步时,一切顺利!即使这对块在JFrame上绘制并正确地落下,但是在Thread.sleep()之后,其余的块只是被实例化,但是它们的getSize()方法给了我x:0y:0;这是否仍然与一个调度程序线程问题有关?或者现在有些不同了?

我觉得(尽管我无法从上面的代码中看出)您正试图从事件调度线程以外的线程向live JFrame(即屏幕上显示的组件,或“已实现”)添加组件。这违反了Swing线程模型,将给您带来无尽的问题

如果要从其他线程对Swing对象进行更改,可以将更改打包到可运行线程中,并使用EventQueue.invokeLater()或invokeAndWait()将其提交给调度线程

编辑:更多信息

另外一些评论(与您的问题没有直接关系,但仍然很重要):在构造函数中执行活动可能不是一个好主意。将JFrame子类化以向其添加组件也可能不是一个好主意。因此,在JFrame而不是JPanel中执行这些操作可能也不是最好的方法

依次考虑:

  • 构造函数应该用于对对象执行初始配置,而不是调用行为。这种分离有助于保持设计的干净性和可维护性。尽管这样做似乎更容易,但我建议不要这样做。在设计过程中的某个时刻,您可能会认为提前创建这些对象而只在以后使用它们更有效

  • 将JFrame子类化以添加组件通常不是一个热门想法。为什么?如果您决定使用一个专门的JFrame,它具有您想要的其他行为,该怎么办?如果您决定使用一个为您提供JFrame的应用程序框架,该怎么办(在框架可能希望跟踪窗口关闭事件以便保存窗口大小和位置的情况下,这是典型的)。无论如何,有很多原因。将您的行为打包到一个与GUI无关的类中,并使用该类将行为注入JFrame(或JPanel)

  • 考虑使用JPanel而不是JFrame。如果需要,可以随时将JPanel添加到JFrame。如果您直接使用JFrame编写word,那么当您决定将其中两个面板并排放在一个容器中时会发生什么情况

  • 因此,我建议您采取以下措施:

    BlockAnimator animator = new BlockAnimator();
    DispatchThread.invokeLater( 
      new Runnable(){
        public void run(){
          JPanel blockAnimationPanel = new JPanel();
          Block block = new Block(...);
          blockAnimationPanel.add(block);
          JFrame mainFrame = new JFrame();
          mainFrame.add(blockAnimationPanel);
          animator.start(); // note that we probably should start the thread *after* the panel is realized - but we don't really have to.
        }
      }
    
    public class BlockAnimator extends Thread{
      private final List<Block> blocks = new CopyOnWriteArray<Block>(); // either this, or synchronize adds to the list
      public void addBlock(Block block){
        blocks.add(block);
      }
      public void run(){
        while(true){ // either put in a cancel check boolean, or mark the thread as daemon!
          DispatchThread.invokeAndWait(
            new Runnable(){
              public void run(){
                for(Block block: blocks){
                  block.moveTo(....); // do whatever you have to do to move the block
                }
              }
            }
          ); // I may have missed the brace/paren count on this, but you get the idea
          spawnNewBlockObjects();
          Thread.sleep(50);
        }
      }
    }
    
    BlockAnimator animator=new BlockAnimator();
    DispatchThread.invokeLater(
    新的Runnable(){
    公开募捐{
    JPanel blockAnimationPanel=新的JPanel();
    块=新块(…);
    blockAnimationPanel.add(块);
    JFrame mainFrame=新JFrame();
    mainFrame.add(blockAnimationPanel);
    animator.start();//请注意,我们可能应该在面板实现后启动线程,但实际上不必这样做。
    }
    }
    公共类BlockAnimator扩展线程{
    private final List blocks=new CopyOnWriteArray();//此选项或同步添加到列表中
    公共无效添加块(块){
    块。添加(块);
    }
    公开募捐{
    while(true){//输入取消检查布尔值,或者将线程标记为守护进程!
    DispatchThread.invokeAndWait(
    新的Runnable(){
    公开募捐{
    用于(块:块){
    block.moveTo(…);//执行所有必须执行的操作来移动块
    }
    }
    }
    )我可能错过了这个机会,但你明白了
    产卵对象();
    睡眠(50);
    }
    }
    }
    
    上面的代码还没有检查过准确性等

    理论上,你可以有一个单独的线程来生成新的块,但是上面的操作非常简单。如果你决定用一个后台线程来实现,就像我在上面展示的那样,你可以在块列表中使用一个简单的ArrayList,因为