JavaFX 画面サイズが一定以下の場合はスクロールバーを出し、一定以上の場合は中のnodeをなるべく最大化する


## 今回のゴール
JavaFxをしようしてGUIアプリを作成する際にListViewを使用しましたが、以下のような要望がありました。

1.  ListViewはなるべく大きく表示したい
    -  → windowの大きさに連動して大きさが自動的に変化する
1. windowサイズがある一定を下回った場合は、ListViewの大きさ変更をやめ、かつ、windowのスクロール表示にしたい。

そんな要望を満たすのに、それなりに苦労したので結果のメモ。

最終的に目指した動きはこんな感じ。
青枠部分がListViewで、ボタンのある位置よりも画面サイズが小さくなった場合にスクロールバーが登場し、横に部品があることを示す、といった具合。
![目指す姿fxml.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222220/350adcde-da43-76c4-3f64-ee05f8d8456e.gif)

## 環境
java1.8
JavaFX Scene Builder 2.0

## まずは結果から
最終的な部品の構成は以下の通りで実現できた。

- ScrollPane
    - AnchorPane
        - TextField
        - Button
        - ListView

![スクリーンショット 2020-07-09 13.20.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222220/41f0ac46-8272-70ad-925c-33c225159b85.png)

### 各部品の設定要点
#### ScrollPane
- ```Properties``` - ```Hbar/Vbar Policy```を```AS_NEEDED```とする。
- ```Layout``` - ```Fit To Width/Height```をtrue(:heavy_check_mark:)とする。

#### AnchorPane
- ```Layout``` - ```Min Width/Height```を、スクロールバーの表示を開始させたいwindowサイズに設定する。


#### ListView
- ```Layout``` - ```Anchor Pane Constrains```の４辺について全て固定値として設定する。

## 実現までの道のりステップバイステップ
### Windowサイズに連動するListViewの設定
以下の組み合わせにより、「Windowサイズに連動してListViewの大きさが変化する」を実現できる。
#### AnchorPaneとListView
使用するのは```AnchorPane```。
```AnchorPane```とは
>AnchorPane allows the edges of child nodes to be anchored to an offset from the anchor pane's edges. 

中に含まれたnodeについて、親であるAnchorPaneの端からの距離を固定することができる、とのこと。

SceneBuliderでAnchorPaneの子要素にControlを追加すると、Layoutに```Anchor Pane Constraints```が表示される。
![スクリーンショット 2020-07-09 12.58.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222220/8e8d6df4-327b-3394-2973-914790303314.png)

この４つのテキストボックスに入力した値（距離）で個要素の端とAnchorPaneの端とが固定される。
下の値のみ固定したのがこれ。
![下のみ固定.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222220/f1ec4684-de07-18d6-f275-8cb2937fb3b8.gif)

一辺のみを固定すると、コントロールの大きさは変更されず、表示位置が変更される。

上下を固定したのがこれ。
![上下固定.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222220/6dd4ffc7-1445-2838-87fd-fd0cbe4fb59b.gif)

上下を固定すると、コントロールの大きさよりも上下からの距離が優先され、コントロールの大きさが伸び縮みするのがわかる。


今回、ListViewの大きさをなるべく大きく表示させるためには、AnchorPaneに内包させるListViewについて、```Anchor Pane Constraints```の4つの辺全てについて、固定値とすれば良いことがわかった。


#### ScrollPane内のAnchorPane
ScrollPaneには```FitTo[Width|Height]Property```がある。
SceneBuilderではこのプロパティを
```Layout```の```Fit To Width```と```Fit To Height```で設定する。

この```Fit To Width/Height```の意味をドキュメントで確認すると
>public final BooleanProperty fitToWidthProperty

>If true and if the contained node is a Resizable, then the node will be kept resized to match the width of the ScrollPane's viewport. If the contained node is not a Resizable, this value is ignored.

（適当な訳）
>trueのとき、中に含まれるnodeがRisizableであれば、nodeはScrollPaneのviewportの幅に連動して大きさが変化する。含まれるnodeがResizableでなければこの値は無視される。

ということで、ScrollPaneの中にAnchorPaneを入れ、```Fit To Width/Height```を```true```にすることで、ScrollPane（今回の場合はWindowサイズ）の大きさを変化に連動してAnchorPaneの大きさが変化するようになった。
（AnchorPaneは、というか、[JavaFXのコントロールは全てResizeable](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/Control.html#isResizable--))

### Windowの大きさが一定以下だった場合の措置
次は「一定の幅よりもWindowの大きさが小さくなった場合は、スクロールバーを表示する」。

ScrollPaneの`FitTo[Width|Height]Property`を```true```とした場合、内部のnodeの大きさはScrollPaneの大きさに連動して変化するが、内部のnodeの```Min [Width|Height]```・```Max [Width|Height]```を超えては大きさは変化しない様子。

そこで、ScrollPaneの中のAnchorPaneの```Min Width```に、```Button```が隠れない大きさを設定すると、、、できた。


## 結果として出来上がったfxml
```目指す姿.fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<ScrollPane fitToHeight="true" fitToWidth="true" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
  <content>
    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="343.0" prefWidth="486.0">
         <children>
            <TextField layoutX="14.0" layoutY="7.0" prefHeight="27.0" prefWidth="247.0" />
            <ListView layoutX="-4.0" layoutY="41.0" prefHeight="357.0" prefWidth="598.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="40.0" />
            <Button layoutX="272.0" layoutY="7.0" mnemonicParsing="false" text="Button" />
         </children></AnchorPane>
  </content>
</ScrollPane>
```