Swing是否支持Windows 7风格的文件选择器?

48

我刚刚在我正在写的一个小型桌面应用程序中添加了一个标准的"打开文件"对话框,基于Swing教程的JFileChooser入口。它生成的窗口看起来像这样:

不需要的/XP风格窗口的截图

但我更喜欢有一个看起来像这样的窗口:

期望的/7风格窗口的截图

换句话说,我想要我的文件选择器具有Windows Vista / Windows 7的样式,而不是Windows XP的样式。 在Swing中是否可以实现?如果可以,如何做到?(为了这个问题,假设代码将仅在Windows 7计算机上运行。)

10个回答

21

在Java 6的Swing中似乎不支持此功能。

目前,我能找到的最简单的打开此对话框的方法是通过SWT而不是Swing。 SWT的FileDialog(javadoc)可以打开此对话框。以下是SWT的FileDialog代码示例FileDialog snippet的修改版本,以使用打开而不是保存对话框。我知道这不完全是您要寻找的,但您可以将其隔离到一个实用程序类中,并将swt.jar添加到您的类路径中以获取此功能。

import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;

public class SWTFileOpenSnippet {
    public static void main (String [] args) {
        Display display = new Display ();
        Shell shell = new Shell (display);
        // Don't show the shell.
        //shell.open ();  
        FileDialog dialog = new FileDialog (shell, SWT.OPEN | SWT.MULTI);
        String [] filterNames = new String [] {"All Files (*)"};
        String [] filterExtensions = new String [] {"*"};
        String filterPath = "c:\\";
        dialog.setFilterNames (filterNames);
        dialog.setFilterExtensions (filterExtensions);
        dialog.setFilterPath (filterPath);
        dialog.open();
        System.out.println ("Selected files: ");
        String[] selectedFileNames = dialog.getFileNames();
        for(String fileName : selectedFileNames) {
            System.out.println("  " + fileName);
        }
        shell.close();
        while (!shell.isDisposed ()) {
            if (!display.readAndDispatch ()) display.sleep ();
        }
        display.dispose ();
    }
} 

1
这个可以运行,虽然它不是技术上的Swing,但它似乎可以与Swing并存,并且工作得非常好。 - Pops
使用SWT的文件选择器会给SWING应用程序增加多少MB的开销? - 添加SWT后,应用程序启动会变慢多少? - 我很想将所有“我的GUI比你的好”炫耀答案的投票都打倒。 - Martin
20
@Martin:我不明白你为什么这么敌对。我只是想回答问题,而不是炫耀。问题问能否在Swing中完成。我的回答是Swing不能完成,并提供了一个可行的替代方案。我同意对这个解决方案进行分析很重要,但我认为这超出了问题的范围。此外,最好将这个解决方案作为应用程序的一部分进行分析。我怀疑这个解决方案对应用程序的开销没有显著贡献,但最好将整个应用程序进行分析。 - John McCarthy

10
即使是本地的Windows应用程序也可以在Windows 7上显示这种类型的对话框。通常由OPENFILENAME结构中的标志以及其大小在调用WinAPI函数GetOpenFileName时进行控制。Swing(Java)使用钩子来获取打开文件对话框中的事件;这些事件在Windows XP和Windows 7版本之间传递方式不同。
因此,答案是您无法从Swing中控制FileChooser的外观。但是,当Java支持这种新外观时,您将自动获得新样式。
另一个选项是使用SWT,如此答案所建议的。或者,您可以使用JNA调用Windows API或编写本地方法来完成此操作。

除非有一个惊人的最后一分钟答案,否则这将因其全面性而获得赏金。 - Pops
1
Swing对话框是完全基于Java的对话框(带有XP克隆LAF),而不是本地对话框。它不仅仅是Java使用本地调用的副作用(尽管这些调用也可以生成类似XP的文件对话框)。 - Michael Brewer-Davis
@MichaelBrewer-Davis 您是正确的。我忽略了Swing是纯Java的事实。使用Spy++确认了这一点:对话框窗口的类是SunAwtDialog,它没有子窗口,与标准系统对话框相反。此外,它看起来非常类似于XP打开文件对话框,但并不完全相同:存在行为和外观上的差异。 - Alexey Ivanov
2
一个JNI解决方案似乎已经在https://code.google.com/p/xfiledialog/中实现,这也可能是一个可行的选择。 - Suma

