今回のゴール
JavaFxをしようしてGUIアプリを作成する際にListViewを使用しましたが、以下のような要望がありました。
- ListViewはなるべく大きく表示したい
- → windowの大きさに連動して大きさが自動的に変化する
- windowサイズがある一定を下回った場合は、ListViewの大きさ変更をやめ、かつ、windowのスクロール表示にしたい。
そんな要望を満たすのに、それなりに苦労したので結果のメモ。
最終的に目指した動きはこんな感じ。
青枠部分がListViewで、ボタンのある位置よりも画面サイズが小さくなった場合にスクロールバーが登場し、横に部品があることを示す、といった具合。
環境
java1.8
JavaFX Scene Builder 2.0
まずは結果から
最終的な部品の構成は以下の通りで実現できた。
- ScrollPane
- AnchorPane
- TextField
- Button
- ListView
- AnchorPane
各部品の設定要点
ScrollPane
-
Properties
-Hbar/Vbar Policy
をAS_NEEDED
とする。 -
Layout
-Fit To Width/Height
をtrue()とする。
AnchorPane
-
Layout
-Min Width/Height
を、スクロールバーの表示を開始させたいwindowサイズに設定する。
ListView
-
Layout
-Anchor Pane Constrains
の4辺について全て固定値として設定する。
実現までの道のりステップバイステップ
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
が表示される。
この4つのテキストボックスに入力した値(距離)で個要素の端とAnchorPaneの端とが固定される。
下の値のみ固定したのがこれ。
一辺のみを固定すると、コントロールの大きさは変更されず、表示位置が変更される。
上下を固定すると、コントロールの大きさよりも上下からの距離が優先され、コントロールの大きさが伸び縮みするのがわかる。
今回、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)
Windowの大きさが一定以下だった場合の措置
次は「一定の幅よりもWindowの大きさが小さくなった場合は、スクロールバーを表示する」。
ScrollPaneのFitTo[Width|Height]Property
をtrue
とした場合、内部のnodeの大きさはScrollPaneの大きさに連動して変化するが、内部のnodeのMin [Width|Height]
・Max [Width|Height]
を超えては大きさは変化しない様子。
そこで、ScrollPaneの中のAnchorPaneのMin Width
に、Button
が隠れない大きさを設定すると、、、できた。
結果として出来上がった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>