JavaFXでMerry Christmas!!

航空券予約サイト[skyticket(スカイチケット)](https://skyticket.jp/)を運営している株式会社アドベンチャーでございます:bow:


skyticketでは、各種デバイス（パソコン・スマートフォン・タブレット）から、旅行を中心とした予約に関わる様々なサービスを提供しています。

スマートフォン向けアプリも展開しています:iphone:
  - [App Store](https://geo.itunes.apple.com/jp/app/ge-hang-kong-quan-jian-suo/id995125257)
  - [Google Play](https://play.google.com/store/apps/details?id=com.adventure.skyticket)

<b>[skyticket:Android版アプリ]</b>
 <img width="200" alt="qiita-square" src="https://qiita-image-store.s3.amazonaws.com/0/315651/50b11e43-e16e-3853-1c3b-b0183eb03778.png">

<font color="Blue">ブルー</font>を基調としたとても爽やかなサイトですね:airplane:
玉城ティナちゃんもとっても素敵:relaxed::relaxed::relaxed:

# 1. さてなにつくろ
さてこの<font color="Blue">ブルー</font>基調のskyticket:airplane:
アドベントカレンダーネタということで、<font color="red">クリスマス</font>っぽい見た目にしよう思います:christmas_tree:

`JavaFX`を使って、skyticketのデスクトップアプリ:computer:を作ります。
とは言っても画面表示はwebviewです・・。
キラキラの見た目にするお:star::star::star:

# 2. 環境
#### 1. OpenJDK 11
2018年9月にリリースされた`Java11`使います。
https://jdk.java.net/11/

```
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)
```

#### 2. OpenJFX 11
`Java11`からOpenJDKに(OracleJDKにも)同梱されなくなったので別途用意します:computer:

https://openjfx.io/openjfx-docs/
↑環境設定、実行手順が易しく書かれています。`Maven` `Gradle`を利用した手順もあります:relaxed:

```
javafx.version=11.0.1
javafx.runtime.version=11.0.1+1
javafx.runtime.build=1
```

#### 3. Scene Builder 11
`gluon`の使います。XML手書きはきついの:older_man_tone1:
`Java11`向けのRC版出ていたのでコレを使います。
https://gluonhq.com/products/scene-builder/



```
Version 11.0.0
```

#### 4. IDE
`Eclipse`使います。
https://www.eclipse.org/downloads/

```
Eclipse IDE for Java Developers
Version: 2018-09 (4.9.0)
Build id: 20180917-1800
```

※あとEclipseプラグインも入れておきます。JavaFXプロジェクトのひな型作ってくれるので楽チン:ok_woman_tone1:

```
e(fx)clipse 3.4.1
```

# 3. つくってみた

#### 1. JavaFXのプロジェクト作成
`Eclipse`でJavaFXプロジェクト作ります。

<details><summary>手順はコチラ (:beach_umbrella:押下で手順が開閉します:beach_umbrella:)</summary><div>
　
・手順1. プロジェクト選択
　
`File` → `New Project...`
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/f37cc4d4-28e0-e3f1-7ae3-c58f67f915c5.png)
`Java FX Project` → **Next >**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/0f5bc8ba-05b8-0dd4-efd7-c21604686c2e.png)
<hr>
・手順2. プロジェクト名入力
![jjj.png](https://qiita-image-store.s3.amazonaws.com/0/315651/6751e8bb-f181-3cc5-22bf-6f2b14016a3a.png)
Project name:`application` → **Next >**
<hr>
・手順3. Java設定
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/71c19bc6-a2f3-d017-d891-6a1b5939d774.png)
ソノママ **Next >**
<hr>
・手順4. JavaFX設定
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/fd25680a-60d0-c279-4826-42a2414967a8.png)

以下の通り入力して **Finish**

|Project設定|設定値|
|:------------------|:------------------|
|Application type|Desktop|
|Package Name|jp.skytikcet|
|Declarative UI|-|
|├Language|FXML|
|├Root-Type|javafx.scene.layout.AnchorPane|
|├File Name|Skyticket|
|└Controller Name|SkyticketController|

<hr>
</div></details>

・JavaFXプロジェクト作成後のディレクトリ・ファイルの構成