8

Java 8可能终于为此提供了解决方案,但不幸的是(对于Swing应用程序而言),它只作为JavaFX类FileChooser提供:

我已经从这里测试过这段代码,它确实弹出了一个现代对话框(在此处为Windows 7):

FileChooser fileChooser = new FileChooser();

//Set extension filter
FileChooser.ExtensionFilter extFilterJPG = new FileChooser.ExtensionFilter("JPG files (*.jpg)", "*.JPG");
FileChooser.ExtensionFilter extFilterPNG = new FileChooser.ExtensionFilter("PNG files (*.png)", "*.PNG");
fileChooser.getExtensionFilters().addAll(extFilterJPG, extFilterPNG);

//Show open file dialog
File file = fileChooser.showOpenDialog(null);

要将其集成到Swing应用程序中,您需要通过Platform.runLater在JavaFX线程中运行它(如此处所示)。

请注意,这将需要您初始化JavaFX线程(在示例中,这是在场景初始化时完成的,在new JFXPanel()中)。

总之,在Swing应用程序中准备好运行的解决方案将如下所示:

new JFXPanel(); // used for initializing javafx thread (ideally called once)
Platform.runLater(() -> {
    FileChooser fileChooser = new FileChooser();

    //Set extension filter
    FileChooser.ExtensionFilter extFilterJPG = new FileChooser.ExtensionFilter("JPG files (*.jpg)", "*.JPG");
    FileChooser.ExtensionFilter extFilterPNG = new FileChooser.ExtensionFilter("PNG files (*.png)", "*.PNG");
    fileChooser.getExtensionFilters().addAll(extFilterJPG, extFilterPNG);

    //Show open file dialog
    File file = fileChooser.showOpenDialog(null);
});

提示:JavaFX 只适用于 Oracle Java,因此您可能需要明确选择它,例如在您的 Eclipse 项目中。相比之下,“Java-1.8 执行环境”可以正确地将 JavaFX 排除在类路径中不可见。 - Luke Usherwood
showOpenDialog(null)没有指定父/所有者窗口,因此弹出窗口不是模态的。它期望一个javafx.stage.Window,但我正在使用Swing的java.awt.Window。我该如何实现模态FileChooser - MasterHD
1
你可能误读了“modern dialog”为模态对话框?无论如何,我认为你不能在JavaFX和Swing窗口之间创建父(更不用说模态)关系(由于它们不同的线程模型、事件循环、不兼容的代码库等原因...)。请参见此答案,它基本上通过阻塞一个窗口并强制将焦点返回到另一个窗口来进行黑客攻击。它有缺陷——多个顶级框架出现在任务栏中,并且这种黑客攻击在某些Linux窗口管理器上无法工作。尽管如此,这是我迄今为止看到的最好的方法... - Luke Usherwood

7

虽然不如Swing版本功能强大,但如果你考虑使用java.awt.FileDialog,也是一个小技巧。它不仅应该看起来像Windows文件选择器,而且实际上就是一个。


