如何在Java FX FXML应用程序中的模型和视图之间进行通信? [英] How to communicate between the Model and View in a Java FX FXML application?

查看:73
本文介绍了如何在Java FX FXML应用程序中的模型和视图之间进行通信?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明:我非常 JavaFX和Model-View-Controller设计原则的新手.

Disclaimer: I'm very new to JavaFX and Model-View-Controller design principles.

这是我的基本情况:

我有一个ScreenManager,它类似于教程中概述的那种.使用屏幕管理器,我使用屏幕按钮上的基本操作处理在多个FXML屏幕之间进行切换.

I have a ScreenManager similar to the one outlined in this tutorial. Using the screen manager, I switch between several FXML screens using basic action handling on the screen buttons.

问题是其中之一是带有地图的游戏屏幕.我将在此游戏屏幕上绘制一个基于图块的地图,因此我在FXML文件中使用了TilePane(如果有更好的方法将图块绘制到屏幕区域,请告诉我).

The problem is that one of these is a game screen with a map. I'll be drawing a tile-based map onto this game screen, so I went with a TilePane in my FXML file(if there's a better way to draw tiles to an area of a screen, please let me know).

以下是我要绘制到游戏屏幕的TilePane的代码:

Here's the code where I create the TilePane that I want to draw to the game screen:

public TilePane createRandomMap(boolean centeredRiver)
{

    Image atlas = new Image("resources/textures/tile_atlas.jpg");
    Random rand = new Random();
    int riverPos = 4, 
        tileHeight = (int)atlas.getHeight()/2, 
        tileWidth = (int)atlas.getWidth()/3;
    if(!centeredRiver)
        riverPos = rand.nextInt(10);
    for(int i = 0; i < 5; i++)
    {
        for(int j = 0; j < riverPos; j++)
        {
            int type = rand.nextInt(4);
            Tile tile = new Tile(tileTypes[type], i, j);
            tile.setImage(atlas);
            tile.setViewport(new Rectangle2D((type%3)*tileWidth, //minX
                    tileHeight-(type/3)*tileHeight, //minY
                    tileWidth, tileHeight)); // width, height
            tile.setFitHeight(tilePane.getHeight()/5); //fit to the size of the pane
            tile.setFitWidth(tilePane.getWidth()/9); //fit to the size of the pane
            tile.setPreserveRatio(true);
            tilesToAdd.add(tile); 
        }
        if(i == 2)
        {
            Tile tile = new Tile(tileTypes[5], i, riverPos);
            tile.setImage(atlas);
            tile.setViewport(new Rectangle2D(2*tileWidth, 0, 
                    tileWidth, tileHeight));
            tile.setFitHeight(tilePane.getHeight()/5);
            tile.setFitWidth(tilePane.getWidth()/9);
            tile.setPreserveRatio(true);
            tilesToAdd.add(tile);
        }
        else
        {
            Tile tile = new Tile(tileTypes[4], i, riverPos);
            tile.setImage(atlas);
            tile.setViewport(new Rectangle2D(tileWidth, 0, 
                    tileWidth, tileHeight));
            tile.setFitHeight(tilePane.getHeight()/5);
            tile.setFitWidth(tilePane.getWidth()/9);
            tile.setPreserveRatio(true);
            tilesToAdd.add(tile);
        }
        for(int j = riverPos + 1; j<9; j++)
        {
            int type = rand.nextInt(4);
            Tile tile = new Tile(tileTypes[type], i, j);
            tile.setImage(atlas);
            tile.setViewport(new Rectangle2D((type%3)*tileWidth, //minX
                    tileHeight-(type/3)*tileHeight, //minY
                    tileWidth, tileHeight)); // width, height
            tile.setFitHeight(tilePane.getHeight()/5); //fit to the size of the pane
            tile.setFitWidth(tilePane.getWidth()/9); //fit to the size of the pane
            tile.setPreserveRatio(true);
            tilesToAdd.add(tile); 
        }
    }
    tilePane.getChildren().addAll(tilesToAdd);

    return tilePane;
}

我已经能够在我的控制器类中访问此TilePane:

I've been able to access this TilePane in my controller class:

