[JavaFx ImageView通过fxml表达式绑定来绑定图像

问题描述 投票:0回答:2

我知道那里有很多javafx教程。但是它们中的99%不能提供良好的fxml东西。在fxml中,我们可以执行数据绑定或“表达式绑定”,如何在javafx上调用它。

通过表达式绑定将图像绑定到ImageView。但是,如果图像更改,则实际显示的图像不会更改。我如何在javafx中做到这一点,并尽量减少代码隐藏。

控制器文件

class MyController {
    private Image _imageA;
    private Image_ imageB;
    private Image_ image;

    public MyController(){
        _imageA = new Image(this.getClass().getResourceAsStream("imageA.png"));
        _imageB = new Image(this.getClass().getResourceAsStream("imageB.png"));
        _image = _imageA;
    }

    public getImage(){
        return _image;
    }

    public void mouseEnter(MouseEvent mouseEvent){
        _image = _imageA;
    }

    public void mouseLeave(MouseEvent mouseEvent){
        _image = _imageB;
    }
}

fxml文件

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

<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="_anchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="868.0" prefWidth="1124.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MyController">

   <ImageView fx:id="_imgview" image="${controller.image}" onMouseEnter="#mouseEnter" onMouseExited="#mouseLeave"/>


</AnchorPane>

当应用程序启动时,将显示图像。但是,当鼠标指针移到图像上方时,将调用getImage方法,但图像不会改变。有没有一种方法可以用最少的代码来解决这个问题?

我不想在这里做这件事

class MyController implements Initializable {
    //..other stuff
    @FXML
    private ImageView _imgview;
    SimpleObjectProperty<Image> _propertyImage = new SimpleObjectProperty<>();

    public void initialize(URL url, ResourceBundle resourceBundle) {
        _imageViewSettings.imageProperty().bind(_propertySettingsImage);
    }

    public void mouseEnter(MouseEvent mouseEvent){
        _imageProperty.setValue(_imageB);
    }

    public void mouseLeave(MouseEvent mouseEvent){
        _imageProperty.setValue(_imageA);
    }
}

使用TableView,已经可以通过可观察的集合绑定数据。如果列表内容更改,则表视图也会更改。因此在这里有效。

<TableView items="${controller.items}">
<!-- other stuff-->
 </TableView>

class TableController{

   private ObservableList<Integer> _list = FXCollections.observableArrayList();

   ObservableList<Integer> getItems(){
        return _list;
   }
}

正如我之前提到的,有很多javafx示例。但是只有几篇介绍fxml。大多数人使用

GridPane p = new GridPane();;
p.setAlignment(...);
p.setChildren(...);

但是我认为引入FXML是为了摆脱WPF / XAML中所有这些代码隐藏的东西。长文本简短问题。如何将Image绑定到ImageView,以便当Image更改时,ImageView将显示新图像?

亲切的问候,鲍勃。

java javafx data-binding expression fxml
2个回答
0
投票

TL; DR:要在FXML文件中进行绑定,您需要使图像属性可观察(例如,通过ObjectProperty<Image>)。


FXML本质上没有什么特别之处。它只是定义了一种以XML语法描述对象图的方法,该对象图由FXMLLoader解释并转换为普通Java对象。它使用相对基本的规则和Java(FX)Bean约定来实现此目的。可以在Introduction to FXML文档中找到更多信息。

使用表达式绑定时,您正在使用JavaFX Property接口的绑定API。换句话说,当您拥有:

<ImageView image="${controller.image}"/>

与在代码中执行以下操作基本相同:

public class FooController {

  private final ObjectProperty<Image> image = new SimpleObjectProperty<>(this, "image");
  // property-getter, getter, and setter omitted for brevity

  @FXML private ImageView imageView; // corresponding fx:id="imageView" in FXML file

  @FXML
  private void initialize() {
    // the "expression binding"
    imageView.imageProperty().bind(imageProperty());
  }
}

这就是您示例中的问题所在。您的控制器不会公开observable图片属性;您只有getImage()方法。由于无法观察到任何内容,因此ImageView更改时Image无法自动更新(即没有任何要绑定的内容)。

这是控制器公开ObjectProperty<Image>的示例。

App.fxml:

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.StackPane?>

<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml/1"
            fx:controller="Controller" prefWidth="1000" prefHeight="650">

  <top>
    <Button text="Browse..." onAction="#handleBrowse" BorderPane.alignment="CENTER_RIGHT">
      <BorderPane.margin>
        <Insets topRightBottomLeft="5"/>
      </BorderPane.margin>
    </Button>
  </top>

  <center>
    <ScrollPane fitToHeight="true" fitToWidth="true" vbarPolicy="NEVER" hbarPolicy="NEVER" 
                pannable="true">
      <StackPane minWidth="-Infinity" minHeight="-Infinity">
        <ImageView image="${controller.image}"/>
      </StackPane>
    </ScrollPane>
  </center>

</BorderPane>

Controller.java:

import java.io.File;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Window;

public class Controller {

  private final ObjectProperty<Image> image = new SimpleObjectProperty<>(this, "image");
  public final void setImage(Image image) { this.image.set(image); }
  public final Image getImage() { return image.get(); }
  public final ObjectProperty<Image> imageProperty() { return image; }

  @FXML
  private void handleBrowse(ActionEvent event) {
    event.consume();

    Window window = ((Button) event.getSource()).getScene().getWindow();

    FileChooser chooser = new FileChooser();
    chooser
        .getExtensionFilters()
        .addAll(new ExtensionFilter("Image Files", "*.png", "*.jpg", "*.jpeg", "*.gif"));
    File file = chooser.showOpenDialog(window);
    if (file != null) {
      setImage(new Image(file.toURI().toString()));
    }
  }
}

Main.java

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

  @Override
  public void start(Stage primaryStage) throws IOException {
    Parent root = FXMLLoader.load(Main.class.getResource("App.fxml"));
    primaryStage.setScene(new Scene(root));
    primaryStage.show();
  }
}

