Skip to content

Instantly share code, notes, and snippets.

@seraphy
Last active April 22, 2019 01:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seraphy/b00f761b39bac7815bd96548fd4e459e to your computer and use it in GitHub Desktop.
Save seraphy/b00f761b39bac7815bd96548fd4e459e to your computer and use it in GitHub Desktop.
JavaFXでTitledPaneを縦分割するSplitePaneをTitledPaneの開閉に伴って自動的に均等リサイズする実装例。
package jp.seraphyware.example.lava8learn.jfx;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* TitledPaneの開閉イベントにより、SplitePaneの分割を自動的に調整する。
* SplitePaneは縦分割されているもののみ受け入れ可能である。
* また、TitledPaneはAnimationされていないものが推奨される。
*/
class TitledPaneStackingCoordinator {
private final SplitPane container;
private InvalidationListener expandChangeListener = obs -> {
Platform.runLater(() -> {
doLayout();
});
};
public TitledPaneStackingCoordinator(SplitPane container) {
this.container = Objects.requireNonNull(container);
if (container.getOrientation() != Orientation.VERTICAL) {
throw new IllegalArgumentException("縦方向の分割のみサポートしています");
}
}
public void doLayout() {
double totalHeight = 0;
List<Node> nodes = container.getItems();
double[] heights = new double[nodes.size()];
for (int idx = 0; idx < nodes.size(); idx++) {
Node node = nodes.get(idx);
double height;
if (node instanceof TitledPane) {
TitledPane tp = (TitledPane) node;
height = node.prefHeight(-1);
if (!tp.isExpanded()) {
// minimizeしている場合は推奨サイズ
((TitledPane) node).setMaxHeight(height);
} else {
// expandedしている場合、推奨サイズか現在サイズの大きい方を採用
height = Math.max(tp.getHeight(), height);
((TitledPane) node).setMaxHeight(Double.MAX_VALUE);
}
} else if (node instanceof Region) {
height = ((Region) node).getHeight();
} else {
height = node.prefHeight(-1);
}
totalHeight += height;
heights[idx] = totalHeight;
System.out.println(node + "=" + height);
}
for (int idx = 0; idx < nodes.size() - 1; idx++) {
double height = heights[idx];
double pert = height / totalHeight;
System.out.println(idx + "=" + pert);
container.getDividers().get(idx).setPosition(pert);
}
}
private HashMap<TitledPane, ContentWatcher> watchingContents = new HashMap<>();
private class ContentWatcher implements ChangeListener<Node> {
private TitledPane parent;
private Node content;
public ContentWatcher(TitledPane parent) {
this.parent = Objects.requireNonNull(parent);
parent.contentProperty().addListener(this);
watchContentVisible(parent.getContent());
}
public void unwatch() {
parent.contentProperty().removeListener(this);
}
public void unwatchContentVisible() {
if (content != null) {
content.visibleProperty().removeListener(expandChangeListener);
content = null;
}
}
private void watchContentVisible(Node content) {
this.content = content;
if (content != null) {
content.visibleProperty().addListener(expandChangeListener);
}
}
@Override
public void changed(ObservableValue<? extends Node> observable, Node oldValue, Node newValue) {
unwatchContentVisible();
watchContentVisible(content);
}
}
public void add(TitledPane titledPane) {
titledPane.expandedProperty().addListener(expandChangeListener);
ContentWatcher oldWatcher = watchingContents.put(titledPane, new ContentWatcher(titledPane));
if (oldWatcher != null) {
oldWatcher.unwatch();
}
}
public void remove(TitledPane titledPane) {
titledPane.expandedProperty().removeListener(expandChangeListener);
ContentWatcher oldWatcher = watchingContents.get(titledPane);
if (oldWatcher != null) {
oldWatcher.unwatch();
}
}
}
public class TitledPaneStackingExample extends Application {
@Override
public void start(Stage stg) throws Exception {
stg.setTitle(getClass().getSimpleName());
SplitPane splitter = new SplitPane();
{
TextArea text1 = new TextArea("aaa");
TextArea text2 = new TextArea("bbb");
TextArea text3 = new TextArea("ccc");
text1.setPrefRowCount(3);
text2.setPrefRowCount(3);
text3.setPrefRowCount(3);
SplitPane pane = new SplitPane();
pane.setOrientation(Orientation.VERTICAL);
TitledPane pane1 = new TitledPane("aaa", text1);
pane1.setAnimated(false);
pane1.setMaxHeight(Double.MAX_VALUE);
TitledPane pane2 = new TitledPane("bbb", text2);
pane2.setAnimated(false);
pane2.setMaxHeight(Double.MAX_VALUE);
pane2.heightProperty().addListener((self, old, v) -> {
System.out.println("height=" + v);
});
TitledPane pane3 = new TitledPane("ccc", text3);
//pane3.setAnimated(false);
pane3.setMaxHeight(Double.MAX_VALUE);
pane.getItems().addAll(pane1, new VBox(new Label("@@@@")), pane2, pane3);
splitter.getItems().add(pane);
// TitledPaneのon/offによりSplitePaneを自動的にリサイズする
// (閉じていないTitledPaneを均等割りする)
TitledPaneStackingCoordinator cood = new TitledPaneStackingCoordinator(pane);
cood.add(pane1);
cood.add(pane2);
cood.add(pane3);
Platform.runLater(cood::doLayout);
}
stg.setScene(new Scene(splitter));
stg.show();
}
public static void main(String[] args) {
launch(args);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment