プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

JavaFXの練習4:レイアウトがわからない

 さぁ、JavaFX2記事第4弾。ついに詰まりました。全然わかりません。誰か教えてください。
 今、nodamushiがわからないこと

  1. 別スレッドで処理した内容をsetTextでLabelの内容を変えようとするとスレッドがJavaFXのスレッドじゃないとエラーになる。SwingUtilities.invokeLater(Runnable)みたいなことはどうすればいいの?
  2. あるプロパティの値の変化でNodeの最適な大きさが変化したときに、どうすれば自動的に大きさを変更できるのか。(Swingでいうrevalidateとかみたいな)
  3. ぶっちゃけ、プロパティのbeanって何よ。豆って何よ。ていうか、プロパティってどれ使えばいいのよ。

 というわけで、誰か優しい人が教えてくれると期待しつつ(チラッ 今日やった内容を書き留めときます。


自作のレイアウトを作ってみる。

 簡単なレイアウトなら、既存のレイアウトパネル群を使えば、大体は出来るだろう。でも、やっぱ出来ないことだってあるじゃん。それに、今までSwing使ってきた私としては、自分でLayoutManagerを書いた方が何となくやりやすい。
 というわけで、練習として、今回は下の図の様に自分の子を円形に配置するレイアウトを作ってみることにしたよ。




 レイアウトのためのノードを作るには、javafx.scene.layout.Paneを継承したクラスを作ればいいっぽい。で、以下のメソッドをオーバーライドする。


  • layoutChildren():実際に子の配置を行うメソッド
  • computePreWidth(double):このノードの最適な幅を計算するメソッド
  • computePreHeight(double):このノードの最適な高さを計算するメソッド
  • computeMinWidth(double):このノードの最小の幅を計算するメソッド
  • computeMinHeight(double):このノードの最小の高さを計算するメソッド
  • computeMaxWidth(double):このノードの最大の幅を計算するメソッド
  • computeMaxHeight(double):このノードの最大の高さを計算するメソッド

 今回、最大最小は特に気にしないことにしたので、最初の三つをオーバーライドすることにした。なお、最小値のデフォルトはinsetの大きさ、最大はDouble.MaxValueを返すらしい。

 円形に配置したいので、高さの最適値は「円の直径+子の高さの中で最大のもの」、幅も同様に「円の直径+この幅の中で最大のもの」とした。今回はinsetは考えていない。
 と、いうわけで、これを元に実装するとこうなる。

package nodamushi.layout;

import javafx.beans.property.*;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.layout.*;

public class CirclePane extends Pane{
  //半径
  private double radius;  
  public CirclePane(double r) {
    setRadius(r);
  }
  
  public void setRadius(double d){radiuse=d;}
  public double getRadius(){return radius;}
  
  private double getMaxChildWidth(double height){
    ObservableList<Node> nodes = getChildren();
    double maxWidth=0;
    for(Node n:nodes){
      double d = n.prefWidth(height);
      if(maxWidth < d)maxWidth = d;
    }
    return maxWidth;
  }
  private double  getMaxChildHeight(double width){
    ObservableList<Node> nodes = getChildren();
    double maxHeight=0;
    for(Node n:nodes){
      double d = n.prefHeight(width);
      if(maxHeight < d)maxHeight = d;
    }
    return maxHeight;
  }
  @Override protected double computePrefWidth(double height) {
    return getMaxChildWidth(height)+getRadius()*2;
  }
  
  @Override protected double computePrefHeight(double width) {
    return getMaxChildHeight(width)+getRadius()*2;
  }
  
  @Override protected void layoutChildren() {
    ObservableList<Node> nodes = getChildren();
    int length = nodes.size();
    if(length == 0)return;
    double step = 2*PI/length;//一つ配置する毎の回転角度
    double rad = 0;//現在の配置角度
    double width = getWidth();//このノードの大きさ
    double height = getHeight();
    double r = getRadius();//半径
    for(Node n:nodes){
      //配置するノードの中心座標を計算
      double x = Math.cos(rad)*r+width/2;
      double y = Math.sin(rad)*r+height/2;
      //配置するノードの左上の座標を計算
      double w = n.prefWidth(-1);
      double h = n.prefWidth(-1);
      x = x- w/2d;
      y = y- h/2d;
      //再配置
      n.resizeRelocate(x, y, w, h);
      //配置角度の更新
      rad+=step;
    }
  }
}

 このCirclePaneに適当にCircleを30個追加して表示した結果が先の図ってわけです。ひとまず、レイアウトをすることは出来たね。でも、せっかくなので、この半径を変更したり、配置の開始角度を変更したりしてアニメーションさせることが出来たら楽しそうだよね。

 というわけで、double radiusをDoublePropertyに変えてみよう………と思わなけりゃ良かった。
 とりあえず、radiusの値の変更があったら、自動で再レイアウトをしてもらわないといけない。何となく、それっぽい関数にrequestLayout()があったので、それを呼んでみた。


private DoubleProperty radius = new DoublePropertyBase() {
  @Override
  public void invalidated() {
    requestLayout();
  }
  
  @Override
  public String getName() {
    return "radius";
  }
  
  @Override
  public Object getBean() {
    return CirclePane.this;
  }
};
public final void setRadius(double d){radiuseProperty().setValue(d);}
public final double getRadius(){return radius.getValue();}

 とりあえず、これでアニメーションしてみた。

//importは略
public class Main  extends Application{
  public static void main(String[] args){launch(args);}
  public void start(Stage stage) throws Exception {
    CirclePane c = new CirclePane(200);
    ObservableList<Node> child = c.getChildren();
    for(int i=0;i<30;i++){
      Color color = new Color(Math.random(), Math.random(),Math.random(), 1);
      child.add(new Circle(20, color));
    }
    VBox vbox = new VBox();
    ObservableList<Node> vchild = vbox.getChildren();
    vchild.add(new Label("top"));
    vchild.add(c);
    vchild.add(new Label("bottom"));
    
    stage.setScene(new Scene(vbox));
    stage.show();
    
    new Timeline(new KeyFrame(new Duration(1500), new KeyValue(c.radiuseProperty(), 250))).play();
  }
}

 以下の内容は勘違いだと気がつきました。ちゃんと、レイアウトの大きさの変更は上のレイアウトに伝わっていました。最初っからがっつりウィンドウサイズを大きくしてから実行した例↓

 きちんとアニメーションに従ってbottomの位置が変化しました。とりあえず、requestLayoutで良さそうです。


 で、結果というと


 ふ〜む………。いやね、ウィンドウサイズまで変化するとは私も思っていなかったんですよ。でも、CirclePaneを代入したVBoxはレイアウトを変更してくれる。つまり、上の図では小さくて見にくいと思いますが、topとbottomの文字が円に被らないように再配置されると思ったんですよ。しかし、結果はtopとbottomは動かないっ!再配置されない!


 で、わからんから、OpenJFXのソースコード読んでみたんですよ。そしたら、平気で、impl_markDrityとか意味わからん関数使っとるんす。意味わからねーよ。
 というわけで、私が目的とする動作をさせるためにはどうすればいいのか、誰か教えてください………(´Д⊂ヽ

JavaFX練習3:アニメーションとか

 前回の続きで今日はアニメーションについて練習しました。あと、SceneはSwingのContentPane相当だということを理解しました。
 しかし、あれだね。JavaFXで3Dが使えるようになると言うことで、今のうちに覚えておこうとやっているんだけど、やっぱりJavaFXって好かんわ。もちろん、Swingよりずっと勝ってるところがあるのはわかるんだよ。でもね、気持ち悪いんだ。
 一番気持ち悪いのはNodeのサイズをいったい誰がどうやって計算してるのかさっぱりわからんこと。prefWidthやらmaxWidthやらminWidthやらはあっても、全部値が-1だったりして、おま、どうやって大きさ決めてんねん。意味わからんわ。だから、初心者の私には挙動がさっぱりわからん。どこをどう設定したら自分の目的とする大きさに設定できるのかがわからん。あと、レンダリングがいったい誰がいつどうやってやってんのかさっぱり見えないこと。設定、設定、で動くのは確かに便利かもしれんが、SwingでpaintComponentやGraphicsとかで直接指示出したり、画像処理で直にピクセル弄ってる人間からしたら気持ち悪くてしょうが無い。もうすでに老害というやつになってるんでしょうか、私。


アニメーション

 JavaFXではアニメーションはAnimationクラスのサブクラス達を使って行うみたいです。挙動はどうあれ、アニメーションの動きの基本は、

こんな感じのようです。このアニメーションはJavaFX Application Threadという名前のスレッドで動いているみたいです。JavaFX Application Threadはどうやら、SwingでいうところのEDTみたいなもののようです。ということは、うっかりアニメーション処理で重たい処理を挟むとJavaFXが止まると言うことなんでしょうかね。
 で、Animationクラスは大きく分けて、TimelineとTransitionの二つで、Timelineはいろんな値を扱ったアニメーションするために、Transitionは対象ノードのプロパティの値を変化させるアニメーションをするために利用するみたいです。

 TimelineはKeyFrameというクラスを使って複数の動作をするアニメーションを定義できるようです。Timelineと言う名前から、私は最初このクラスの挙動は、KeyFrame1を実行後、KeyFrame2を実行して、その後KeyFrame3を………というものだと思っていたのですが、全部いっぺんに動作し始めるみたいでした。

 この図で言うと、上の挙動をするとおもっていたけど、下の方だったということです。

 Transitionは、FadeTransition, FillTransition,PathTransition, RotateTransition, ScaleTransition, StrokeTransition, TranslateTransitionがあり、それぞれ透明度の変化だったり、移動だったりのアニメーションを行ってくれます。また、これらのアニメーションを同時に全て実行するアニメーションをつくるParallelTransition、アニメーションが終わると次のアニメーションを実行するSequentialTransitionがある。


PauseTransitionの価値がよくわかんないっす。setDelayじゃだめなんすか?


スライドイン、スライドアウトするアニメーションを作ってみる

 前回のやつをアニメーションするようにします。アニメーションは

  1. スライドインしながらフェードイン もしくは スライドアウトしながらフェードアウト
  2. 矢印の向き回転

 という動きをするようにします。

 まずは、スライドの動きを作りたいのですが、JavaFXのアニメーションをさせるには何はともあれWritablePropertyが何か必要なんですが、widthPropertyはReadOnlyだし、maxwidthとかの値はそもそもpropertyがない。

 どうすればいいのか、よくわからなかったので、正攻法かどうかわかりませんが、自分で以下のようなWritablePropertyを定義してみました。

    //AnchorPane直下のVBoxもControllerで受け取れるように変更しました。
    public VBox contents_info;
    //サイズを変えるために、間に噛ませたプロパティ
    private DoubleProperty infoboxWidth=new SimpleDoubleProperty(1){
        public void set(double d) {
            super.set(d);
            //dによってmaxWidthを変化させる
            if(d==1d){
                infobox.setMaxWidth(-1);
                return;
            }
            double contents_info_width = contents_info.getWidth();
            double boxwidth = contents_info_width*d;
            infobox.setMaxWidth(boxwidth);
        }
    };

 で、このプロパティの値を0〜1まで変化させることで、スライドのアニメーションを作ってみました。ノードのサイズの変更は、resizeRelocateを使ったら出来るかと思ったのですが、スライドさせることが出来ませんでした。
 以下はアニメーション定義の全容です。全てのソースコードはGitHubにあげておきました。
 もっとうまいやり方があるのかもしれませんが、とりあえず、今回はゴリゴリアニメーションの定義を書いてみました。一応、目的とする動作をさせることが出来ました。



    private SequentialTransition openAnimation,closeAnimation;

    public void slideAction(ActionEvent event){
        if(isOpen)closeAnimation.play();
        else openAnimation.play();
        isOpen = !isOpen;
    }

    @Override
    public void initialize(URL url, ResourceBundle resource) {
        //アニメーション設定

        //スライドインさせるアニメーション
        Timeline slidein = new Timeline(
                new KeyFrame(new Duration(0), new EventHandler<ActionEvent>(){
                    @Override
                    public void handle(ActionEvent e) {
                        if(closeAnimation.getStatus()==Status.RUNNING)
                            closeAnimation.stop();//ついでなので、クローズアニメーションをストップさせてみた
                    }

                }),
                new KeyFrame(new Duration(300),new KeyValue(infoboxWidth,1))
                );
        //フェードインさせるアニメーション
        //Timelineに追加してもいい気がしたけど、
        //練習のためにFadeTransitionを利用
        FadeTransition fadein = new FadeTransition(new Duration(300),infobox);
        fadein.setFromValue(0);
        fadein.setToValue(1);
        fadein.setDelay(new Duration(100));//ちょっと開始を遅らせる。

        //スライドとフェードを結合
        ParallelTransition open = new ParallelTransition(slidein,fadein);

        //スライドインが終わった後にボタンの向きを回転させる
        RotateTransition openrotate = new RotateTransition(new Duration(300),sbutton);
        openrotate.setToAngle(-90);
        openrotate.setFromAngle(90);
        openrotate.setDelay(new Duration(100));


        //openと回転を順次実行するアニメーション
        openAnimation = new SequentialTransition(open,openrotate);

        //スライドアウト
        Timeline slideout = new Timeline(
                new KeyFrame(new Duration(0), new EventHandler<ActionEvent>(){

                    @Override
                    public void handle(ActionEvent e) {
                        if(openAnimation.getStatus()==Status.RUNNING)
                            openAnimation.stop();
                    }

                }),
                new KeyFrame(new Duration(300),new KeyValue(infoboxWidth, 0))
                );
        //フェードアウト
        FadeTransition fadeout= new FadeTransition(new Duration(200),infobox);
        fadeout.setFromValue(1);
        fadeout.setToValue(0);

        //結合
        ParallelTransition close = new ParallelTransition(slideout,fadeout);

        //回転
        RotateTransition closerotate = new RotateTransition(new Duration(300),sbutton);
        closerotate.setToAngle(90);
        closerotate.setFromAngle(-90);
        closerotate.setDelay(new Duration(100));
        //結合
        closeAnimation = new SequentialTransition(close,closerotate);

JavaFXの練習2:Controllerとか

 というわけで、今回もJavaFXの練習した内容です。

 今回は以下のような感じのパネルを作ってみようとしております。



 画像と、ファイル名と、ファイルの説明を表示して、ボタンをクリックするとファイル名と説明部分がスライドアウトしたりスライドインしたりするようなものです。かっくいいね。このスライドイン、アウトをどうすればいいのかにえらい四苦八苦しました。。。


 今回はJava言語を利用する必要があるので、Eclipseでプロジェクトを作成します。先に空っぽのtest.cssと表示する画像のサンプルをプロジェクトフォルダに入れておきました。



 JavaFXの開発をするにはjfrt.jarが必要ですので、jre/libの下にあるjfxrt.jarをビルドパスに追加しておきます。




 ビルダーを起動して、とりあえず空っぽのまま、fxmlをEclipseのプロジェクト上に保存しておきます。





 さて、準備完了したので上の設計図の様に作れるように配置をしていきます。とりあえず、こんな感じになりました。




 AnchorPaneはスライドインしたり、スライドアウトしたりする為に利用しています。VBoxのAnchorPaneに対する制約は、上下右を全部0にしています。
 

 また、全てのノードの大きさを計算した結果の最適な大きさに指定しています。ていうか、なんでJavaFXはサイズに関する指定をCSSでできるようにしなかったのかね?





 CSSを適応させる為に、各ノードのStyleClass名をこんな感じに設定しました。

 で、先に作っておいたtest.cssを読み込ませます。



 さて、CSSを作っていきます。
 まずは背景は真っ黒で、文字列は白色なので、その設定。

.image_info_panel{
    -fx-background-color:black;
    -fx-padding:20px 0 20px 20px;
}

.image_info_panel .label{
    -fx-text-fill:white;
    -fx-font:14px "Arial",self;
}

 次に、文字列やボタンを予定通り下に配置する為にalignmentをbottom-leftにする。

.contents_info,.slidepanel{
    -fx-alignment:bottom-left;
    -fx-padding:0 5px 10px 5px;
}



 ボタンの色と、ボタンの上にマウスが乗ったときに色がほんのり変わるように設定。三角形が横を向くように-fx-rotateをCSSで90を指定しておくと、Java側でsetRotateをしても変化させることが出来なくなったので、コメントアウトしています。この辺JavaFXのCSSは使いにくい…。

.slidebutton{
    -fx-background-color:rgba(0,0,0,0);
    -fx-padding:0;
    -fx-text-fill:gray;
    /*-fx-rotate:-90;*/
}

.slidebutton:hover{
    -fx-text-fill:#608C71;
   -fx-effect:dropshadow( gaussian,#ff0 ,10px, 0, 0,0);
}



 微妙に装飾が寂しかったので、色々装飾。そのさい、-fx-font-style:italicを設定しても、全然斜体にならなかったんですけど、これなんでなんですかね。

/*画像のファイル名*/
.fname_info{
    -fx-padding:0 10px 10px 10px;

    /*font-styleで結果が変わらないんだけど、なんで?*/
    -fx-font-style:italic;
    

    /*仕方が無いので、書き直し*/
    -fx-font:italic 14px "Arial",self;
    -fx-effect:dropshadow( gaussian,#ff0 ,10px, 0, 0,0);
}

/*画像の説明*/
.exp_info{
    -fx-padding:0 10px 0 30px;
}

/*画像の内側にインナーシャドウ*/
.contents_img{
    -fx-effect:innershadow( gaussian, black, 20px , 0,10,0);
}



 はい、これでCSSの設定は終わりです。こんな感じのデザインになりました。

 お〜、当初の予定をだいぶいい感じに再現できましたね。後はJavaでこれらを操作できるようにControllerの設定をしてビルダーのお仕事は終了です。

 Eclipseに戻ってきて、JavaFXを実行できるようにします。実はSceneが何なのか私よく理解してないんですが、とりあえず、これが定石らしいです。

import java.net.*;
import java.nio.file.*;
import javafx.application.*;
import javafx.fxml.*;
import javafx.scene.*;
import javafx.stage.*;

public class Main extends Application{
  public static void main(String[] args) {
    launch(args);
  }

  @Override
  public void start(Stage stage) throws Exception {
    Path fxmlfile = Paths.get("test.fxml");
    URL url = fxmlfile.toUri().toURL();
    
    Parent parent = (Parent)FXMLLoader.load(url);
    
    Scene scene = new Scene(parent);
    stage.setScene(scene);
    stage.show();
  }
}




 で、コントローラーの作成。infoboxとかsbuttonとかの値の設定やslideActionとかの呼び出しはリフレクションが使われる為、セキュリティの設定よっては、privateやprotectedにするとエラーになるらしい。というわけで、publicにしておいた。privateなどの場合は@FXMLアノテーションを付ける必要がある。

package test;

import javafx.event.*;
import javafx.fxml.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;

public class ImgInfoController{
  //これらの値は自動的にセットされる
  public AnchorPane infobox;
  public Button sbutton;
  
  public void slideAction(ActionEvent event){
    System.out.println("クリックされた!");
  }
}

実行結果




 予定通り、閉じたり開いたり出来るようにしたい。今回はいきなりアニメーションに行くのはしんどいので単純に閉じたり開いたりするだけを実装してみました。

private boolean isOpen=true;

public void slideAction(ActionEvent event){
  System.out.println("クリックされた!");
  
  if(isOpen){
    infobox.setMaxWidth(0);
    infobox.setMinWidth(0);
    sbutton.setRotate(90);
    isOpen=false;
  }else{
    infobox.setMaxWidth(Double.MAX_VALUE);
    sbutton.setRotate(-90);
    isOpen=true;
  }  
}

 で、やってみると………

 あるぅええええ?画像の上に文字が出てるんですけどー。とりあえず、CSSのoverflow:hiddenとか設定すればいいのかと思ったら、そんな項目ないし。で、色々悩んで、とりあえずclip設定すればいいんでね?とやってみた。
 clipの設定はオブジェクトが作られたときにやって欲しかったので、このクラスにInitializableを実装する。

import java.net.URL;
import java.util.ResourceBundle;
import javafx.scene.shape.Rectangle;
public class ImgInfoController implements Initializable{
  //オーバーしたのを隠すためのクリップ
  private Rectangle clip;
  
  @Override
  public void initialize(URL url, ResourceBundle resource) {
    clip = new Rectangle();
    infobox.setClip(clip);
    //clipの大きさをinfoboxの大きさに常に合わせる
    clip.widthProperty().bind(
        infobox.widthProperty());
    clip.heightProperty().bind(
        infobox.heightProperty());
    
    sbutton.setRotate(-90);
  }

 実行結果



 おぉ、消えた消えたヽ( ・∀・)ノ

 次はアニメーションに挑戦かー。まだやってないので、次がいつのかわかりません。

JavaFX2の練習1

 JavaFX2のCSS意味わかんない。そもそも、そんなCSS得意でもないのに、普通のHTML用のCSSともなーんか全然違ってマジブッコロ。JavaFXのCSSのページまじ意味わからねーし。それぐらいならSwingでゴリるわ。とか思ってたのでJavaFX2手を出していませんでしたが、日本語記事は全然見つけられなかったけど、英語記事だとわかりやすいのがいくつか見つかってきたので色々試したことを雑記していこうと思います。たまったらそのうちまとめる。

ラベルで簡単なCSSの練習

 JavaFX Scene Builderを使ってCSSの記述の練習をしていこうと思います。
 今日はフォントの設定と、背景と、ラベルの画像の指定について。
 まずは、とりあえず、Labelを貼り付けます。StyleClassの項目の[+]を押して、testclassという名前のクラスを追加しておきました。




 次に、デスクトップにtest.cssというファイルを作成しました。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-padding:10px;
}

 StyleSheetsの項目の+から、この作成したファイルを読み込むよう設定します。するとこうなりました。




 ちょっと、悪戯して、Builderの方でラベルのボックスサイズを変えます。

 文字列をセンタリングしたいです。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    /* text-alignmentじゃ、文字列はセンタリングされなかった
    -fx-text-alignment:center;*/
    -fx-alignment:center;
}

 理由は知りませんが、text-alignmentじゃ、センタリングされませんでした。このCSSは何の意味があるのか、今の私にはまだわかりません。

 とりあえず、alignmentでセンタリングできました。




 背景色を指定してみます。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-alignment:center;
    -fx-background-color:#9cc;
}





 JavaFXには背景にレイヤーという概念があるみたいです。次の様に複数の背景を指定できます。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-alignment:center;
    -fx-background-color:#9cc,#cff;
}

 すると、こうなります。



 現在は二つのレイヤーの大きさが同じなので、一番上の#cffだけが見えてる状態らしいです。background-insetsで、背景のマージン的なものを設定します。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-alignment:center;
    -fx-background-color:#9cc,#cff;
    -fx-background-insets: 0 ,1 3 20 3;
}

 一つ目のレイヤーはinsetsはすべて0で、上のレイヤーは上1px、左3px、下20px、右3pxになっています。




 さらに、背景は各レイヤーについて角丸を適応できます。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-alignment:center;
    -fx-background-color:#9cc,#cff;
    -fx-background-insets: 0 ,1 3 20 3;
    -fx-background-radius:0 , 0 0 20px 20px;
}





 おっしゃおっしゃ、背景についてはだいぶ理解した。次は、テキストにアンダーラインを入れてみる。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-alignment:center;
    -fx-background-color:#9cc,#cff;
    -fx-background-insets: 0 ,1 3 20 3;
    -fx-background-radius:0 , 0 0 20px 20px;
    -fx-underline:true;
}


 できたできた。


 ラベルには、どうやら文字列の前に画像を挿入する機能があるらしい。graphicで指定できる。とりあえず、はてなさんのアイコンを突っ込んでみた。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-alignment:center;
    -fx-background-color:#9cc,#cff;
    -fx-background-insets: 0 ,1 3 20 3;
    -fx-background-radius:0 , 0 0 20px 20px;
    -fx-underline:true;
    -fx-graphic:url("http://www.hatena.ne.jp/images/portal/logo-portal-top2@2x.png");
}


 おぉ、挿入された………けど、はてなさん、でかすぎです。文字が見切れてます。なので、文字を折り返しさせます。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-alignment:center;
    -fx-background-color:#9cc,#cff;
    -fx-background-insets: 0 ,1 3 20 3;
    -fx-background-radius:0 , 0 0 20px 20px;
    -fx-underline:true;
    -fx-graphic:url("http://www.hatena.ne.jp/images/portal/logo-portal-top2@2x.png");
    -fx-wrap-text:true;
}

 ほむほむ。でも、なんかあれだね。はてなさんを左じゃなくて、上に持って行きたい。

