質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.02%

JavaFXでのBackgroundImageの中央指定

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,156

glat2238

score 120

JavaFXでのBackgroundImageの中央指定

今、JavaFXで簡単なアプリケーションを作成しているのですが画像を読み込めても中央に表示することができません。(画面を縮小すると画像が左寄りになってしまう)
BackgroundPosition.CENTERを追加したのですが画像が左寄りです。
これを中央に寄せられないでしょうか?
できなければ画面の横を縮めたら縦も比例して縮まるように設定などできないでしょうか。
どなたか教えてください。

 該当のソースコード

public class Main extends Application {

@Override
public void start(Stage stage) throws MalformedURLException, URISyntaxException {
Image img = new Image(new File(SFile.getApplicationPath(Main.class).getParent +File.separator +"data" + File.separator + "client" + File.separator + "image" + File.separator + "image.png").toURI().toURL().toString());

BackgroundSize bs = new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, false,false, false, true);
BackgroundImage bimg = new BackgroundImage(img, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER, bs);
Background background = new Background(bimg);

StackPane root = new StackPane();
root.setBackground(background);

Scene scene = new Scene(root, 800, 500);

stage.setScene(scene);
stage.setFullScreen(true);
stage.show();
}
public static void main(String[] args) {
launch(args);

}

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

BackgroundSizeのcovertをfalseにcontainをtrueにしてみてはいかがでしょうか?


追記:質問者さんの本来の期待(処理をごりごり書くのではなく、制約として記述すること?)はCSSあるいはCSS相当のBackgroundによる指定だと思うのですが、自分には指定方法がわかりません。

回避策として多少処理を書くのであれば次のようには書けると思います。背景画像ではなくStackPaneの一番底にImageViewを配置する方法です。ImageViewならfitWidth, fitHeight, viewportの各プロパティーを制御することで比較的自由度の高い配置がプログラミングできるかと思います。

public class FxImageTest extends Application {
  @Override
  public void start(Stage stage) throws Exception {
    Image img = new Image(FxImageTest.class.getResource("image2.jpg")
                            .toURI().toURL().toString());

    StackPane root = new StackPane();

    ImageView iv = new ImageView();
    iv.fitWidthProperty().bind(root.widthProperty());
    iv.fitHeightProperty().bind(root.heightProperty());
    Binding<Rectangle2D> viewport = Bindings.createObjectBinding(
      () -> {
        Image image = iv.getImage();
        if (image == null) return null;
        double vpWidth = Math.max(1, image.getWidth());
        double vpHeight = Math.max(1, image.getHeight());
        double imageRatio = vpWidth / vpHeight;
        Bounds bounds = root.getBoundsInLocal();
        double boundsRatio = Math.max(1, bounds.getWidth())
                           / Math.max(1, bounds.getHeight());
        if (boundsRatio > imageRatio) {
          // 元画像の上下をカット
          vpHeight = vpWidth / boundsRatio;
        } else {
          // 元画像の左右をカット
          vpWidth = vpHeight * boundsRatio;
        }
        return new Rectangle2D(
          (image.getWidth() - vpWidth) * 0.5,
          (image.getHeight() - vpHeight) * 0.5,
          vpWidth, vpHeight);
      },
      root.boundsInLocalProperty(), iv.imageProperty());
    iv.setImage(img); // viewportの初期値を設定するため。

    viewport.addListener((ob, ov, nv) -> iv.setViewport(nv)); // ※補足参照

    Button button = new Button("toggle background");
    button.setOnAction(ev -> iv.setImage(iv.getImage() == null ? img : null));
    button.setPrefSize(150, 30);

    root.getChildren().addAll(iv, button);

    Scene scene = new Scene(root, 800, 500);

    stage.setScene(scene);
    stage.show();
  }
}

※補足:
コード中の※の行ですが、本当は
iv.viewportProperty().bind(viewport);
と書きたいところですが、そうやってしまうとstack overflowが起きます。

JavaFXのプロパティーのバインドはObserverパターンを非常に簡単に利用できる点はよいのですが、ジオメトリー(ノードのレイアウト)にかかわるプロパティーに下手に依存関係を定義してしまうと内部のレイアウト処理の実装からの影響で、依存関係の循環が発生してしまう点に注意が必要です。

本件ではChangeListener経由でviewportプロパティーの設定をすることで一応回避できているように見えますが、ChangeListenerは万能ではなく、プロパティーの依存関係に本質的に巡回関係ができてしまうとどうやってもNGになる場合もあります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/17 15:09

    出来ました!
    ありがとうございます。

    キャンセル

  • 2018/03/17 15:15

    cssでできたかどうかに興味がありますが・・・ひょっとして自分の追記(回避策)ぐらいしかありませんでした?

    キャンセル

  • 2018/03/19 12:57

    CSSでやっても同じ結果でしたw
    何かのバグですかね?

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.02%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる