JFileChooser.showSaveDialog()没有弹出窗口

7
我是一个有用的助手,可以翻译文本。
我正在使用Eclipse IDE,并尝试从另一个方法中调用showSaveDialog(null),该方法反过来又从main方法调用,但当我调用它时没有任何反应。
主要代码如下:
public static void main(String[] args) {
    Main main = new Main();
    main.pt = main.new PlayThread();
    main.generateSound(getSamples(2), 500);
    main.play(main.sound, 1);
    if(new Scanner(System.in).nextLine().equals("save")){
        System.out.println(main.save());
    }
}

所有在调用main.save()之前的代码都可以正常运行,而main.save()的开始如下:
public boolean save(){
    System.out.println("Calling save");
    try{
    String saveName = getSaveTo("C:/");
    System.out.println("I'm here now");

getSaveTo() 看起来像这样:

public String getSaveTo(String def){
    JFileChooser chooser = new JFileChooser();
    System.out.println("Save called");
    //chooser.setCurrentDirectory(new File(def));
    int resp = chooser.showSaveDialog(null);
    if(resp == JFileChooser.APPROVE_OPTION){
        return chooser.getSelectedFile() + ".wav";
    }else return null;
}

在执行了main中的前几个函数后,我输入了“save”,控制台打印出:
Calling save
Save called

但是对话框从未打开,并且从未显示“我现在在这里”。为什么会这样?此外,当我键入
new JFileChooser().showSaveDialog(null)
时,它可以正常显示,但在我的save()方法中却不能。有任何想法吗?
编辑:
以下是一个简化的程序,具有相同的问题:
package com.funguscow;

import java.util.Scanner;

import javax.swing.JFileChooser;

import com.funguscow.Main.PlayThread;

public class Test {
public static void main(String[] args) {
    Test main = new Test();
    Scanner scan = new Scanner(System.in);
    boolean should = scan.nextLine().equals("save");
    scan.close();
    if(should){
        System.out.println(main.save());
    }
}

public String getSaveTo(String def){
    JFileChooser chooser = new JFileChooser();
    System.out.println("Save called");
    //chooser.setCurrentDirectory(new File(def));
    int resp = chooser.showSaveDialog(null);
    System.out.println("Save called");
    if(resp == JFileChooser.APPROVE_OPTION){
        return chooser.getSelectedFile() + ".wav";
    }else return null;
}

public boolean save(){
    System.out.println("Calling save");
    try{
    String saveName = getSaveTo("C:/");
    System.out.println("I'm here now");
    if(saveName == null)return false;
    }catch(Exception e){}
    return false;
}
}

“如果您想进一步检查,可以自行运行此操作。
这个最小化可复现代码示例可能足以重现问题,似乎使用System.in初始化的Scanner干扰了JFileChooser显示“打开文件”对话框的能力,即使确保文件选择器在Swing事件线程上运行。”
import java.util.Scanner;    
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;

public class Test3 {
   public static void main(String[] args) {
      Scanner scan = new Scanner(System.in);
      System.out.print("Enter something and press Enter: ");
      scan.nextLine();
      scan.close();

      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            JFileChooser fileChooser = new JFileChooser();
            int result = fileChooser.showOpenDialog(null);
         }
      });
      // JFileChooser fileChooser = new JFileChooser();
      // int result = fileChooser.showOpenDialog(null);
   }
}

你能否将你的问题浓缩成一个小的可编译和可运行的程序呢?请帮助我们在自己的机器上测试你的问题,以更好地帮助你。 - Hovercraft Full Of Eels
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Hovercraft Full Of Eels
1
OP,我的代码是一个简化版本,有点类似于你的代码,它使用了System.in与Scanner,然后关闭了Scanner,接着创建并显示了一个 JFileChooser,但是后者被设置为在 Swing 事件分派线程上运行。 - Hovercraft Full Of Eels
1
另外,你的第二个例子对我来说也可以正常工作。 - MadProgrammer
2
} catch (Exception e) {} //永远不要吞噬异常。当在catch块中添加e.printStackTrace()时会发生什么? - Qix - MONICA WAS MISTREATED
显示剩余10条评论
2个回答

