具有多个视图和控制器的Scala和JavaFX的“真实” MVC模式

时间:2018-08-27 19:06:16

标签: java scala javafx model-view-controller

我正在构建一个scala桌面应用程序,要求是:

  • Java 8 (不是9,不是10,不是7)
  • 使用Scala(最终使用Java)
  • MVC模式(视图和控制器必须是“平台”独立的,我的老师已经有一个正常的控制台和Swing视图,但是什么也没告诉我们)
  • View实现必须在JavaFX上

我不能使用ScalaFX,Swing(Java)或Swing(Scala)。

在主要的主要方面,从现在开始MyAwesomeApp我做了:

class MyAwesomeApp extends Application {
  override def start(primaryStage: Stage) {
    // Stuff here
  }
}

object MyAwesomeApp extends App {
  Application.launch(classOf[MyAwesomeApp], args: _*)
}

现在选择三个通用视图:MainMenuSettingsMenuNewGameMenuGamePanel。 可以想象,在开始时,应用程序将以MainMenu启动,其中有两个按钮可以带到NewGameMenuSettingsMenu。然后在NewGameMenu中将有一个按钮,将我带到GamePanel

为此,我将有一个控制器:MainMenuController,SettingsMenuController,NewGameMenuController,GamePanelController。但是此控制器与平台无关:换句话说,没有JavaFX控制器。为什么?因为JavaFX控制器可以“查看”视图的所有组件,例如TextBoxes,Button等,并且可以仅使用@FXML标签“加载”它们。这不是我的情况,因为我必须是“独立于视图”的,所以我考虑了一个包装器和一个观察者模式。

我想到了FXControllers(JavaFX控制器)和Observers(真实的MVC控制器)。每个FXController将具有一个实现其特征(= interface)的Observer。这是一些代码:

这是加载fxml的帮助对象

object ViewLoader {
  def loadView(controller: GenericFXController[_], layoutPath: String): Pane = {
    val loader = new FXMLLoader(getClass.getResource("/layouts/" + layoutPath))
    loader.setController(controller)
    loader.load()
  }
}

一个通用的FXController将会有这个(我使用了通用类型“ A”,因为我知道每个FXController都会有一个观察者,但是我不知道哪个):

abstract class GenericFXController[A] (private val path: String) {
  protected var observer: A = _
  protected val layout: Pane = ViewLoader.loadView(this, path)

  def setObserver(observer: A): Unit = {
    this.observer = observer
  }

  def getLayout: Pane = layout
  def getPaneType: SceneType.Value = paneType
}

最后是为MainMenu定义的FXController:

class MainMenuPaneFXController extends GenericFXController[MainMenuObserver](path = "MainMenu.fxml") {
  @FXML protected var startNewGameBtn: Button = _
  @FXML protected var settingsBtn: Button = _

  this.startNewGameBtn.setOnMouseClicked(_ => {
    this.observer.onStartNewGameBtnPressed()
  })
  this.settingsBtn.setOnMouseClicked(_ => {
    this.observer.onSettingsPressed()
  })
}

trait MainMenuObserver {
  def onStartNewGameBtnPressed()
  def onSettingsPressed()
}

现在简化了控制器:

class MainMenuObserverImpl extends MainMenuObserver {
  override def onStartNewGameBtnPressed(): Unit = println("Start pressed")
  override def onSettingsPressed(): Unit = println("Settings pressed")
}

我有一个视图包装器,将JavaFX隐藏在trat和class后面。

trait GenericView {
  def showView(): Unit
  def closeView(): Unit
  def isShowing: Boolean

  def setScene(sceneType: SceneType)
}

SceneType是简单的“枚举”,具有类似“ MainMenu”,“ SettingsMenu”等的声音。

在主要方法中,我启动视图

   val view: GenericView = new ViewFX(primaryStage)

...现在如何将“观察者”传递给“ FXControllers”?

我可以向GenerView特性添加方法,但必须保持“平台无关”,因此我无法返回JavaFX Scene或Swing Button。该视图无法确定控制器类:应该具有一个“ .setObserver(),并且FXControllers在某种程度上是该视图的一部分。

是否应该在ViewFX中创建每个场景,并在创建时为每个人设置控制器?如何将视图传递给控制器​​(以及用于更改场景的GenericView)?

我的应用以前很简单:一个屏幕,一个控制器。在这种情况下,我该如何优雅地进行而不会“作弊”?

0 个答案:

没有答案