```
application
│  .classpath
│  .project
│  build.fxbuild
│
├─.settings
│      org.eclipse.jdt.core.prefs
│
├─bin
│  └─jp
│      └─skyticket
│              Main.class
│              SkyticketController.class
│              Skyticket.fxml
│              application.css
│
└─src
    └─jp
        └─skyticket
                Main.java                  
                SkyticketController.java   
                Skyticket.fxml             
                application.css            
```
#### 2. FXML修正
　
Scene Builder(GUI)使って`Skyticket.fxml`のレイアウト修正をしていきます。
コントローラで操作するために`fx:id`(フィールドと対応)と`On Action`(メソッドと対応)も併せて設定します。
↓目指すレイアウト構造はこんなん。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/860442a0-9d42-37a4-a744-5c14ee26faab.png)

<details><summary>手順はコチラ (:beach_umbrella:押下で手順が開閉します:beach_umbrella:)</summary><div>
　
・手順1. ScreenBuilder起動

Skyticket.fxmlを選択して右クリックからの`Open with ScreenBuilder`でScreenBuilderを起動します。
（ScreenBuilderが起動しない場合は、`Window` → `Preferences` → `JavaFX`から「SceneBuilder.exe」までのパス設定をすることで機能するようになります。）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/ced4b359-361e-4a2b-bbf5-90334e8247b5.png)


デフォルトはこのような状態です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/17184915-16d4-e458-ec4a-6322f8fa9f33.png)
<hr>

・ 手順2. AnchorPaneレイアウト
まずは`AnchorPane`から。幅と高さを設定します。
デフォルトではリージョンの最小(USE_COMPUTED_SIZE)が設定されているので、これを書き換えます。
スマホっぽいサイズ設定にしました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/441349ef-bf94-ab8d-72cb-02cbd243aca0.png)

そして`fx:id`。コントローラ側でコンポーネントにアクセスするためのidを設定します。
（コントローラからコンポーネントを操作しない場合は設定の必要ありません。）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/85a19290-fb93-915e-0a6e-adcbf2f7b870.png)

※FXMLとコントローラとの紐付け`fx:controller`はデフォルトで設定済みです。
<hr>

・ 手順3.webViewレイアウト
続いて`webView`
左のControlsからドラッグ＆ドロップで配置します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/ad648777-9ad7-5c79-4b3b-d6eb37deea2c.png)

`WebView`についても幅と高さを与え、`fx:id`を設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/0f82a309-1297-ee8e-a816-5987a6aac0b1.png)
<hr>
・ 手順4.buttonレイアウト
同様に左のControlsからドラッグ＆ドロップで配置します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/297280ec-cd7c-fc5d-164d-50345ae48b06.png)

同様に幅と高さを与え、`fx:id`を設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/f18337e9-dd0d-8824-2033-083d96e17143.png)

加えて`On Action`にボタン押下時のイベントハンドラ(メソッド名)を設定します。

残りのボタンも同様に設定します。（雑:older_man_tone1:
<hr>

