読者です 読者をやめる 読者になる 読者になる

JavaFX のイニシャライズ処理を Google guice で整理

前回に引き続き、JavaFX のイニシャライズ処理を Google guice で整理してみる。
起動のメインクラス・・・共通イニシャライズとして、guice の Module を配置、
最初に起動されるコントローラ・・・次画面のイニシャライズとして Module を配置、
  →画面遷移のパラメータ転送に相当する。

メインクラス

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.name.Names;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.util.Callback;

public class MainApp extends Application{
   static String WINDOW_TITLE  = "fxtest";
   private Stage stage;

   public static void main(String[] args){
      launch(args);
   }
   private Module commonModule = new AbstractModule(){
      @Override
      protected void configure(){
         binder().bind(String.class).annotatedWith(Names.named("TITLE")).toInstance("fxtest");
      }
   };
   /* @see javafx.application.Application#start(javafx.stage.Stage) */
   @Override
   public void start(Stage primaryStage) throws Exception{
      stage = primaryStage;
      setPage(StartPage.class, "start.fxml");
   }

   public void setPage(final Class<?> cls, String fxml, Module...modules) throws Exception{
      Injector injector = modules.length==0 ? Guice.createInjector(commonModule)
             : Guice.createInjector(commonModule).createChildInjector(modules);
      FXMLLoader loader = new FXMLLoader(cls.getResource(fxml));
      loader.setControllerFactory(p->injector.getInstance(cls));
      Scene scene = new Scene((Parent)loader.load());
      BasePage page = (BasePage)loader.getController();
      page.setApp(this);
      stage.setTitle(WINDOW_TITLE);
      stage.setScene(scene);
      stage.show();
   }
}

javafx.application.Application の start で、画面遷移としての共通メソッド setPage を呼ぶ。
メインクラスで実行する場合や、Module を指定しない画面遷移を判定して→Module...modules の length  
Guice の createInjector 、さらに、createChildInjector で生成する Injector で
FXMLLoader のファクトリを用意する。
このファクトリの指定は、ラムダで書ける。

loader.setControllerFactory(new Callback<Class<?>, Object>(){
   @Override
   public Object call(Class<?> type){
      return injector.getInstance(cls);
   }
});

と書くところを、javafx.util.Callback が、@FunctionalInterfaceであるので、

loader.setControllerFactory(p->injector.getInstance(cls));

と書ける。



start.fxml の コントローラ

import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
import javafx.fxml.FXML;

public class StartPage extends BasePage{
   @FXML
   @Override
   public void initialize(){
      super.initialize();
   }
   public void nextPage(){
      setPage(AlphaPage.class, "alpha.fxml", new AbstractModule() {
         @Override
         protected void configure() {
            binder().bind(String.class).annotatedWith(Names.named("STATUS")).toInstance("あいう");
            binder().bind(String.class).annotatedWith(Names.named("BUTTON")).toInstance("送信");
         }
      });
   }
}

共通イニシャライズとメインクラスインスタンスを参照するための BasePage を継承している。
次画面 alpha.fxml を

import javax.inject.Inject;
import javax.inject.Named;
import com.google.inject.Module;
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public abstract class BasePage{
   @Inject @Named("TITLE") private String titlename;
   private MainApp application;

   @FXML private Label title;

   @FXML
   public void initialize(){
      title.setText(titlename);
   }
   public void setApp(MainApp application){
      this.application = application;
   }
   public void setPage(Class<? extends BasePage> cls, String fxml, Module...modules){
      try{
         application.setPage(cls, fxml, modules);
      }catch(Exception e){
         e.printStackTrace();
         throw new RuntimeException(e);
      }
   }
}

共通イニシャライズ  @FXML public void initialize() が存在する。
画面遷移の setPage メソッドが存在する。

import javax.inject.Inject;
import javax.inject.Named;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;

public class AlphaPage extends BasePage{
   @Inject @Named("STATUS") String statusValue;
   @Inject @Named("BUTTON") String buttonName;

   @FXML private Label status;
   @FXML private Button btn1;

   @FXML
   @Override
   public void initialize(){
      super.initialize();
      status.setText(statusValue);
      btn1.setText(buttonName);
   }
   public void backPage(){
      setPage(StartPage.class, "start.fxml");
   }
}

initialize() をオーバライドして、共通とこの画面専用のイニシャライズを行う。
backPage() メソッドで元の画面を呼び出すように設定する。