JavaFX:将从根视图触发的 FileChooser 中提取的图像传递到嵌套视图中的 ImageView 对象
JavaFX: Passing on an Image extracted from a FileChooser triggered from the Root view into an ImageView object in a nested view
我是 JavaFX 的新手,所以请原谅我的笨拙。
所以我正在制作图片查看器。这是我第一次使用 MVC 范式设计 GUI,所以我不太确定我在做什么。
我想要完成的事情:
这只是一个普通的图片查看器。就像您想要查看图片时打开的标准 Windows 程序一样。
我做了什么:
所以我目前有2个观点:
Root.fxml 视图,这是一个 BorderPane,顶部只有一个 MenuBar
带有触发 FileChooser 并设置
MainApp.java currentImage 变量为用户选择的任何内容。
嵌套在 (root.setCenter) 里面,我有 ImageViewer.fxml 视图,
这是一个仅包含 ImageView 的 AnchorPane。
我 100% 确定我的 FXML 没有任何问题,并且所有控制器和变量都已正确绑定。这一定是 MainApp 或 Controllers 中的问题。
我不知道如何将图像从 RootViewController 传递到 ImageViewerController。每当我尝试在 ImageViewerController 初始化程序中为 mainApp.currentImage 初始化侦听器时,我都会收到 NullPointerException。如果我把它放在 setMainApp 方法中,它什么也不做。这是我的代码:
代码:
MainApp.java
import sample.model.MyImage;
import samlple.view.ImageViewerController;
import sample.view.RootController;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
public class MainApp extends Application {
final static File img = new File("C:\test\1.png"); // test initial image loading
private Stage primaryStage;
private BorderPane rootLayout;
private MyImage currentImage = new MyImage(img);
public MainApp() {
}
public MyImage getCurrentImage(){
return this.currentImage;
}
public void setCurrentImage(MyImage myImage){
currentImage = myImage;
}
public void start(Stage primaryStage) throws Exception{
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Hi");
initRootLayout();
showImageViewer();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/Root.fxml"));
rootLayout = loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// Giving the controller access to the main app.
RootController controller = loader.getController();
controller.setMainApp(this);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
System.err.println("Root layout loading error.");
}
}
/**
* Shows the ImageViewer inside the root layout.
*/
public void showImageViewer() {
try {
// Load ImageViewer.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/ImageViewer.fxml"));
AnchorPane imageViewer = loader.load();
// Set the ImageViewer into the center of root layout.
rootLayout.setCenter(imageViewer);
// Giving the controller access to the main app.
ImageViewerController controller = loader.getController();
controller.setMainApp(this);
} catch (IOException e) {
e.printStackTrace();
System.err.println("ImageViewer layout loading error.");
}
}
/**
* Called when user clicks the "Open" menu item
*/
public void menuOpenImageFile() {
FileChooser fileChooser = new FileChooser();
//Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("Image types (*.jpg; *.gif; *.bmp; *.png)", "*.jpg", "*.png", "*.bmp", "*.gif");
fileChooser.getExtensionFilters().add(extFilter);
//Show save file dialog
File imageFile = fileChooser.showOpenDialog(primaryStage);
MyImage myImageToSetAsCurrent = new MyImage(imageFile);
setCurrentImage(myImageToSetAsCurrent);
}
/**
* Returns the main stage.
* @return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) { launch(args); }
}
MyImage.java(一个模型图像 class 来保存属性):
package sample.model;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.image.Image;
import java.io.File;
public class MyImage {
private final ObjectProperty<File> imageFile;
private final ObjectProperty<Image> image;
public MyImage(){
this(null);
}
public MyImage(File imageFile) {
this.imageFile = new SimpleObjectProperty<>(imageFile);
this.image = new SimpleObjectProperty<>(new Image("file:" + imageFile.toString()));
}
public File getImageFile() {
return imageFile.get();
}
public ObjectProperty<File> imageFileProperty() {
return imageFile;
}
public void setImageFile(File myImageFile) {
this.imageFile.set(myImageFile);
}
public Image getImage() {
return image.get();
}
public ObjectProperty<Image> imageProperty() {
return image;
}
public void setImage(Image myImage) {
this.image.set(myImage);
}
}
RootController.java
import samlple.MainApp;
import javafx.fxml.FXML;
import javafx.scene.control.MenuItem;
public class RootController {
@FXML
private MenuItem open;
// Reference to the main application.
private MainApp mainApp;
/**
* The constructor.
* The constructor is called before the initialize() method.
*/
public RootController() {
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
@FXML
private void initialize() {
}
@FXML
private void openImageFile () {
mainApp.menuOpenImageFile();
}
/**
* Is called by the main application to give a reference back to itself.
*
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
}
}
ImageViewerController.java
import sample.MainApp;
import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class ImageViewerController {
@FXML
private ImageView imageView;
// Reference to the main application.
private MainApp mainApp;
/**
* The constructor.
* The constructor is called before the initialize() method.
*/
public ImageViewerController() {
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
@FXML
private void initialize() {
mainApp.getCurrentImage().imageProperty().addListener(((observable, oldValue, newValue) -> imageView.setImage(newValue))); //This keeps throwing NullPointerException!
}
/**
* Is called by the main application to give a reference back to itself.
*
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
imageView.setImage(mainApp.getCurrentImage().imageProperty().get());
}
}
当您初始化所有内容时,ImageViewController
正在观察主应用程序的 current MyImage
对象的 imageProperty
。当您从菜单中选择新图像时,您在主应用程序中设置了一个新的 MyImage
对象:但是 ImageViewController
仍在观察原始 MyImage
对象中的 imageProperty
. imageProperty
永远不会改变。
您可能希望将 currentImage
设为最终版本,只需更改 属性:
public class MainApp extends Application {
final static File img = new File("C:\test\1.png"); // test initial image loading
private Stage primaryStage;
private BorderPane rootLayout;
private final MyImage currentImage = new MyImage(img);
public MainApp() {
}
public MyImage getCurrentImage(){
return this.currentImage;
}
// public void setCurrentImage(MyImage myImage){
// currentImage = myImage;
// }
// ...
public void menuOpenImageFile() {
FileChooser fileChooser = new FileChooser();
//Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("Image types (*.jpg; *.gif; *.bmp; *.png)", "*.jpg", "*.png", "*.bmp", "*.gif");
fileChooser.getExtensionFilters().add(extFilter);
//Show save file dialog
File imageFile = fileChooser.showOpenDialog(primaryStage);
currentImage.setImageFile(imageFile);
currentImage.setImage(new Image(imageFile.toURI().toString()));
}
}
请注意,您还有另一个错误:您在控制器的 initialize()
方法中调用了 mainApp.getCurrentImage()
,在您有机会设置主应用程序之前必须调用该方法。您应该在 setMainApp(...)
方法中注册侦听器:
import sample.MainApp;
import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class ImageViewerController {
@FXML
private ImageView imageView;
// Reference to the main application.
private MainApp mainApp;
/**
* Is called by the main application to give a reference back to itself.
*
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
imageView.setImage(mainApp.getCurrentImage().imageProperty().get());
mainApp.getCurrentImage().imageProperty().addListener(((observable, oldValue, newValue) -> imageView.setImage(newValue)));
}
}
我是 JavaFX 的新手,所以请原谅我的笨拙。
所以我正在制作图片查看器。这是我第一次使用 MVC 范式设计 GUI,所以我不太确定我在做什么。
我想要完成的事情:
这只是一个普通的图片查看器。就像您想要查看图片时打开的标准 Windows 程序一样。
我做了什么:
所以我目前有2个观点:
Root.fxml 视图,这是一个 BorderPane,顶部只有一个 MenuBar 带有触发 FileChooser 并设置 MainApp.java currentImage 变量为用户选择的任何内容。
嵌套在 (root.setCenter) 里面,我有 ImageViewer.fxml 视图, 这是一个仅包含 ImageView 的 AnchorPane。
我 100% 确定我的 FXML 没有任何问题,并且所有控制器和变量都已正确绑定。这一定是 MainApp 或 Controllers 中的问题。
我不知道如何将图像从 RootViewController 传递到 ImageViewerController。每当我尝试在 ImageViewerController 初始化程序中为 mainApp.currentImage 初始化侦听器时,我都会收到 NullPointerException。如果我把它放在 setMainApp 方法中,它什么也不做。这是我的代码:
代码:
MainApp.java
import sample.model.MyImage;
import samlple.view.ImageViewerController;
import sample.view.RootController;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
public class MainApp extends Application {
final static File img = new File("C:\test\1.png"); // test initial image loading
private Stage primaryStage;
private BorderPane rootLayout;
private MyImage currentImage = new MyImage(img);
public MainApp() {
}
public MyImage getCurrentImage(){
return this.currentImage;
}
public void setCurrentImage(MyImage myImage){
currentImage = myImage;
}
public void start(Stage primaryStage) throws Exception{
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Hi");
initRootLayout();
showImageViewer();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/Root.fxml"));
rootLayout = loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// Giving the controller access to the main app.
RootController controller = loader.getController();
controller.setMainApp(this);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
System.err.println("Root layout loading error.");
}
}
/**
* Shows the ImageViewer inside the root layout.
*/
public void showImageViewer() {
try {
// Load ImageViewer.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/ImageViewer.fxml"));
AnchorPane imageViewer = loader.load();
// Set the ImageViewer into the center of root layout.
rootLayout.setCenter(imageViewer);
// Giving the controller access to the main app.
ImageViewerController controller = loader.getController();
controller.setMainApp(this);
} catch (IOException e) {
e.printStackTrace();
System.err.println("ImageViewer layout loading error.");
}
}
/**
* Called when user clicks the "Open" menu item
*/
public void menuOpenImageFile() {
FileChooser fileChooser = new FileChooser();
//Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("Image types (*.jpg; *.gif; *.bmp; *.png)", "*.jpg", "*.png", "*.bmp", "*.gif");
fileChooser.getExtensionFilters().add(extFilter);
//Show save file dialog
File imageFile = fileChooser.showOpenDialog(primaryStage);
MyImage myImageToSetAsCurrent = new MyImage(imageFile);
setCurrentImage(myImageToSetAsCurrent);
}
/**
* Returns the main stage.
* @return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) { launch(args); }
}
MyImage.java(一个模型图像 class 来保存属性):
package sample.model;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.image.Image;
import java.io.File;
public class MyImage {
private final ObjectProperty<File> imageFile;
private final ObjectProperty<Image> image;
public MyImage(){
this(null);
}
public MyImage(File imageFile) {
this.imageFile = new SimpleObjectProperty<>(imageFile);
this.image = new SimpleObjectProperty<>(new Image("file:" + imageFile.toString()));
}
public File getImageFile() {
return imageFile.get();
}
public ObjectProperty<File> imageFileProperty() {
return imageFile;
}
public void setImageFile(File myImageFile) {
this.imageFile.set(myImageFile);
}
public Image getImage() {
return image.get();
}
public ObjectProperty<Image> imageProperty() {
return image;
}
public void setImage(Image myImage) {
this.image.set(myImage);
}
}
RootController.java
import samlple.MainApp;
import javafx.fxml.FXML;
import javafx.scene.control.MenuItem;
public class RootController {
@FXML
private MenuItem open;
// Reference to the main application.
private MainApp mainApp;
/**
* The constructor.
* The constructor is called before the initialize() method.
*/
public RootController() {
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
@FXML
private void initialize() {
}
@FXML
private void openImageFile () {
mainApp.menuOpenImageFile();
}
/**
* Is called by the main application to give a reference back to itself.
*
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
}
}
ImageViewerController.java
import sample.MainApp;
import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class ImageViewerController {
@FXML
private ImageView imageView;
// Reference to the main application.
private MainApp mainApp;
/**
* The constructor.
* The constructor is called before the initialize() method.
*/
public ImageViewerController() {
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
@FXML
private void initialize() {
mainApp.getCurrentImage().imageProperty().addListener(((observable, oldValue, newValue) -> imageView.setImage(newValue))); //This keeps throwing NullPointerException!
}
/**
* Is called by the main application to give a reference back to itself.
*
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
imageView.setImage(mainApp.getCurrentImage().imageProperty().get());
}
}
当您初始化所有内容时,ImageViewController
正在观察主应用程序的 current MyImage
对象的 imageProperty
。当您从菜单中选择新图像时,您在主应用程序中设置了一个新的 MyImage
对象:但是 ImageViewController
仍在观察原始 MyImage
对象中的 imageProperty
. imageProperty
永远不会改变。
您可能希望将 currentImage
设为最终版本,只需更改 属性:
public class MainApp extends Application {
final static File img = new File("C:\test\1.png"); // test initial image loading
private Stage primaryStage;
private BorderPane rootLayout;
private final MyImage currentImage = new MyImage(img);
public MainApp() {
}
public MyImage getCurrentImage(){
return this.currentImage;
}
// public void setCurrentImage(MyImage myImage){
// currentImage = myImage;
// }
// ...
public void menuOpenImageFile() {
FileChooser fileChooser = new FileChooser();
//Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("Image types (*.jpg; *.gif; *.bmp; *.png)", "*.jpg", "*.png", "*.bmp", "*.gif");
fileChooser.getExtensionFilters().add(extFilter);
//Show save file dialog
File imageFile = fileChooser.showOpenDialog(primaryStage);
currentImage.setImageFile(imageFile);
currentImage.setImage(new Image(imageFile.toURI().toString()));
}
}
请注意,您还有另一个错误:您在控制器的 initialize()
方法中调用了 mainApp.getCurrentImage()
,在您有机会设置主应用程序之前必须调用该方法。您应该在 setMainApp(...)
方法中注册侦听器:
import sample.MainApp;
import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class ImageViewerController {
@FXML
private ImageView imageView;
// Reference to the main application.
private MainApp mainApp;
/**
* Is called by the main application to give a reference back to itself.
*
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
imageView.setImage(mainApp.getCurrentImage().imageProperty().get());
mainApp.getCurrentImage().imageProperty().addListener(((observable, oldValue, newValue) -> imageView.setImage(newValue)));
}
}