.testclass{
    -fx-font:italic bold 14px 'Arial','MS P明朝',serif;
    -fx-alignment:center;
    -fx-background-color:#9cc,#cff;
    -fx-background-insets: 0 ,1 3 20 3;
    -fx-background-radius:0 , 0 0 20px 20px;
    -fx-underline:true;
    -fx-graphic:url("http://www.hatena.ne.jp/images/portal/logo-portal-top2@2x.png");
    -fx-wrap-text:true;
    -fx-content-display:top;
}


 うん、上に来た。


 今日の所はこんな所かなー。次はボタンかなー。

FXML試してみた

お久しぶりです。卒論で死にかけていますがひとまず息はしています。


さて、JavaFXが出てから結構立ったのでそろそろ私も弄ってみようと、FXMLというXML文書でGUIを構成する機能を試してみましたところ、謎の挙動を示したのでここで報告しておこうと思います。

"Getting Started with FXML"にあるSign inの画面とほぼ作ろうとしている物は同じです。ただ、画像を用意するのが面倒だったので、端折ってGridPane部分だけ作っています。


まずはJavaFXを起動するApplicationを拡張したクラス。

package fx;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.Stage;

public class FXTest extends Application{
	public void start(Stage stage) throws Exception {
		stage.setTitle("FMLX Test");
		Parent root = FXMLLoader.load(FXTest.class.getResource("login.fxml"));
		Scene scene = new Scene(root);
		stage.setScene(scene);
		stage.show();
	}
	public static void main(String[] args) {
		launch(args);
	}
}

