0

我正在尝试使用 Java3D 的 Canvas3D 对象制作一个包含一些 3D 内容的 Swing GUI。问题是 Canvas3D 对象初始化需要一段时间,我希望 Swing GUI 立即出现。我对这个问题的解决方案是在单独的线程中初始化 Canvas3D,然后在初始化后将其添加到 JFrame。但是,当那个单独的线程将 Canvas3D 添加到 JFrame 时,窗口会暂时失去焦点,这是不可取的。我怎样才能防止这种情况发生?我已经包含了一个简单的示例来说明我正在尝试做的事情:

public class Main extends JFrame {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Main()::setup);
    }

    private void setup() {
        setSize(600, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        Thread thread = new Thread() {
            @Override
            public void run() {
                Canvas3D canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
                SwingUtilities.invokeLater(() -> {
                    Main.this.add(canvas); //the window loses focus for a moment here
                    Main.this.revalidate();
                });
            }
        };

        thread.start();
    }
}

我正在使用 Java3D 1.7.1。


我已根据 R VISHAL 的评论修改了我的代码,但问题仍然存在。

public class Main extends JFrame {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Main()::setup);
    }

    private void setup() {
        setSize(600, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        SwingWorker<Canvas3D, Object> worker = new SwingWorker<Canvas3D, Object>() {
            @Override
            public Canvas3D doInBackground() {
                return new Canvas3D(SimpleUniverse.getPreferredConfiguration());
            }

            @Override
            public void done() {
                try {
                    Main.this.add(get());
                } catch (InterruptedException|ExecutionException e) {
                    throw new RuntimeException();
                }

                Main.this.requestFocusInWindow();
                Main.this.revalidate();
            }
        };

        worker.execute();
    }
}
4

1 回答 1

0

好的,所以这可能不是答案,但将其作为评论太大了

使用具有 CardLayout 作为框架的 JPanel, contentpane将此面板内的一个屏幕设置为背景[或在显示画布之前要显示的任何初始屏幕],然后在初始化画布后将其添加到内容窗格中第二个屏幕,然后调用 CardLayout 的show()方法来显示画布

public class Add
{
 public static void main(String args[])
 {
  JFrame frame=new JFrame("Test");
  
  JPanel mainPanel=new JPanel(new CardLayout());
  mainPanel.addMouseListener(new MouseAdapter()
  {
   int count=0;
   @Override
   public void mouseClicked(MouseEvent m)
   {
    System.out.println("Focus "+(++count));
   }
  });
  
  JPanel background=new JPanel();
  background.setBackground(Color.WHITE);
  mainPanel.add("Screen1",background);     //Initial Screen To Show Something To The User While Canvas Is Being Initialized
  frame.setContentPane(mainPanel);
  
  SwingWorker worker=new SwingWorker<Canvas3D,Object>()
  {
   @Override
   public Canvas3D doInBackground(){return new Canvas3D();}
   
   @Override
   public void done()
   {
    try
    {
     mainPanel.add("Screen2",get());    //Add Canvas To MainPanel
     
     CardLayout layout=(CardLayout)mainPanel.getLayout();
     
     layout.show(mainPanel,"Screen2"); //Remember This While Using CardLayout
    }
    catch(InterruptedException | ExecutionException ex){}
   }
  };
  
  frame.setSize(500,500);
  
  frame.setVisible(true);
  
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
  worker.execute();
 }
 
 private static final class Canvas3D extends JPanel
 {
  private Canvas3D()
  {
   super(new BorderLayout());
   
   try{Thread.sleep(5000);}//Mimicing Long Operations
   catch(Exception ex){}
  }
  
  @Override
  public void paintComponent(Graphics g)
  {
   super.paintComponent(g);
   
   g.setColor(Color.BLACK);
   
   g.fillRect(0,0,500,500);
   
   g.setFont(new Font("",Font.BOLD,15));
   
   g.setColor(Color.RED);
   
   g.drawString("CANVAS 3D",100,100);
  }
 }
}

当然,您将使用实际的 canvas3d 而不是这个自定义的,并且无需重新验证或 requestFocus()

如果您仍然担心焦点,您总是可以创建一个 javax.swing.Timer 类来请求焦点到您的 mainPanel 或框架 [或两者都看什么对您有用] 每秒/或毫秒

  Timer timer=new Timer(1000,new ActionListener()  //milliseconds make it 1 if your are dead serious about focus
  {
   @Override
   public void actionPerformed(ActionEvent e)
   {
    mainPanel.requestFocusInWindow();
    
    frame.requestFocusInWindow();  //May not be required since we are already requesting focus in mainPanel
   }
  });
  timer.start();

如果你想对焦点更加偏执,你总是可以添加一个焦点监听器

 mainPanel.addFocusListener(new FocusListener()
  {
   @Override
   public void focusLost(FocusEvent e) 
   {
    mainPanel.requestFocusInWindow();
    
    frame.requestFocusInWindow();
   }
   
   @Override
   public void focusGained(FocusEvent e){}
  });

如果这些建议中的任何一个有效/无效,请在下面发表评论:)

于 2020-06-26T07:59:58.760 回答