2015-11-23 52 views
1

我是JavaFX的新手,請原諒Noobishness。JavaFX:將從根視圖觸發的FileChooser中提取的圖像傳遞到嵌套視圖中的ImageView對象

所以我正在製作圖片瀏覽器。這是我第一次用MVC模式設計GUI,所以我不太確定自己在做什麼。

我正在試圖完成:

這只是一個正常的圖片瀏覽器。就像您想要查看圖片時打開的標準Windows程序一樣。

我所做的:

所以我現在有2次:

  1. 的Root.fxml觀點,這是隻是一個BorderPane一個菜單欄頂部 與菜單項「打開」這觸發FileChooser並將MainApp.java currentImage變量設置爲用戶選擇的任何值。

  2. 嵌套在(root.setCenter)裏面,我有ImageViewer.fxml視圖, 這是一個AnchorPane,它只包含一個ImageView。

我100%肯定我的FXML沒有問題,並且所有控制器和變量都被正確綁定。這在MainApp或控制器中必定是個問題。

我不知道如何將圖像從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(模型圖像類來保存屬性):

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

回答

1

當初始化應有盡有,ImageViewController是觀察的主要應用程序的當前MyImage對象的imageProperty。當從菜單中選擇新圖像時,在主應用程序中設置了一個新的MyImage對象:但ImageViewController仍在觀察原始MyImage對象中的imagePropertyimageProperty永不改變。

你可能想使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))); 
    } 
} 
相關問題