FXMLLoderでclassファイルの場所にあるlogin.fxmlを読み込んでシーンにするだけです。
次に、先にコントローラーにあたるLogin.javaを示します。

package fx;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
public class Login {
	@FXML TextField usnameField;
	@FXML PasswordField passwordField;
	@FXML protected void loginAction(ActionEvent a){
		System.out.println(usnameField.getText()+ "  " + passwordField.getText());
	}
}

ユーザー名とパスワードを表示するだけです。


最後に問題のlogin.fxmlです。

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.*?>
<?import javafx.scene.image.*?>

<GridPane fx:controller="fx.Login" alignment="top_center" hgap="8" vgap="8" style="-fx-padding: 40 0 0 0;" xmlns:fx="http://javafx.com/fxml">
	<children>
	
<!--	<Label text="dummy text"  GridPane.columnIndex="1" GridPane.rowIndex="0"/>  -->
	
		<Label text="log in:"  
			style="-fx-font:normal 15 Tahoma;"
			GridPane.columnIndex="0" GridPane.rowIndex="0"/>
		
		<Label text="name" 
			style="-fx-font:normal 10 Tahoma;"
			GridPane.columnIndex="0" GridPane.rowIndex="1"/>
		
		<TextField fx:id="usnameField"  GridPane.columnIndex="1" GridPane.rowIndex="1"/>
		
		<Label text="password" 
			style="-fx-font:normal 10 Tahoma;"
			GridPane.columnIndex="0" GridPane.rowIndex="2"/>
		
		<PasswordField fx:id="passwordField" style="" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
		
		<Button text="submit" style="" onAction="#loginAction" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
	</children>
</GridPane>



ほぼ"Example 8"の丸パクリですね。

まず、これで実行するとこうなります。

styleでフォントサイズをlog inは15に、その他は10にしているのに差がありません。


次にコメントアウトしていたダミーラベルを表示させてみます。
このダミーラベルはstyleが設定されていません。

きちんと文字の大きさに差が出ました。


これってどういうことなんでしょうか?