首页 文章

绑定到GraphicPropety的JavaFX ImageView未显示在多个视图上

提问于
浏览
0

这个问题 Build 在我之前提出的一个问题上:JavaFX Sync Duplicate Views to the Same Controller (FXML & MVC)

但涉及一个单独的问题 . 我正在尝试创建一个播放/暂停按钮,只要按下按钮到相应的>播放和||,它就会交换图形 . 暂停图像

ButtonModel.java

public class ButtonModel {
    private final ObjectProperty<ImageView> playImage = new SimpleObjectProperty<ImageView>();
    private boolean paused;

    public final ObjectProperty<ImageView> playImageProperty() {
        return this.playImage;
    }

    //... Other Getters and Setters...
}

ButtonPanelController.java

public class ButtonPanelController implements Initializable {
    @FXML
    Button myButton

    ButtonModel model

    public ButtonPanelController(ButtonModel model) {
        this.model = model;
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        model.setPaused(true);
        myButton.graphicProperty().bind(model.playImageProperty());
    }

    public void buttonClickedAction(ActionEvent e) {
        Image image = null;
        if(model.isPaused()) {
            image = new Image(this.getClass().getResourceAsStream("play.png"));
        } else {
            image = new Image(this.getClass().getResourceAsStream("pause.png"));

        }
        model.setPlayImage(new ImageView(image));
        model.togglePaused();
    }
}

我使用相同的.fxml创建两个场景,并将我的模型的单个实例传递给两个控制器以控制状态 .

我遇到的问题是,当我更改ImageView属性时,图像仅出现在我的第二阶段的按钮上 . 我的第一个舞台上的按钮正在调整大小以适应图像的样子,当我放置断点并查看第一个按钮的“图形”属性时,我可以看到它的'observable'属性设置为我的ObjectProperty参考,但是图像本身不会显示在按钮上 . 第二阶段的按钮按预期工作,但第一阶段的按钮不是 .

我已经能够使用相同的架构成功绑定RadioButton的SelectedProperty,所以我不确定为什么Button的graphicProperty有问题 .

1 回答

  • 2

    这里的概念问题是您使用模型来存储UI组件(例如 ImageView ) . 模型应该存储应用程序的状态,而不是应用程序的视图 .

    它实际上不起作用的技术原因是节点只能出现在一个场景中,而在任何场景图中最多只出现一次 . 由于您在两个控制器视图对之间共享模型,因此您尝试在每个视图中显示相同的 ImageView 实例,这是不允许的 . (例如,由于您只有一个 ImageView 实例,您期望通过 playImage.get().getScene()playImage.get().getLayoutX() 等方法调用返回什么?)

    您的模型应该只存储状态(即数据):

    public class ButtonModel {
        private final BooleanProperty paused = new SimpleBooleanProperty();
    
        public final BooleanProperty pausedProperty() {
            return paused ;
        }
    
        public final boolean isPaused() {
            return pausedProperty().get();
        }
    
        public final void setPaused(boolean paused) {
            pausedProperty().set(paused);
        }
    
        public void togglePaused() {
            setPaused(! isPaused());
        }
    
        //... Other Getters and Setters...
    }
    

    并且您的控制器应该关注基于模型中的数据更新自己的视图(以及响应用户输入更新模型):

    public class ButtonPanelController implements Initializable {
    
        // We can use a single instance of each image (which is not a node)
        // and share it between 
        // multiple image views, so we can make the images static 
        // to conserve memory. Note that fairly spectacularly bad things
        // will happen if there is an exception thrown loading these images...
    
        private static Image playImage = 
            new Image(ButtonPanelController.class.getResource("play.png").toExternalForm());
        private static Image pauseImage = 
            new Image(ButtonPanelController.class.getResource("pause.png").toExternalForm());
    
        // But each controller *must* have its own imageview to display in
        // the button in its view:
    
        private ImageView buttonGraphic ;
    
        @FXML
        Button myButton
    
        ButtonModel model
    
        public ButtonPanelController(ButtonModel model) {
            this.model = model;
        }
    
        @Override
        public void initialize(URL location, ResourceBundle resources) {
            buttonGraphic = new ImageView();
            myButton.setGraphic(buttonGraphic);
            model.setPaused(true);
            buttonGraphic.imageProperty().bind(Bindings
                .when(model.pausedProperty())
                .then(playImage)
                .otherwise(pauseImage));
        }
    
        public void buttonClickedAction(ActionEvent e) {
            model.togglePaused();
        }
    }
    

    请注意,您可以在FXML中定义 ImageView 并将其注入控制器:

    <Button fx:id="myButton" onAction="#handleButtonAction">
        <graphic>
            <ImageView fx:id="buttonGraphic"/>
        </graphic>
    </Button>
    

    然后当然摆脱了 initialize 方法的前两行,并使用 @FXML 注释 buttonGraphic 字段 . (主要思想基本相同 . )

相关问题