・ 最終的なレイアウト
![image.png](https://qiita-image-store.s3.amazonaws.com/0/315651/22a8d392-4577-598e-f4e7-2f0b5bcedcf2.png)


</div></details>



・最終的なFXML

```Skyticket.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.web.WebView?>

<AnchorPane fx:id="ap" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="812.0" prefWidth="375.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jp.skyticket.SkyticketController">
   <children>
      <WebView fx:id="webView" layoutX="0" layoutY="34.0" prefHeight="802.0" prefWidth="365.0" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="5.0" />
      <Button text="よやく" fx:id="btnToppage" onAction="#toppage" layoutX="0" layoutY="712.0" prefHeight="100.0" prefWidth="125.0" mnemonicParsing="false" />
      <Button text="マイページ" fx:id="btnMypage" onAction="#mypage" layoutX="125.0" layoutY="712.0" prefHeight="100.0" prefWidth="125.0" mnemonicParsing="false"  />
      <Button text="押しちゃダメ" fx:id="btnSetting" onAction="#setting" layoutX="250.0" layoutY="712.0" prefHeight="100.0" prefWidth="125.0" mnemonicParsing="false"  />
   </children>
</AnchorPane>
```
ここまでレイアウト作成です。

#### 3. CSS修正
続いてCSSを修正します。

```application.css
/* 通常スタイル */
.button {
  -fx-border-color: white;
  -fx-background-color: #0896ff;
}
/* クリスマス用スタイル */
.button.christmas {
  -fx-background-color: #b79e00;
  -fx-font-family: 'ふい字'
}
```
あとwebviewの方の装飾用に`skyticket.css`を新規作成します。

#### 4. メイン、コントローラクラスの修正
続いてJavaファイル(主にコントローラ)を修正します。

まずはメインクラス。JavaFXのエントリポイントですね。
ほぼデフォルトままで、シーンのサイズ修正とタイトル設定しただけです。

```Main.java
package jp.skyticket;
	
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.fxml.FXMLLoader;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
        	AnchorPane root = FXMLLoader.load(getClass().getResource("Skyticket.fxml"));
            Scene scene = new Scene(root, 375, 812); // サイズを修正
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setTitle("Merry Christmas!!"); // タイトル設定を追加
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
```

続いてコントローラクラス。
フィールド(画面コンポーネント)とメソッド(イベント)を記述します。
<font color="Blue">@FXML</font>アノテーションを付けることでFXMLと関連付き、コントローラ側で操作可能になります。
（しかしFXMLからコントローラのソース自動生成出来ないのかなぁ:santa_tone2:


```SkyticketController.java
package jp.skyticket;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;

public class SkyticketController  {
        // 画面コンポーネント
        @FXML private AnchorPane ap;
        @FXML private WebView webView;
        @FXML private Button btnMypage;
        @FXML private Button btnToppage;
        @FXML private Button btnSetting;

        private WebEngine webEngine;

        /**
         * 初期化
         * ・UAを設定してトップ画面を描画します。
         * 
         * @param event
         */
	    @FXML
	    private void initialize() {   
	    	this.webEngine = this.webView.getEngine();
	    	// スマートフォン用ページ表示したいのでUA設定
	    	this.webEngine.setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) CriOS/61.0.3163.73 Mobile/15A372 Safari/602.1");
	    	this.webEngine.load("https://skyticket.jp");
	    }
        /**
         * btnMypage押下時のイベントハンドラ
         * ・マイページを描画します。
         * 
         * @param event
         */
	    @FXML
	    private void mypage(ActionEvent event) {
	    	this.webEngine.load("https://skyticket.jp/user/");
	    }
        /**
         * btnToppage押下時のイベントハンドラ
         * ・トップ画面を描画します。
         * 
         * @param event
         */
	    @FXML
	    private void toppage(ActionEvent event) {
	    	this.webEngine.load("https://skyticket.jp");
	    }
        /**
         * btnSetting押下時のイベントハンドラ
         * ・ボタンに装飾用クラスを追加します。
         * ・クリスマス用CSSを適用します。
         * ・トップ画面を描画します。
         * 
         * @param event
         */
	    @FXML
	    private void setting(ActionEvent event) {
	    	// 各種ボタンにクリスマス装飾スタイル用のクラスを設定   	
	    	this.btnMypage.getStyleClass().add("christmas");
	    	this.btnToppage.getStyleClass().add("christmas");
	    	this.btnSetting.getStyleClass().add("christmas");

	    	// webviewのクリスマス用CSS適用
	    	this.webEngine.setUserStyleSheetLocation(getClass().getResource("skyticket.css").toString());
	    	
	    	this.webEngine.load("https://skyticket.jp");
	    }
}
```

以上で作成完了でございます。


# 4.うごかしてみた
まずは初期表示。
普通にwebview機能しています。
 <img width="300" alt="qiita-square" src="https://qiita-image-store.s3.amazonaws.com/0/315651/62d96901-9acc-883f-901e-50210ecafbb2.png">



下部のボタン`「よやく」` ボタン、`「マイページ」`ボタンもいい感じ。
普通にチケット申し込みも可能です:airplane:
 <img width="300" alt="qiita-square" src="https://qiita-image-store.s3.amazonaws.com/0/315651/7df4f344-af3b-22eb-4c92-fe4433e10893.png">

最後に`「押しちゃダメ」`ボタン
 <img width="300" alt="qiita-square" src="https://qiita-image-store.s3.amazonaws.com/0/315651/5ac18958-f960-ed24-3955-b485723beeb2.png">


　
ぜったいに・・・

押しちゃダメ・・・
　
　
　
サンタさんとの  

おやくそく
　
　
　
ぜったいに・・・
　

<details open>
  <summary><font color="#b79e00"><b>:no_good:ココぜったいに押しちゃにダメだお:no_good_tone3:</b></font></summary>
  <div>
　
:christmas_tree::christmas_tree::christmas_tree:Merry Christmas:christmas_tree::christmas_tree::christmas_tree:
 <img width="450" alt="qiita-square" src="https://qiita-image-store.s3.amazonaws.com/0/315651/72a6a937-54b5-5e6a-b53b-6c5a2e99a343.png">


出演男優はアドベンの`はるたん`こと、春木部長です。
おっさんずラブ:heart_eyes::heart_eyes::heart_eyes:

それでは皆さん、良いクリスマスを～:christmas_tree::santa_tone2::christmas_tree:

以上でございます！
　</div>
</details>