调用setImage()后,包含ImageView的ScrollPane不会更新其滚动条 [英] ScrollPane containing ImageView does not update its scrollbars after calling setImage()

查看:114
本文介绍了调用setImage()后,包含ImageView的ScrollPane不会更新其滚动条的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用简单的图像查看器试验JavaFX。我希望它显示图像,如果它不适合窗口,则显示滚动条。要显示的图像使用 FileChooser 加载,并使用 imageView.setImage设置为 ImageView (图片)

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).

问题是包含 ScrollPane <的滚动条调用 imageView.setImage(image)后,em>不更新。相反,我需要执行一个改变场景的动作(例如调整窗口大小)。当显示图像并加载另一个图像时,它的行为相同,即滚动条的大小反映了前一个图像的大小。

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>


推荐答案

我可以确认这个错误。一个快速而肮脏的解决方法,为我修复它:

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

snapshot()每当某个UI元素未更新或正确显示时,方法有时会像魅力一样工作。但是不要问我为什么会这样,也许它与渲染过程有关,因为 snapshots 强制JavaFX渲染场景图或目标元素。在 ImageView 的一部分之后添加新图像时,需要随机的东西来创建正确的滚动条大小。
也许这与我几周前发现的奇怪行为。当时的解决方法也解决了我的问题。

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.

(真的很晚)编辑

问题是由包含 ImageView 引起的。如果您使用继承自 Pane 的节点,即使没有解决方法, ScrollPane 的行为也应符合预期。只要 Pane,仍可用作 ScrollPane 的直接子项包装 ImageView

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.

示例:

<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>

这篇关于调用setImage()后,包含ImageView的ScrollPane不会更新其滚动条的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