JavaFX ImageView not updating - image

So I'm trying to load and save Images into an imageView where the location of the image is chosen through a file browser. I've been working on this for several days now and I'm gonna have a stroke if I can't get it fixed. I've tried everything I can think of. Thank you in advance for helping.
UPDATED:
Here is my main class:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
public Main(){}
#Override
public void start(Stage primaryStage) throws Exception{
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Help Please");
initRootLayout();
showScreen();
}
public void initRootLayout(){
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
RootLayout controller = loader.getController();
controller.setMain(this);
primaryStage.show();
}catch(Exception e ){e.printStackTrace();}
}
public void showScreen(){
try{FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/sample.fxml"));
BorderPane sample = (BorderPane)loader.load();
rootLayout.setCenter(sample);
Controller controller = loader.getController();
controller.setMain(this);
}catch (Exception e){e.printStackTrace();}
}
public Stage getPrimaryStage(){return primaryStage;}
public static void main(String[] args) {
launch(args);
}
}
Here is the rootLayout:
public class RootLayout {
private Main main;
private Controller controller = new Controller();
public void setMain(Main main){this.main = main;}
#FXML
private void handleOpen(){
FileChooser fileChooser = new FileChooser();
FileChooser.ExtensionFilter extensionFilter = new FileChooser.ExtensionFilter(
"PNG files (*.png)","*png");
fileChooser.getExtensionFilters().add(extensionFilter);
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if(file!= null){
controller.updateImage(file.toURI().toString());
}
}
}
And here is the controller:
public class Controller implements Initializable {
#FXML
ImageView imageView = new ImageView();
String imageURL;
Main main = new Main();
public void setMain(Main main){
this.main = main;
}
public void updateImage(String url){
if(url.length()>=1){
Image image = new Image(url);
imageView.setImage(image);
System.out.println(url);
}
else{
System.out.println(url);
System.out.println("image invalid");
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}

Two things:
Never assign a field whose value is to be injected by an FXMLLoader (e.g. #FXML fields). Doing so is a waste of resources at best and introduces subtle bugs at worst. For instance, if you were to leave the imageView field uninitialized you'd be getting a NullPointerException which would indicate a problem with your setup. Since you do initialize the field, however, you don't get any errors and there's a false impression of the code working.
In your RootLayout controller class, you have:
private Controller controller = new Controller();
That instance of Controller you just created is not linked to any FXML file. And since you initialize the imageView field (see first point) you end up updating an ImageView which is not being displayed anywhere; this is where not initializing said field would have given a nice indication of there being a problem. The solution is to pass the Controller instance created by the FXMLLoader to the RootLayout instance created by the other FXMLLoader.
Also, in the same class you have:
Main main = new Main();
Which is also unnecessary since the created instance of Main is both not the correct instance and is replaced by the call to #setMain(Main) almost immediately.
Assuming your FXML files (which you did not provide) are correct, the Java classes should look more like:
Main.java
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private RootLayout rootLayoutController;
public Main() {}
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Help Please");
initRootLayout();
showScreen();
}
public void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// store RootLayout instance in field so #showScreen()
// can reference it
rootLayoutController = loader.getController();
rootLayoutController.setMain(this);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public void showScreen() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/sample.fxml"));
BorderPane sample = (BorderPane) loader.load();
rootLayout.setCenter(sample);
Controller controller = loader.getController();
controller.setMain(this);
// set Controller instance on RootLayout instance
rootLayoutController.setController(controller);
} catch (Exception e) {
e.printStackTrace();
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
RootLayout.java
public class RootLayout {
private Main main;
private Controller controller;
public void setMain(Main main) {
this.main = main;
}
public void setController(Controller controller) {
this.controller = controller;
}
#FXML
private void handleOpen() {
FileChooser fileChooser = new FileChooser();
// Note extensions should be prefixed with "*."
FileChooser.ExtensionFilter extensionFilter =
new FileChooser.ExtensionFilter("PNG files (*.png)", "*.png");
fileChooser.getExtensionFilters().add(extensionFilter);
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if (file != null) {
controller.updateImage(file.toURI().toString());
}
}
}
Controller.java
public class Controller implements Initializable {
#FXML ImageView imageView; // leave uninitialized, will be injected
String imageURL;
Main main;
public void setMain(Main main) {
this.main = main;
}
public void updateImage(String url) {
if (url.length() >= 1) {
Image image = new Image(url);
imageView.setImage(image);
System.out.println(url);
} else {
System.out.println(url);
System.out.println("image invalid");
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {}
}
Note: Did not test new code.

Related

JavaFX custom controls created with a Builder and binding expressions

I’m using Spring together with JavaFx. To use spring bean as a custom control I need to use BuilderFactory and a Builder to get a bean from the context. Otherwice I don't have an application context
Parent.java
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ParentControl extends VBox {
#Autowired
ControlFXMLLoader controlFXMLLoader;
#Value("classpath:/parent.fxml")
private Resource fxml;
#PostConstruct
void load() throws IOException {
controlFXMLLoader.load(fxml.getURL(), this);
}
public ParentControl() {
//no application context
}
public LocalDate getDate() {
return LocalDate.now();
}
}
BeanBuilderFactory.java
#Component
public class BeanBuilderFactory implements BuilderFactory {
private Logger logger = LogManager.getLogger(BeanBuilderFactory.class);
#Autowired
private ConfigurableApplicationContext context;
public BeanBuilderFactory() {
}
private JavaFXBuilderFactory defaultBuilderFactory = new JavaFXBuilderFactory();
#Override
public Builder<?> getBuilder(Class<?> type) {
try {
String[] beanNames = context.getBeanNamesForType(type);
if (beanNames.length == 1) {
return new Builder<Object>() {
#Override
public Object build() {
return context.getBean(beanNames[0]);
}
};
} else {
return defaultBuilderFactory.getBuilder(type);
}
} catch (BeansException e) {
return defaultBuilderFactory.getBuilder(type);
}
}
}
And then I user this BuilderFactory to load fxml for a custom control
ControlFXMLLoader.java
#Component
public class ControlFXMLLoader {
private Logger logger = LogManager.getLogger(ControlFXMLLoader.class);
#Autowired
protected ConfigurableApplicationContext context;
#Autowired
protected BeanBuilderFactory beanBuilderFactory;
public Object load(URL fxmlUrl, Parent root, Object controller) throws IOException {
logger.debug("load");
javafx.fxml.FXMLLoader loader = new javafx.fxml.FXMLLoader(fxmlUrl);
loader.setControllerFactory(context::getBean);
loader.setBuilderFactory(beanBuilderFactory);
loader.setRoot(root);
loader.setController(controller);
return loader.load();
}
public Object load(URL fxmlUrl, Parent root) throws IOException {
return load(fxmlUrl, root, root);
}
}
Now I have a child custom control
ChildControl.java
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ChildControl extends VBox {
public ChildControl() {
}
#Autowired
ControlFXMLLoader controlFXMLLoader;
#Value("classpath:/child.fxml")
private Resource fxml;
#PostConstruct
void load() throws IOException {
controlFXMLLoader.load(fxml.getURL(), this);
}
ObjectProperty<LocalDate> date = new SimpleObjectProperty<LocalDate>();
public LocalDate getDate() {
return date.get();
}
public void setDate(LocalDate date) {
this.date.set(date);
}
public ObjectProperty<LocalDate> dateProperty() {
return date;
}
#FXML
protected void doSomething() {
System.out.println("The button was clicked! " + date.get().toString());
}
}
And want to assign the date to the child from parent fxml
parent.fxml
<fx:root type="com.example.javafx.ParentControl" xmlns:fx="http://javafx.com/fxml">
<ChildControl date="${controller.date}"/>
</fx:root>
child.fxml
<fx:root type="com.example.javafx.ChildControl" xmlns:fx="http://javafx.com/fxml">
<TextField fx:id="textField"/>
<Button text="Click Me" onAction="#doSomething"/>
</fx:root>
The problem is that FXMLLoader doesn’t not allow to use Binding Expression together with a Builder. I got "Cannot bind to builder property." exception.
Below is the part of the code from FXMLLoader.java and the very last if that causes the problem.
Is there some other solution?
FXMLLoader.java
public void processPropertyAttribute(Attribute attribute) throws IOException {
String value = attribute.value;
if (isBindingExpression(value)) {
// Resolve the expression
Expression expression;
if (attribute.sourceType != null) {
throw constructLoadException("Cannot bind to static property.");
}
if (!isTyped()) {
throw constructLoadException("Cannot bind to untyped object.");
}
// TODO We may want to identify binding properties in processAttribute()
// and apply them after build() has been called
if (this.value instanceof Builder) {
throw constructLoadException("Cannot bind to builder property.");
}

Spring Boot and Java Fx

To start my application, I'm avoiding the "implements CommandLineRunner" process to do the setup but I am facing a problem in this line
fxmlLoader.setControllerFactory(springContext::getBean);
where fxmlLoader is an instance of FxmlLoader and springContext ia an instance of ConfigurableApplicationContext. I am facing this error,
"The method setControllerFactory(Callback<Class<?>,Object>) in the
type FXMLLoader is not applicable for the arguments
(springContext::getBean)".
Can anyone help me with the exact syntax? My imported package reports an error as
"The type org.springframework.beans.factory.annotation.Autowire
cannot be resolved. It is indirectly referenced from required .class
files" .
ok, ive understood that this would require a little code, but ive found already built project with a solution similar to what ive proposed - heres example https://github.com/ruslanys/sample-spring-boot-javafx
you just to tie javafx to spring with context.getAutowireCapableBeanFactory().autowireBean(this);
in AbstractJavaFxApplicationSupport.java file
code will look like this
public abstract class AbstractJavaFxApplicationSupport extends Application {
private static String[] savedArgs;
protected ConfigurableApplicationContext context;
#Override
public void init() throws Exception {
context = SpringApplication.run(getClass(), savedArgs);
context.getAutowireCapableBeanFactory().autowireBean(this);
}
#Override
public void stop() throws Exception {
super.stop();
context.close();
}
protected static void launchApp(Class<? extends AbstractJavaFxApplicationSupport> appClass, String[] args) {
AbstractJavaFxApplicationSupport.savedArgs = args;
Application.launch(appClass, args);
}
}
and tie all you view like bean
#Configuration
public class ControllersConfiguration {
#Bean(name = "mainView")
public ViewHolder getMainView() throws IOException {
return loadView("fxml/main.fxml");
}
#Bean
public MainController getMainController() throws IOException {
return (MainController) getMainView().getController();
}
protected ViewHolder loadView(String url) throws IOException {
InputStream fxmlStream = null;
try {
fxmlStream = getClass().getClassLoader().getResourceAsStream(url);
FXMLLoader loader = new FXMLLoader();
loader.load(fxmlStream);
return new ViewHolder(loader.getRoot(), loader.getController());
} finally {
if (fxmlStream != null) {
fxmlStream.close();
}
}
}
public class ViewHolder {
private Parent view;
private Object controller;
public ViewHolder(Parent view, Object controller) {
this.view = view;
this.controller = controller;
}
public Parent getView() {
return view;
}
public void setView(Parent view) {
this.view = view;
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
}
}
then in controller you may enjoy spring magic and javafx magic together
public class MainController {
#Autowired private ContactService contactService;
#FXML private TableView<Contact> table;
#FXML private TextField txtName;
#FXML private TextField txtPhone;
#FXML private TextField txtEmail;}
and just start your app like this
#SpringBootApplication
public class Application extends AbstractJavaFxApplicationSupport {
#Value("${ui.title:JavaFX приложение}")//
private String windowTitle;
#Qualifier("mainView")
#Autowired
private ControllersConfiguration.ViewHolder view;
#Override
public void start(Stage stage) throws Exception {
stage.setTitle(windowTitle);
stage.setScene(new Scene(view.getView()));
stage.setResizable(true);
stage.centerOnScreen();
stage.show();
}
public static void main(String[] args) {
launchApp(Application.class, args);
}}

Store dynamic screen with multiple stages

I´ve got 2 different screens, in the first screen the user can load images and the other one is just a single button (the stage is invisible so the stage has to be different from screen 1) used to go back to the first stage. The problem is I don't know how to keep the images loaded when I go back from screen 2 to screen 1 (I hide stage 1 when I go to stage 2) . This is my current method in both screens.
#FXML
private void goToScreen1(ActionEvent event) throws Exception{
Stage stage = (Stage) showStage.getScene().getWindow();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/sample.fxml"));
Parent root = fxmlLoader.load();
Stage primaryStage = new Stage();
primaryStage.setResizable(true);
primaryStage.setOpacity(0.0);
primaryStage.show();
primaryStage.setX(0);
primaryStage.setY(0);
primaryStage.setHeight(primScreenBounds.getHeight());
primaryStage.setWidth(primScreenBounds.getWidth() / 2);
}
You have to store a reference to your stage content, so you don't have to reinstantiate it.
One possibility is to store it in the mainController and provide an EventHandler to your subController, to switch the stage
public interface ScreenController {
void setOnSwitchStage(EventHandler<ActionEvent> handler);
}
public class Screen1Controller implements Initializable, ScreenController {
#FXML
private Button btnSwitchScreen
public Screen1Controller() {
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
#Override
public void setOnSwitchStage(EventHandler<ActionEvent> handler) {
btnSwitchScreen.setOnAction(handler);
}
}
public class YourApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Stage secondaryStage = new Stage();
initScreen(primaryStage, secondaryStage, "screen1.fxml");
initScreen(secondaryStage, primaryStage, "screen2.fxml");
primaryStage.show();
}
private void initScreen(Stage parentStage, Stage nextStage, String resource) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource(resource));
Scene scene = new Scene(loader.load());
parentStage.setScene(scene);
ScreenController screenController = loader.getController();
screenController.setOnSwitchStage(evt -> {
nextStage.show();
parentStage.hide();
});
}
public static void main(String[] args) {
launch(args);
}
}
if the primaryStage is where "user can load images" do :
static Stage primaryStage;
#FXML
private void goToScreen1(ActionEvent event) throws Exception{
Stage stage = (Stage) showStage.getScene().getWindow();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/sample.fxml"));
Parent root = fxmlLoader.load();
if(primaryStage==null)
primaryStage = new Stage();
primaryStage.setResizable(true);
primaryStage.setOpacity(0.0);
primaryStage.show();
primaryStage.setX(0);
primaryStage.setY(0);
primaryStage.setHeight(primScreenBounds.getHeight());
primaryStage.setWidth(primScreenBounds.getWidth() / 2);
}

Update JavaFX scene graph from a Thread

I need to update my GUI based on client input. Calling my controller class method, from the background task works. But it can't update the GUI, because it is not the JavaFX application thread..please help.
I tried many of the related Q & A, but I am still confused.
Should I use Platform. runLater or Task ?
Here's my class where I create an instance of controller class
public class FactoryClass {
public static Controller_Gui1 createGUI() {
FXMLLoader fxLoader = new FXMLLoader();
fxLoader.setLocation(MainApp_Gui1.class.getResource("/com/Gui_1.fxml"));
AnchorPane anchorPane = null;
try {
anchorPane = (AnchorPane) fxLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
Controller_Gui1 controller_Gui1 = (Controller_Gui1) fxLoader
.getController();
Scene scene = new Scene(anchorPane);
//System.out.println(scene);
controller_Gui1.setScene(scene);
return controller_Gui1;
}
}
Controller class
#FXML
Button B1 = new Button();
#FXML
public void handleButton1() {
B1.setDisable(true);
}
Application class
public class MainApp_Gui1 extends Application {
Controller_Gui1 cGui;
#Override
public void start(Stage primaryStage) throws Exception {
initScene(primaryStage);
primaryStage.show();
System.out.println("asdasd");
SceneSetting sceneSetting = new SceneSetting();
//handleEvent();
System.out.println("after");
sceneSetting.setSceneAfter();
System.out.println("after2");
}
// creating scene
private void initScene(Stage primaryStage) throws IOException {
primaryStage.setScene(getScene(primaryStage));
}
public Scene getScene(Stage primaryStage) {
Controller_Gui1 cGui;
cGui = FactoryClass.createGUI();
return cGui.getScene();
}
public void ExcessFromOutside() {
Platform.runLater(new Runnable() {
public void run() {
System.out.println(Platform.isFxApplicationThread());
cGui.handleButton1();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
I want to call ExcessFromOutside() method from another thread.
I got a null pointer exception while trying to update the GUI
Here's my application class
public class MainAppGui1 extends Application {
Controller_Gui1 controller_Gui1;
#Override
public void start(Stage primaryStage) throws Exception {
initScene(primaryStage);
primaryStage.show();
}
// creating scene
public void initScene(Stage primaryStage) throws IOException {
FXMLLoader fxLoader = new FXMLLoader();
fxLoader.setLocation(MainApp_Gui1.class.getResource("/com/Gui_1.fxml"));
AnchorPane anchorPane=new AnchorPane();
anchorPane = (AnchorPane) fxLoader.load();
Controller_Gui1 controller_Gui1 = (Controller_Gui1) fxLoader.getController();
Scene scene = new Scene(anchorPane);
primaryStage.setScene(scene);
}
#FXML
public void ExcessFromOutside()
{
Platform.runLater(new Runnable() {
#Override
public void run() {
System.out.println("called atleast");
controller_Gui1.handleButton1();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
and this is the class from where i tried to update the GUI
public class Hudai {
public static void main(String args[]) throws InterruptedException
{
new Thread()
{
public void run()
{
MainAppGui1.main(null);
}
}.start();
Thread.sleep(5000);
MainAppGui1 m = new MainAppGui1();
m.ExcessFromOutside();
}
}
To disable your button in a different thread you can use Task's updateValue.
Task<Boolean> task = new Task<Boolean>() {
#Override
public Boolean call() {
... // The task that this thread needs to do
updateValue(true);
...
return null;
}
};
button.disableProperty().bind(task.valueProperty());
If you want to use a new thread to call a method, which alters the scene graph, the best chance you have is to use Platform.runLater() in it.
//code inside Thread
...
// code to run on the JavaFX Application thread
Platform.runLater(new Runnable() {
#Override
public void run() {
handleButton1();
}
});
...
You should get a NullPointerException when you run this program.
The problem is, the member of MainApp_Gui1
Controller_Gui1 cGui;
never gets a value.
Remove line "Controller_Gui1 cGui;" from this code:
public Scene getScene(Stage primaryStage) {
// Hudai hudai = new Hudai(primaryStage);
// return hudai.getScene();
Controller_Gui1 cGui;
cGui = FactoryClass.createGUI();
return cGui.getScene();
}

How to reload the screen when changing languages​ in JavaFX?

Well, I'm beginner with java and fxml.
I creating a application and need to change language of the screen. I have the file with internationalized keys but i no have idea as reload the screen with the changed language.
The application have a menu where have the language available. I want just refresh screen when the user change language.
change is still manual, as you can see on code: (Main.java):
public class Main extends Application {
private Locale locale = new Locale("en", "US");
private Image icon = new Image("picture.jpg");
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Home.fxml"), ResourceBundle.getBundle("label", locale));
Scene scene = new Scene(root);
stage.setTitle("GUI");
stage.getIcons().add(icon);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
This code is on the controller, when change language:
#FXML
private void btnMenuLanguageEnglishAction(ActionEvent event) {
this.locale = new Locale("en", "US");
}
#FXML
private void btnMenuLanguagePortuguesAction(ActionEvent event) {
this.locale = new Locale("pt", "BR");
}
how to send this locale to main and refresh the screen?
as will be the method that I use? I've tried some that I saw here on the site but no one answered my question.
Here is my implementation:
import javafx.fxml.FXMLLoader;
import javafx.geometry.NodeOrientation;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
/**
* Created by Fouad on 3/20/2015.
*/
public abstract class I18NController
{
private Stage primaryStage;
public void setPrimaryStage(Stage primaryStage){this.primaryStage = primaryStage;}
public Stage getPrimaryStage(){return primaryStage;}
public final void changeLanguage(I18NLanguage language) throws IOException
{
StateBundle stateBundle = new StateBundle();
onSaveState(stateBundle);
Locale locale = language.getLocale();
Locale.setDefault(locale);
ResourceBundle resourceBundle = getResourceBundle(locale);
URL fxml = getFXMLResource();
FXMLLoader loader = new FXMLLoader(fxml, resourceBundle);
Parent root = loader.load();
NodeOrientation nodeOrientation = language.getNodeOrientation();
root.setNodeOrientation(nodeOrientation);
primaryStage.setScene(new Scene(root));
primaryStage.sizeToScene();
I18NController newController = loader.getController();
newController.setPrimaryStage(primaryStage);
onLoadState(newController, language, resourceBundle, stateBundle);
}
protected abstract ResourceBundle getResourceBundle(Locale locale);
protected abstract URL getFXMLResource();
protected abstract void onSaveState(StateBundle stateBundle);
protected abstract void onLoadState(I18NController newController, I18NLanguage newLanguage, ResourceBundle resourceBundle, StateBundle stateBundle);
public static interface I18NLanguage
{
Locale getLocale();
NodeOrientation getNodeOrientation();
}
public static class StateBundle
{
private Map<String, Object> sMap = new HashMap<>();
StateBundle(){}
public void putData(String key, Object value)
{
sMap.put(key, value);
}
public <T> T getDate(String key, Class<T> type)
{
return type.cast(sMap.get(key));
}
}
}
You can use this class as base class to the controller, something like this:
JavaFXController.java:
public class JavaFXController extends I18NController implements Initializable
{
#FXML private DatePicker dpDate;
#FXML private RadioButton rdoArabic;
#FXML private RadioButton rdoEnglish;
// ...
#Override
public void initialize(URL location, ResourceBundle resources)
{
// ...
rdoEnglish.setOnAction(e ->
{
try
{
changeLanguage(AppSettings.Language.ENGLISH);
}
catch(IOException e1)
{
e1.printStackTrace();
}
});
rdoArabic.setOnAction(e ->
{
try
{
changeLanguage(AppSettings.Language.ARABIC);
}
catch(IOException e1)
{
e1.printStackTrace();
}
});
}
// ...
#Override
protected ResourceBundle getResourceBundle(Locale locale)
{
return ResourceBundle.getBundle("com/stackoverflow/gui/resources/JavaFXResourceBundle", locale, new UTF8Control());
}
#Override
protected URL getFXMLResource()
{
return getClass().getResource("resources/JavaFXDocument.fxml");
}
#Override
protected void onSaveState(StateBundle stateBundle)
{
LocalDate localDate = dpDate.getValue();
boolean isRdoArabicSelected = rdoArabic.isSelected();
boolean isRdoEnglishSelected = rdoEnglish.isSelected();
stateBundle.putData("localDate", localDate);
stateBundle.putData("isRdoArabicSelected", isRdoArabicSelected);
stateBundle.putData("isRdoEnglishSelected", isRdoEnglishSelected);
}
#Override
protected void onLoadState(I18NController newController, I18NLanguage newLanguage, ResourceBundle resourceBundle, StateBundle stateBundle)
{
JavaFXController controller = (JavaFXController) newController;
controller.getPrimaryStage().setTitle(resourceBundle.getString("window.title"));
NodeOrientation nodeOrientation = newLanguage.getNodeOrientation();
LocalDate localDate = stateBundle.getDate("localDate", LocalDate.class);
boolean isRdoArabicSelected = stateBundle.getDate("isRdoArabicSelected", Boolean.class);
boolean isRdoEnglishSelected = stateBundle.getDate("isRdoEnglishSelected", Boolean.class);
controller.dpDate.setValue(localDate);
controller.rdoArabic.setSelected(isRdoArabicSelected);
controller.rdoEnglish.setSelected(isRdoEnglishSelected);
}
}
AppSettings.java:
import com.parmajeyat.autobooking.gui.I18NController;
import javafx.geometry.NodeOrientation;
import java.util.Locale;
/**
* Created by Fouad on 2/7/2015.
*/
public final class AppSettings
{
private static final class Locales
{
public static final Locale SAUDI_AR_LOCALE = new Locale.Builder().setLanguageTag("ar-SA-u-nu-arab").build(); // nu is for numbers
public static final Locale SAUDI_EN_LOCALE = new Locale("en", "SA");
}
public static enum Language implements I18NController.I18NLanguage
{
ARABIC(Locales.SAUDI_AR_LOCALE, NodeOrientation.RIGHT_TO_LEFT),
ENGLISH(Locales.SAUDI_EN_LOCALE, NodeOrientation.LEFT_TO_RIGHT);
private Locale locale;
private NodeOrientation nodeOrientation;
Language(Locale locale, NodeOrientation nodeOrientation)
{
this.locale = locale;
this.nodeOrientation = nodeOrientation;
}
public Locale getLocale(){return locale;}
public NodeOrientation getNodeOrientation(){return nodeOrientation;}
}
}
UTF8Control.java:
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
/**
* Created by Fouad on 2/1/2015.
*/
public class UTF8Control extends ResourceBundle.Control
{
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException
{
// The below is a copy of the default implementation.
String bundleName = toBundleName(baseName, locale);
String resourceName = toResourceName(bundleName, "properties");
ResourceBundle bundle = null;
InputStream stream = null;
if(reload)
{
URL url = loader.getResource(resourceName);
if(url != null)
{
URLConnection connection = url.openConnection();
if(connection != null)
{
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
}
else
{
stream = loader.getResourceAsStream(resourceName);
}
if(stream != null)
{
try
{
// Only this line is changed to make it to read properties files as UTF-8.
bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
}
finally
{
stream.close();
}
}
return bundle;
}
}
I know, it is quite old question, but I am new in JavaFX and I had just the same problem.
Here is my final solution for change language in app. It may not be ideal, but it works for me.
In controller I have that method:
#FXML
private BorderPane root; //root pane
#FXML
private void changeLocale(ActionEvent event) throws IOException{
Scene scene = root.getScene();
if(event.getSource().equals(lang_en)){
scene.setRoot(FXMLLoader.load(getClass().getResource("Layout.fxml"),ResourceBundle.getBundle("resources/Bundle", Locale.ENGLISH))); // = new Locale("en")
}else if(event.getSource().equals(lang_cs)){
scene.setRoot(FXMLLoader.load(getClass().getResource("Layout.fxml"),ResourceBundle.getBundle("resources/Bundle", new Locale("cs", "CZ"))));
}else{
}
}
The method loads new loader into my scene (to load into stage also works).
For complete scenario ... I can change current language with two radiomenuitems in menu, so after loading new loader (in the method "public void initialize(URL location, ResourceBundle resources)" in controller) I change selection of radiomenuitems with this switch:
switch(resources.getLocale().getLanguage()){
case "en":
lang_en.setSelected(true);
break;
case "cs":
lang_cs.setSelected(true);
break;
default:
break;
}
It is simple and may be useful for someone else who will have that problem.
Use binding with your Labeled (Label, Text, TitledPane, etc).
I managed to solve, the code:
Main:
public class Main extends Application {
private static Locale locale = new Locale("pt", "BR");
private static Image icone = new Image("picture.jpg");
private Scene scene;
public static Stage stage;
/**
*
* #param st
* #throws Exception
*/
#Override
public void start(Stage st) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Home.fxml"), ResourceBundle.getBundle("label", locale));
stage = st;
scene = new Scene(root);
stage.setTitle("GUI");
stage.getIcons().add(icone);
stage.setScene(scene);
stage.show();
}
public static Image getIcone() {
return icone;
}
public static Locale getLocale() {
return locale;
}
public static void setLocale(Locale locale) {
Main.locale = locale;
}
public void reload() throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Home.fxml"), ResourceBundle.getBundle("label", locale));
scene = new Scene(root);
stage.setTitle("GUI");
stage.getIcons().add(icone);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
the controller:
#FXML
private void btnMenuLanguageEnglishAction(ActionEvent event) throws IOException {
Main.setLocale(new Locale("en", "US")); // change to english
Main.stage.close();
Main reload = new Main();
reload.reload();
}
#FXML
private void btnMenuLanguagePortuguesAction(ActionEvent event) throws IOException {
Main.setLocale(new Locale("pt", "BR")); // change to Portuguese;
Main.stage.close();
Main reload = new Main();
reload.reload();
}
but still has a problem: the language change can occur only once, the second time gives fatal error... Hope that helps someone.

Resources