回答編集履歴

2

説明のミスを訂正

2018/03/17 06:12

投稿

KSwordOfHaste
KSwordOfHaste

スコア18394

test CHANGED
@@ -134,4 +134,4 @@
134
134
 
135
135
 
136
136
 
137
- 本件ではChangeListener経由で(2)->(3)をすることで一応回避できているように見えますが、ChangeListenerは万能ではなく、プロパティーの依存関係に本質的に巡回関係ができてしまうとどうやってもNGになる場合もあります。
137
+ 本件ではChangeListener経由でviewportプロパティーの設定をすることで一応回避できているように見えますが、ChangeListenerは万能ではなく、プロパティーの依存関係に本質的に巡回関係ができてしまうとどうやってもNGになる場合もあります。

1

実装例追記

2018/03/17 06:12

投稿

KSwordOfHaste
KSwordOfHaste

スコア18394

test CHANGED
@@ -1 +1,137 @@
1
1
  BackgroundSizeのcovertをfalseにcontainをtrueにしてみてはいかがでしょうか?
2
+
3
+
4
+
5
+ ---
6
+
7
+ 追記:質問者さんの本来の期待(処理をごりごり書くのではなく、制約として記述すること?)はCSSあるいはCSS相当のBackgroundによる指定だと思うのですが、自分には指定方法がわかりません。
8
+
9
+
10
+
11
+ 回避策として多少処理を書くのであれば次のようには書けると思います。背景画像ではなくStackPaneの一番底にImageViewを配置する方法です。ImageViewならfitWidth, fitHeight, viewportの各プロパティーを制御することで比較的自由度の高い配置がプログラミングできるかと思います。
12
+
13
+
14
+
15
+ ```java
16
+
17
+ public class FxImageTest extends Application {
18
+
19
+ @Override
20
+
21
+ public void start(Stage stage) throws Exception {
22
+
23
+ Image img = new Image(FxImageTest.class.getResource("image2.jpg")
24
+
25
+ .toURI().toURL().toString());
26
+
27
+
28
+
29
+ StackPane root = new StackPane();
30
+
31
+
32
+
33
+ ImageView iv = new ImageView();
34
+
35
+ iv.fitWidthProperty().bind(root.widthProperty());
36
+
37
+ iv.fitHeightProperty().bind(root.heightProperty());
38
+
39
+ Binding<Rectangle2D> viewport = Bindings.createObjectBinding(
40
+
41
+ () -> {
42
+
43
+ Image image = iv.getImage();
44
+
45
+ if (image == null) return null;
46
+
47
+ double vpWidth = Math.max(1, image.getWidth());
48
+
49
+ double vpHeight = Math.max(1, image.getHeight());
50
+
51
+ double imageRatio = vpWidth / vpHeight;
52
+
53
+ Bounds bounds = root.getBoundsInLocal();
54
+
55
+ double boundsRatio = Math.max(1, bounds.getWidth())
56
+
57
+ / Math.max(1, bounds.getHeight());
58
+
59
+ if (boundsRatio > imageRatio) {
60
+
61
+ // 元画像の上下をカット
62
+
63
+ vpHeight = vpWidth / boundsRatio;
64
+
65
+ } else {
66
+
67
+ // 元画像の左右をカット
68
+
69
+ vpWidth = vpHeight * boundsRatio;
70
+
71
+ }
72
+
73
+ return new Rectangle2D(
74
+
75
+ (image.getWidth() - vpWidth) * 0.5,
76
+
77
+ (image.getHeight() - vpHeight) * 0.5,
78
+
79
+ vpWidth, vpHeight);
80
+
81
+ },
82
+
83
+ root.boundsInLocalProperty(), iv.imageProperty());
84
+
85
+ iv.setImage(img); // viewportの初期値を設定するため。
86
+
87
+
88
+
89
+ viewport.addListener((ob, ov, nv) -> iv.setViewport(nv)); // ※補足参照
90
+
91
+
92
+
93
+ Button button = new Button("toggle background");
94
+
95
+ button.setOnAction(ev -> iv.setImage(iv.getImage() == null ? img : null));
96
+
97
+ button.setPrefSize(150, 30);
98
+
99
+
100
+
101
+ root.getChildren().addAll(iv, button);
102
+
103
+
104
+
105
+ Scene scene = new Scene(root, 800, 500);
106
+
107
+
108
+
109
+ stage.setScene(scene);
110
+
111
+ stage.show();
112
+
113
+ }
114
+
115
+ }
116
+
117
+ ```
118
+
119
+
120
+
121
+ ---
122
+
123
+ ※補足:
124
+
125
+ コード中の※の行ですが、本当は
126
+
127
+ `iv.viewportProperty().bind(viewport);`
128
+
129
+ と書きたいところですが、そうやってしまうとstack overflowが起きます。
130
+
131
+
132
+
133
+ JavaFXのプロパティーのバインドはObserverパターンを非常に簡単に利用できる点はよいのですが、ジオメトリー(ノードのレイアウト)にかかわるプロパティーに下手に依存関係を定義してしまうと内部のレイアウト処理の実装からの影響で、依存関係の循環が発生してしまう点に注意が必要です。
134
+
135
+
136
+
137
+ 本件ではChangeListener経由で(2)->(3)をすることで一応回避できているように見えますが、ChangeListenerは万能ではなく、プロパティーの依存関係に本質的に巡回関係ができてしまうとどうやってもNGになる場合もあります。