fx:include与"resources"一起使用会抛出MissingResourceException的异常,但在Java代码中可以正常工作。

13 浏览
0 Comments

fx:include与"resources"一起使用会抛出MissingResourceException的异常,但在Java代码中可以正常工作。

我正在尝试在FXML文件的fx:include中使用resource标签。

我不明白的是,使用ResourceBundle.getBundle()手动加载资源包完全没有问题。

我已经尝试了很多在fxml中的变体,比如:

  • "lang_challenges"

  • "wand555/github/io/challengesreworkedgui/lang_challenges"

package wand555.github.io.challengesreworkedgui;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;
public class ChallengeApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        ResourceBundle.clearCache();
        ResourceBundle bundle = new ResourceBundleWrapper(ResourceBundle.getBundle("wand555/github/io/challengesreworkedgui/lang_challenges"));
        System.out.println(bundle.getString("challenge.name")); // 输出 "abc"
        FXMLLoader loader = new FXMLLoader(ChallengeApplication.class.getResource("overview.fxml"), bundle);
        Parent root = loader.load();
        Scene scene = new Scene(root, 1000, 1000);
        stage.setScene(scene);
        stage.show();
    }
    public static void main(String[] args) {
        launch();
    }
    // 这个类实际上什么也不做,但它将由应用程序类加载器加载
    // 而不是系统类加载器。
    private static class ResourceBundleWrapper extends ResourceBundle {
        private final ResourceBundle bundle;
        ResourceBundleWrapper(ResourceBundle bundle) {
            this.bundle = bundle;
        }
        @Override
        protected Object handleGetObject(String key) {
            return bundle.getObject(key);
        }
        @Override
        public Enumeration getKeys() {
            return bundle.getKeys();
        }
        @Override
        public boolean containsKey(String key) {
            return bundle.containsKey(key);
        }
        @Override
        public Locale getLocale() {
            return bundle.getLocale();
        }
        @Override
        public Set keySet() {
            return bundle.keySet();
        }
    }
}

overview.fxml:



   
      
         
            
               
                  

lang_challenges.properties包含一个键值对

challenge.name=abc

lang_challenges_de.properties具有相同的内容

challenge.name=abc

这是我收到的错误消息

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:465)
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1082)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:901)
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: javafx.fxml.LoadException: 
/Users/felixnaumann/Documents/ChallengesReworked/ChallengesReworkedGUI/target/classes/wand555/github/io/challengesreworkedgui/overview.fxml:21
    at javafx.fxml@19/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2707)
    at javafx.fxml@19/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2685)
    at javafx.fxml@19/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
    at javafx.fxml@19/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2516)
    at challenges.reworked.gui/wand555.github.io.challengesreworkedgui.ChallengeApplication.start(ChallengeApplication.java:22)
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:847)
    at javafx.graphics@19/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:484)
    at javafx.graphics@19/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics@19/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
    at javafx.graphics@19/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
Caused by: java.util.MissingResourceException: Can't find bundle for base name lang_challenges, locale de_DE
    at java.base/java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:2045)
    at java.base/java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1683)
    at java.base/java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1575)
    at java.base/java.util.ResourceBundle.getBundle(ResourceBundle.java:1280)
    at javafx.fxml@19/javafx.fxml.FXMLLoader$IncludeElement.processAttribute(FXMLLoader.java:1100)
    at javafx.fxml@19/javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:230)
    at javafx.fxml@19/javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:755)
    at javafx.fxml@19/javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2808)
    at javafx.fxml@19/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2634)
    ... 9 more
Exception running application wand555.github.io.challengesreworkedgui.ChallengeApplication

0
0 Comments

fx:include with "resources" throws MissingResourceException but works in Java Code

最近我在深入研究ResourceBundle和ClassLoader的源代码后,终于找到了解决方法。

解决方法如下:

1. 将.properties文件放在resources文件夹的根目录下,并在fxml文件中使用fx:include标签引用它,代码如下:


这样就可以正常工作了。但是对于将资源包分成不同包的大型项目来说,这种方法不太适用。

2. 如果在resources包中使用子包,可以在resources标签中给出完全限定的路径名。例如,如果你的包结构从src开始是com/example/demo/,则使用以下代码:


但是,我们还没有完成。你需要在module-info.java文件中将包打开给所有模块,使用以下代码:

opens com.example.demo;

在我的看法中,这听起来像是一个bug。我调试了整个加载过程,当从fxml文件中加载second_bundle时,调用者模块是javafx.fxml。根据getResourceAsStream的Java文档(在加载时内部调用了该方法):

一个包名是从资源名中派生的。如果包名是模块中的一个包,那么只有在至少对调用者的模块开放包时,此方法的调用者才能找到资源。如果资源不在模块的包中,则资源不会被封装。

理论上,只需将包打开给javafx.fxml应该就可以了,但实际上不行...

下面是一个完整的演示项目,项目使用了子包。

项目结构如下:

[项目结构](https://i.stack.imgur.com/jW7NR.png)

HelloApplication类:

public class HelloApplication extends Application {
    public void start(Stage stage) throws IOException {
        ResourceBundle bundle = new ResourceBundleWrapper(ResourceBundle.getBundle("test_bundle"));
        System.out.println(bundle.getString("first.button")); // 输出"English/Deutsch"
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("first.fxml"), bundle);
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }
    public static void main(String[] args) {
        launch();
    }
    private static class ResourceBundleWrapper extends ResourceBundle {
        private final ResourceBundle bundle;
        ResourceBundleWrapper(ResourceBundle bundle) {
            this.bundle = bundle;
        }
        protected Object handleGetObject(String key) {
            return bundle.getObject(key);
        }
        public Enumeration getKeys() {
            return bundle.getKeys();
        }
        public boolean containsKey(String key) {
            return bundle.containsKey(key);
        }
        public Locale getLocale() {
            return bundle.getLocale();
        }
        public Set keySet() {
            return bundle.keySet();
        }
    }
}

first.fxml:


    
        
    
    

second.fxml:


    
        
    
    

test_bundle.properties:

first.button=English

second_bundle.properties:

second.button=Hello

module-info.java:

module com.example.demo {
    requires javafx.controls;
    requires javafx.fxml;
    opens com.example.demo;
    exports com.example.demo;
}

总结一下,这个问题的解决方法是将.properties文件放在resources文件夹的根目录下,并正确引用它。对于子包,需要在module-info.java文件中将包打开给所有模块。希望这篇文章对你有帮助。

0