public class GameScreenController implements Initializable, ControlledScreen {

ScreenManager screenManager;

@FXML
TilePane mapPane

/**
 * Initializes the controller class.
 */
@Override
public void initialize(URL url, ResourceBundle rb) {
    TileEngine engine = new TileEngine();
    mapPane = engine.createRandomMap(true);
}    

在上述实例中,我将在FXML屏幕中定义的TilePane设置为在模型中创建的TilePane,但是出现以下错误:

In the above instance, I'm setting the TilePane defined in my FXML screen to the TilePane I created in my model, but I get the following error:

Can not set javafx.scene.layout.TilePane field
screens.gameScreen.GameScreenController.mapPane to 
javafx.scene.layout.AnchorPane

这是我的FXML文件中处理TilePane的部分:

Here's the segment of my FXML file dealing with the TilePane:

<TilePane id="MapPane" fx:id="mapPane" layoutX="0.0" layoutY="0.0" prefColumns="9" prefHeight="560.0" prefTileHeight="112.0" prefTileWidth="112.0" prefWidth="1108.0" visible="true" />

总体而言,我真的很努力地将注意力集中在JavaFX和游戏设计上,但是非常想学习.我将很乐于澄清和批评它的结构如何使其变得更好,甚至是与我要问的问题无关的事情.

I'm really struggling to wrap my head around JavaFX and game design in general, but very much want to learn. I'll be happy to clarify and to take criticism on any part of how this is structured in order to make it better, even things that don't pertain to the question I'm asking.

提前谢谢!

推荐答案

MVC

严格来说,JavaFX不支持MVC(无论如何它都是由发明人自己声明的),但是不支持某种MVVM(Model-View-ViewModel)

Strictly speaking JavaFX does not support MVC (which is dead anyways declared by the inventor himself) but some kind of MVVM (Model-View-ViewModel)

模型与控制器之间的通信

我通常使用依赖注入来做到这一点.如果您想要一个简单的FrameWork,我可以建议您 afterburner.fx 或Google Guice.两种轻量级框架都可以满足您的需求.基本上,他们可以为您处理类的实例化,并确保是否仅希望模型的一个实例处于活动状态.

I generally use Dependency Injection to do so. If you want a simple FrameWork I can suggest you afterburner.fx or Google Guice. Both pretty light weight frameworks which do what you want. Basically they can handle the instantiation of the classes for you and assure if wanted that only one instance of your model is active.

一些示例代码:

public class Presenter {
    // Injects
    @Inject private StateModel stateModel;
}

无需声明构造函数或任何其他内容.如果需要的话,您需要做更多的工作来测试它,但是总的来说,我不想再错过此功能,因为通常您只需要一个StateModel,其中所有信息都来自于此,所有视图仅用于侦听或更改它们.

No need for declaring Constructor or anything. You need a bit more work to test it if you want so, but overall I don't want to miss this feature again, because generally you just want one StateModel where all your information is derived from and all the views just listen to them or change them.

让我们举一个进一步的例子,以扩展到已发布的代码

Lets give a further example to extend to posted code

@Singleton
public class StateModel {
    // Properties
    private final StringProperty name = new SimpleStringProperty();

    // Property Getter
    public StringProperty nameProperty() { return this.name; }
}

如果现在在Presenter/Controller中声明了标签,我们可以像这样监听模型中的更改

If we have a label now declared in our Presenter/Controller, we can listen to the changes in our model like this

public class Presenter implements Initializable {
    // Nodes
    @FXML private Label nodeLabelName;

    // Injects
    @Inject private StateModel stateModel;

    @Override
    public void initialize(URL location, ResourceBundle resource) {
        this.nodeLabelName.textProperty().bind(this.stateModel.nameProperty());
    }
}

我可以给你的另一个建议是Threading.通常,您希望您的业务逻辑在另一个线程中执行,否则您的应用程序线程将在处理时开始滞后

Another suggestion I can give you is Threading. You generally want your Business Logic to be performed in another Thread else your Application-Thread will start lagging when it is processing

这篇关于如何在Java FX FXML应用程序中的模型和视图之间进行通信?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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