此外,至少在发布到公共论坛时,请遵循Java naming conventions。变量/字段名称中的下划线(_)不是常规的,这会使其他Java程序员更难理解您的代码。


0
投票

您只能绑定到可以观察到的对象(即,当它更改其值时会触发通知的对象)。标准的Java引用不会执行此操作,因此,如果绑定到从get...()方法返回的常规值,则只要该值发生更改,就不会自动更新。

因此,如果您要完全使用FXML表达式绑定来完成此操作,则需要使用JavaFX属性模式,即。

class MyController implements Initializable {
    //..other stuff

    // you may not even need this if you use expression bindings uniquely:
    @FXML
    private ImageView imgview;

    private Image imageA;
    private Image imageB;

    public MyController(){
        imageA = new Image(this.getClass().getResourceAsStream("imageA.png"));
        imageB = new Image(this.getClass().getResourceAsStream("imageB.png"));
    }


    private final ObjectProperty<Image> image = new SimpleObjectProperty<>();

    public ObjectProperty<Image> imageProperty() {
        return image ;
    }

    public final Image getImage() {
        return imageProperty().get();
    }

    public final void setImage(Image image) {
        imageProperty().set(image);
    }

    public void initialize(URL url, ResourceBundle resourceBundle) {
        // ...
    }

    public void mouseEnter(MouseEvent mouseEvent){
        image.set(imageB);
    }

    public void mouseLeave(MouseEvent mouseEvent){
        image.set(imageA);
    }
}

然后

<ImageView fx:id="imgview" image="${controller.image}" onMouseEnter="#mouseEnter" onMouseExited="#mouseLeave"/>

将按预期工作。

或者,由于您的控制器(在以下情况下,它作为演示者的作用更大)具有对ImageView的引用,因此您可以执行:

<ImageView fx:id="imgview" onMouseEnter="#mouseEnter" onMouseExited="#mouseLeave"/>

class MyController implements Initializable {
    //..other stuff

    @FXML
    private ImageView imgview;

    private Image imageA;
    private Image imageB;

    public MyController(){
        imageA = new Image(this.getClass().getResourceAsStream("imageA.png"));
        imageB = new Image(this.getClass().getResourceAsStream("imageB.png"));
    }

    public void initialize(URL url, ResourceBundle resourceBundle) {
        imageView.setImage(imageA);
    }


    public void mouseEnter(MouseEvent mouseEvent){
        imgview.setImage(imageB);
    }

    public void mouseLeave(MouseEvent mouseEvent){
        imageview.setImage(imageA);
    }
}

最后,请注意,对于此特定功能,您可以使用CSS进行所有这些操作:

<ImageView fx:id="imgview" id="#myImage" />

并且在CSS文件中:

#myImage {
    -fx-image: url("@imageA.png");
}
#myImage:hover {
    -fx-image: url("@imageB.png");
}

并且不需要发布任何控制器代码(您可能需要在此处调整图像的路径,因为在这种情况下,它们将相对于CSS资源。

© www.soinside.com 2019 - 2024. All rights reserved.