Изображение привязки JavaFx ImageView через привязки выражений f xml - PullRequest
0 голосов
/ 29 апреля 2020

Я знаю, что есть много обучающих программ по javafx. Но 99% из них не дают хороших вещей xml. В f xml мы можем выполнить привязку данных или «привязку выражений», как она вызывается в javafx.

Привязка Image к 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;
    }
}

Файл f xml

<?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. Но всего несколько обложек для xml. Большинство людей используют

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

Но я подумал, что F XML был введен, чтобы избавиться от всего этого кода, как в WPF / XAML. Длинный текст короткого вопроса. Как привязать изображение к ImageView, чтобы при изменении изображения в ImageView отображалось новое изображение?

С уважением, Боб.

Ответы [ 2 ]

1 голос
/ 29 апреля 2020

Вы можете привязать только то, что можно наблюдать (то есть то, что запускает уведомления, когда оно меняет свое значение). Стандартная ссылка Java этого не сделает, поэтому простая привязка к обычному значению, возвращаемому методом get...(), не будет автоматически обновляться при изменении этого значения.

Так что если вы хотите сделать это полностью с помощью F XML привязок выражений, поэтому необходимо будет использовать шаблон свойства 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="#my-image" />

и в файле CSS:

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

, и ни один из опубликованных кодов контроллера не является необходимо (вам может понадобиться настроить пути к изображению здесь, так как они будут относительно ресурса CSS в этом случае).

1 голос
/ 29 апреля 2020

TL; DR : чтобы сделать привязку в файле F XML, необходимо сделать свойство изображения видимым (например, через ObjectProperty<Image>).


В F XML нет ничего особенного. Он просто определяет способ описания графа объектов в синтаксисе XML, который интерпретируется FXMLLoader и преобразуется в обычные Java объекты. Он использует относительно базовые c правила и соглашения Java (FX) Bean для выполнения sh. Дополнительную информацию можно найти в документе Введение в F XML.

Когда вы используете привязки выражений, вы используете API привязки интерфейса JavaFX Property. Другими словами, когда у вас есть:

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

И вот откуда возникает проблема в вашем примере. Ваш контроллер не предоставляет наблюдаемое свойство изображения; у вас есть только метод getImage(). Поскольку ничего не наблюдается, ImageView не может автоматически обновляться при изменении Image (т. Е. Не к чему привязываться).

Вот пример, где контроллер предоставляет ObjectProperty<Image>.

App.f xml:

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

Контроллер. 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 соглашениям об именах , по крайней мере, при публикации на форуме c. Эти подчеркивания (_) в именах переменных / полей не являются традиционными, и другим Java программистам становится сложнее понять ваш код.

...