5
遗憾的是,这种方法行不通;它还会弹出XP-style对话框。我使用的测试代码直接来自http://www.java2s.com/Code/JavaAPI/java.awt/newFileDialogFrameparentStringtitleintmode.htm。 - Pops
1
这个怎么样?(https://dev59.com/oVfUa4cB1Zd3GeqPJqHt#6153182) - trashgod
1
@trashgod:在接下来的几个小时里,我将开始一个名为“文件管理器”的新主题,专门讨论该代码的扩展版本。当我开始时,我会在你链接的主题中添加一条评论,并希望能在最新的主题上见到你。 - Andrew Thompson
+1 因为这可能是某些场景下的适当解决方案。至少你可以删除文件,获取上下文菜单等等... 只要注意主要限制:在 Windows 上无法使用文件过滤,并且不支持多个文件选择。 <rant>Java 7 在这个古老的类上进行了微小的增强。我可以理解焦点正在从 Swing 转移,但我仍然无法相信他们不能为这个基本重要的对话框做更多的事情!</rant> - Luke Usherwood

7

我不认为Swing会覆盖那个,尽管可能会有,如果没有,你可能需要看一下像SWT这样的东西,它将使用实际的本地组件,或者做一个自定义的UI元素,就像“Filthy Rich Clients”书中的一些内容。


2

好问题+1,看起来似乎他们在Java6中没有为Win7(defaultLookAndFeel)实现某些功能,但对于WinXP,它可以正常工作,我也希望存在某些方法/属性

无论如何,您可以尝试使用以下代码:

import java.io.File;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;

class ChooserFilterTest {

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                String[] properties = {"os.name", "java.version", "java.vm.version", "java.vendor"};
                for (String property : properties) {
                    System.out.println(property + ": " + System.getProperty(property));
                }
                JFileChooser jfc = new JFileChooser();
                jfc.showOpenDialog(null);
                jfc.addChoosableFileFilter(new FileFilter() {

                    @Override
                    public boolean accept(File f) {
                        return f.isDirectory() || f.getName().toLowerCase().endsWith(".obj");
                    }

                    @Override
                    public String getDescription() {
                        return "Wavefront OBJ (*.obj)";
                    }

                    @Override
                    public String toString() {
                        return getDescription();
                    }
                });
                int result = JOptionPane.showConfirmDialog(null, "Description was 'All Files'?");
                System.out.println("Displayed description (Metal): " + (result == JOptionPane.YES_OPTION));
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    SwingUtilities.updateComponentTreeUI(jfc);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                jfc.showOpenDialog(null);
                result = JOptionPane.showConfirmDialog(null, "Description was 'All Files'?");
                System.out.println("Displayed description (System): " + (result == JOptionPane.YES_OPTION));
            }
        };
        SwingUtilities.invokeLater(r);
    }

    private ChooserFilterTest() {
    }
}

刚试了一下,它仍然会给出一个类似于我第一张截图的选择器。不过还是很感谢你的努力。 - Pops

1

无法在目录中工作!DirectoryDialog将我们带回到树状目录选择器,它与问题中列出的选择器相同。问题是它不允许我选择/打开隐藏的文件夹。也不允许浏览到像AppData、ProgramData等文件夹。

Windows 7风格的文件对话框(swt)允许浏览这些文件夹,但是又不允许选择文件夹:(

更新 要查看隐藏文件夹,请使用JFileChooser并设置setFileHidingEnabled(false)。唯一的要求是用户需要在Windows资源管理器的

文件夹选项 -> 查看

中选择“显示隐藏的文件、文件夹和驱动器”

您将无法获得地址栏的灵活性,但是如果您正在寻找一种非树形文件选择器来浏览/查看隐藏的文件/文件夹,则应该足够了


1
约翰·麦卡锡的回答似乎是最好的。这里有一些建议。
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.graphics.Image;

在左上角添加图片。重要的是使用"getResourceAsStream",当导出为Runnable jar后,您会注意到:
Display display = new Display();
Shell shell = new Shell(display);
InputStream inputImage = getClass().getResourceAsStream("/app/launcher.png");
if (inputImage != null) {
    shell.setImage(new Image(display, inputImage));
}

用户的主目录:
String filterPath = System.getProperty("user.home");

获取绝对路径名,而不是依赖过滤器的路径名,在其他驱动器上这种方式是错误的。
String absolutePath = dialog.open();

0
JFileChooser 一直在 Swing 中看起来有点奇怪,也有点慢。 尝试使用 SWT 的 filechooser 或者你可以在 JNA 中包装 C 调用。

1
使用SWT的文件选择器会给SWING应用程序增加多少MB的开销? - 添加SWT后,应用程序启动会变慢多少? - 我很想将所有“我的GUI比你的好”炫耀答案的投票都打倒。 - Martin

0

由于Swing模拟了各种外观和感觉,我猜你最好的选择是升级你的JRE到最新版本,并希望JFileChooser UI已经得到更新。


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