JFileChooser.showSaveDialog()没有显示出来

时间:2015-01-25 21:42:20

标签: java swing file file-io jfilechooser

我正在使用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

但是对话框永远不会打开,它从来没有说过#34;我现在就在这里。"为什么?此外,当我输入

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;
}
}

如果您想更多地检查它,可以自己运行。


此MCVE可能足以重现该问题,并且似乎使用System.in初始化的扫描程序干扰了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);
   }
}

2 个答案:

答案 0 :(得分:5)

On Windows, Scanner interferes w/ JFileChooser -- why?

tl; dr 严格来说,用户通过使用来自系统的JFileChooser预期输入来干扰Scanner控制台(System.in)。无论哪种方式,它只是关于窗口焦点和Java的Dialog模态。

解释

发生错误是因为对话框出现在后台,因为nextLine() Scanner System.in上的Dialog实际上强制用户将焦点切换到控制台。应用程序失去焦点,因此Dialog出现在后台。代码没有"挂起"它本身只是等待用户选择一个文件或做任何其他对话选项 - 直到他这样做,它什么都不做。如果有某种操作系统问题阻止后台窗口显示/正常工作(例如某些"总是在前台"窗口阻碍它) - 那么,那么你的应用程序是挂着,等待由于没有人可以使用输入对话框本身而不可能发生的输入事件。

对于受影响的人 - < 在那里。我已经在Windows XP上的Java 8,Windows 7上的Java 8和其他一些配置上测试了这段代码 - 在所有情况下,Dialog 都是创建的,可能隐藏在桌面背景中由于焦点损失。它没有显示在任务栏中,但 显示在 Alt + Tab 列表中。尝试在IDE外部运行 ,当需要控制台输入时,他们倾向于使用app焦点做一些奇怪的事情(由于&#34;真实&#34;控制台被重定向到IDE输出窗口等)。通过Dialog管道提供数据而不是使用&#34;真实&#34;控制台也可以防止这种情况发生。

就我的想象而言,变通办法需要将焦点强加到应用程序的框架或创建一个动态的永远在顶部的框架,以保持焦点在应有的位置(在对话)。由于JFileChooser的{​​{1}}(或任何其他类似的对话)本身就是一个即发即弃的对象,因此在没有指定父级的情况下将其强制转移到前台是很困难的。因此,对于无父类Dialog s,我认为最简单的方法是使用如下代码:

示例性解决方案

    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本身,可以通过继承它并在setAlwaysOnTop(true)调用中应用前面提到的createDialog()(请注意createDialog()有{{ 1}}访问protected,无法在不扩展类的情况下更改行为,类似于那里的所有JFileChooser相关内容,例如

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 ); 的常规实用程序类。

<子> NB切换往返EDT在这里没有做任何事情,因为这线程本身相关(尽管UI的多线程是问题的根源,有点)。

答案 1 :(得分:2)

我相信answer by vaxquis非常正确,因为选择器正在显示,就在它保持焦点的当前窗口后面。它与Dialog模态有关,哪些窗口优先于各种操作系统上的其他窗口(可能与某些IDE组合)。

我找到了以下解决方法 - 保持所有其他代码完全相同,扩展JFileChooser,然后在MyFileChooser方法中使用JFileChooser而不是getSaveTo()。我已经在我的设置上进行了测试(Windows 8,Java 7)。 YMMV

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 has appended这个方法给他们的答案,所以答案是,我会说,是规范的。