JavaFX change the image in an imageView

2020-07-14 12:54发布

问题:

Basically I have a method to load an Image from database into an imageView and a second method to change the image I'm sucessfully running both methods without getting an exception but after the setImage in changeImage() method what do I need to update and how (scene,stage) is it possible at all. I know that there is no method like repaint() in swing in javafx, so how do I approach this ?

public class MainMenuController implements Initializable {

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }

    private AnchorPane stck1;

 @FXML
    private AnchorPane openSecondWindow(ActionEvent event) throws Exception {
        GUIController ctrl = new GUIController();
        Stage stage = new Stage();
       setStck1((AnchorPane) FXMLLoader.load(InteractiveFictionGame2.class.getResource("GUI.fxml")));
        ImageView img_1 = new ImageView(ctrl.loadImg().getImage());
        img_1.setPreserveRatio(true);
        img_1.setSmooth(true);
        img_1.setCache(true);
        getStck1().getChildren().add(img_1);
        Scene scene = new Scene(getStck1());
        stage.setTitle("Interactive Fiction Game");
        stage.setScene(scene);
         stage.setFullScreen(true);
       // stage.sizeToScene();
        stage.show();
       return getStck1();
    }






public class GUIController implements Initializable {

    @FXML
    private TabPane tb1;

    /**
     * Initializes the controller class.
     *
     * @param url
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }
    @FXML
    private ImageView img_1;





 public ImageView loadImg() {

        try {

            con = DriverManager.getConnection(host, unm, pswrd);
            stmnt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
            rs = stmnt.executeQuery(SQL);
            rs.next();
            fis = rs.getBinaryStream(4);
            imgt = javax.imageio.ImageIO.read(fis);
            Image newImg = SwingFXUtils.toFXImage(imgt, null);
            img_1 = new ImageView();
            img_1.setImage(newImg);
            rs.close();
            stmnt.close();

            con.close();
        } catch (Exception e) {
            System.out.println("Not working");
        }
        return img_1;
    }


public void changeImage() {
..
            fis = rs.getBinaryStream(1);
            imgt = javax.imageio.ImageIO.read(fis);
            Image newImg = SwingFXUtils.toFXImage(imgt, null);
            img_1.setImage(newImg);
...
 } catch (Exception e) {
            System.out.println("Not working");
        }
        return img_1;
    }

回答1:

Your Issue

If you have a member node in your controller which you inject using @FXML, you should never create a new object instance using a new constructor and assign that new object to your existing reference. Instead just use the object with FXML created for you.

You have:

@FXML
private ImageView img_1;

That's fine. Then in loadImg, you have:

img_1 = new ImageView();
img_1.setImage(newImg);

That is bad. You already have an ImageView which the FXMLLoader created for you when you loaded your FXML document. The FXML Loader then assigned that ImageView to your img_1 reference because you used an @FXML annotation.

How to Fix it

So all you need to do is to stop creating new ImageViews and only write:

img_1.setImage(newImg);

And you are done.

Why it works

The Image property of ImageView is an observable property. The JavaFX system observes the Image property for any changes and if it changes, automatically updates the image displayed on the screen for the ImageView. You don't need to perform any repaint call (there is no such repaint routine to call in any case).

Background Reading

If you want to understand the JavaFX scene graph architecture better, read the Oracle tutorial on it:

  • Working with the JavaFX Scene Graph.

Some Tips

  • You can create a JavaFX image directly from an InputStream, you don't need to use ImageIO and SwingFXUtils for this task.
  • You can use a Task to communicate with a database and your application may be more responsive.
  • It is probably simpler to read the image from a file or over http rather than from a database.

Disclaimer

Besides the issue pointed out here, there may be other errors in code you have not provided which may prevent you from getting your application to work as you wish.



回答2:

Java I graduate here:
In my JavaFX term project I had to update an imageView object upon a setOnAction Event (clicking a button). This allowed the program user to click through a series of pictures.

The following worked great:
First create your Image and ImageView instances:

Image imageObject = new Image();
ImageView imageViewObject = new ImageView();

Then down in the code a button event causes the (next) image to be assigned and updated as follows:

btn.setOnAction(e -> {
    imageIndex++;
    imageFilename = imageNamesArray.get(imageIndex);
    imageObject = new Image(imageFilename);  
    imageViewObject.setImage(imageObject);
}

Note: The filename(s) in my project are jpg file (names) saved as String elements in an ArrayList(). The button click also increments the array index to the next jpg filename (and path or URL) and the new image would appear. So as in the aforementioned answer you only create one ImageViewObject but you do reassign a new image to the image object "imageObject" each time.