5
在Windows上,扫描器与JFileChooser冲突——为什么?严格来说,是用户使用期望从系统控制台(System.in)接收输入的扫描器干扰了JFileChooser。无论如何,这只涉及到窗口焦点和Java的Dialog模态。
出现此错误是因为需要在Scanner上调用nextLine()来从System.in读取输入,这实际上迫使用户切换到控制台。应用程序失去了焦点,因此Dialog会出现在后台。代码本身不会“挂起”,它只会等待用户选择文件或执行任何其他对话框选项-在这之前,它什么也不做。如果有某种阻止后台窗口正常显示/工作(例如一些“始终在前景中”的窗口阻碍它)的操作系统问题 - 那么您的应用程序将“悬停”在那里,等待不太可能发生的输入事件,因为没有人可以使用输入对话框本身。
对于受影响的任何人-Dialog确实存在。我已在Windows XP上的Java 8、Windows 7上的Java 8以及其他一些配置上测试了此代码-在所有情况下,Dialog都被创建了,可能由于焦点丢失而隐藏在桌面的后台。它不在任务栏中显示,但在Alt+Tab列表中显示。尝试在IDE之外运行它,当需要控制台输入时,它们往往会对应用程序焦点进行奇怪的处理(因为“真实”的控制台被重定向到IDE输出窗口等)。使用“<”管道而不是使用“真实”控制台传递数据可能也会防止发生此行为。
就我想象力所及,解决方法要么需要强制将焦点放在应用程序框架上,要么需要创建一个即兴的始终在最前面的框架,以保持焦点在Dialog上。由于JFileChooser(或任何其他类似的Dialog)的Dialog本身是一个Fire-and-Forget对象,因此很难在没有指定父级的情况下将其强制置于前景。因此,我认为对于无父级Dialog,最简单的方法是只需使用以下代码:
    Scanner scan = new Scanner( System.in );
    System.out.print( "Enter something and press Enter: " );
    scan.nextLine();
    scan.close();

    SwingUtilities.invokeLater( new Runnable() {
        public void run() {
            JFrame jf = new JFrame( "Dialog" ); // added
            jf.setAlwaysOnTop( true ); // added
            JFileChooser fileChooser = new JFileChooser();
            int result = fileChooser.showOpenDialog( jf );  // changed
            //System.out.print( "result: " + result );
            jf.dispose(); // added
        }
    } );

或者,如果您希望干扰Dialog本身,可以通过子类化并在createDialog()调用中应用前面提到的setAlwaysOnTop(true)(请注意,JFileChooser中的createDialog()具有protected访问权限,使得无法在不扩展该类的情况下更改行为,与所有Dialog相关的东西类似),例如:

int result = new JFileChooser() {
  @Override
  protected JDialog createDialog( Component parent ) throws HeadlessException {
    JDialog jDialog = super.createDialog( parent );
    jDialog.setAlwaysOnTop( true );
    return jDialog;
  }
}.showOpenDialog( null );

甚至可以创建一个继承JFileChooser的常规实用程序类来执行此操作。

注意:在此处切换/反切EDT无效,因为本身与线程无关(尽管UI的多线程是问题的源头,某种程度上)。


我不建议使用setAlwaysOnTop,因为它对桌面的影响比较大,会遮挡系统上的所有其他应用程序。 - MasterHD

2
我相信vaxquis的回答是正确的,因为选择器被显示在当前窗口的后面,但它仍然保持焦点。这与对话框模态有关,以及在各种操作系统上哪些窗口优先于其他窗口(可能与一些IDE组合使用)。
我发现以下解决方法 - 在保持所有其他代码完全相同的情况下,扩展JFileChooser,然后在getSaveTo()方法中使用MyFileChooser而不是JFileChooser。我已经在我的设置上进行了测试(Windows 8,Java 7)。你的情况可能会有所不同。
class MyFileChooser extends JFileChooser {
    protected JDialog createDialog(Component parent) throws HeadlessException {
        JDialog dialog = super.createDialog(parent);
        dialog.setAlwaysOnTop(true);
        return dialog;
    }
}

这样可以让您使用原始的构造函数(.openSaveDialog(null)),并且具有很好的优势,即如果您希望,在重写的createDialog()方法中可以设置对话框的其他特性。

编辑

我看到vaxquis 现在也已添加此方法到他们的答案中,因此我认为该答案是规范的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接