ScrollPane containing ImageView does not update it

2019-02-27 00:59发布

I am experimenting JavaFX with a simple image viewer. I want it to display an image and, if it does not fit in the window, display scrollbars. The image to dislay is loaded with the FileChooser and set to the ImageView using imageView.setImage(image).

The problem is that the scrollbars of the containing ScrollPane do not update after calling imageView.setImage(image). Instead, I need to perform an action that changes the scene (e.g. resize the window). It behaves the same when an image is displayed and another is loaded, i.e. the sizes of the scrollbars reflect the size of the previous image.


The bug is reproducible using the following cut-down version of the code:

(Java)

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

public class ImageViewerBug extends Application
{
    @FXML private ImageView imageView;

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ImageViewerBug.fxml"));
        fxmlLoader.setController(this);
        try {
            BorderPane borderPane = (BorderPane) fxmlLoader.load();
            Scene scene = new Scene(borderPane);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
        catch( IOException e ) {
            throw new RuntimeException(e);
        }
    }

    public void loadClicked() {
        FileChooser fileChooser = new FileChooser();
        File f = fileChooser.showOpenDialog(null);
        if( f != null ) {
            try( InputStream is = new FileInputStream(f) ) {
                Image image = new Image(is);
                imageView.setImage(image);
            }
            catch( IOException e ) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

(FXML)

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.Group?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<BorderPane id="BorderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml">
  <center>
    <ScrollPane prefHeight="200.0" prefWidth="200.0">
      <content>
        <Group>
          <ImageView fx:id="imageView" pickOnBounds="true" preserveRatio="true" />
        </Group>
      </content>
    </ScrollPane>
  </center>
  <top>
    <ToolBar>
      <items>
        <Button mnemonicParsing="false" onAction="#loadClicked" text="Load" />
      </items>
    </ToolBar>
  </top>
</BorderPane>

2条回答
老娘就宠你
2楼-- · 2019-02-27 01:22

I can confirm this bug. A quick and dirty workaround that fixed it for me:

    public void loadClicked() {
    FileChooser fileChooser = new FileChooser();
    File f = fileChooser.showOpenDialog(null);
    if( f != null ) {
        try( InputStream is = new FileInputStream(f) ) {
            Image image = new Image(is);
            imageView.setImage(image);
            imageView.snapshot(new SnapshotParameters(), new WritableImage(1, 1));
            double rnd = new Random().nextInt(10);
            imageView.setX(rnd/1000);
            imageView.setY(rnd/1000);
        }
        catch( IOException e ) {
            e.printStackTrace();
        }
    }
}

The snapshot() Method sometimes works like a charm whenever some UI-element isn't updated or displayed correctly. But don't ask me why that works, maybe it has something to do with the rendering process, because snapshots forces JavaFX to render the scene graph or the target elements. The random stuff is needed to create the right scrollbar sizes when adding a new image after one already part of the ImageView. Maybe this has something to do with the strange behaviour i found some weeks earlier. The workaround also solved my problems back then.

(Really late)EDIT

The problem is caused by the Group which wraps the ImageView. If you use a node that inherits from Pane the behaviour of the ScrollPane should be as expected even without the workaround. A Group can still be used as the direct child of the ScrollPane as long as a Pane wraps the ImageView.

Example:

<ScrollPane prefHeight="200.0" prefWidth="200.0">
  <content>
    <Group id="Group">
      <children>
        <HBox id="HBox" alignment="CENTER" layoutX="0.0" layoutY="0.0" spacing="5.0">
          <children>
            <ImageView fx:id="imageView" pickOnBounds="true" preserveRatio="true" />
          </children>
        </HBox>
      </children>
    </Group>
  </content>
</ScrollPane>
查看更多
Luminary・发光体
3楼-- · 2019-02-27 01:30

I have had the same problem with image...Instead of

Image image = new Image(is);

You should write:

Image image = new Image("file:" + ABSOLUTE_PATH_TO_FILE);

Image will refresh automatically, you don't have to do anything. For me, it works

查看更多
登录 后发表回答