えいのうにっき

a-knowの日記です

2012 JavaFX Advent Calendar 11日目・TableView内で棒グラフを表現する

こんにちは!a-knowです。 今日の更新は「JavaFX Advent Calendar 2012」の、11日目のエントリとなります。 Advent Calendarは初挑戦なもので、どうぞお手柔らかに・・・。。(これまでの皆様のエントリで、既に尻込みしてますw)

僕とJavaFX

僕とJavaFXとの関わりを簡単に申し上げさせて頂きますと。。 ちょうど1年ほど前、趣味で作っていたSwingアプリケーションが“改造につぐ改造”でかなりのスパゲッティになってしまっておりまして、 「そろそろ一気に作り直したいなー」と考えていたところにJavaFX 2.0が登場した(目についた)、というのが発端になります。 それじゃあせっかくだし、自分にとって新しい技術で・・・ってんで、FXで作り直したのが今年の5月・・・という、ただそれだけのお話なんですけどね。 (とはいえ何故だか、「はじめようJavaFX 2.x」というテーマで勉強会で登壇させて頂いたこともありましたがw)

もちろん、FXそのものやその考え方(FXMLとか、WebStartとか、cssへの対応とかはステキ!)、今後の展望など、どれをとっても好きなのですが、 それ(アプリケーションの作り直し)以来はあまり情報を追えていないのであまり新しいお話はできない・・・。。 ということで、ここは無理せず、実際に作ったFXアプリケーション「sa-boom!! client」を開発する際に 一番苦労した点について(今振り返ればなんてことはないのですが)、書かせて頂ければと思います。

今回のFXアプリケーションを作る上で一番苦労したところ

sa-boom!! client」は、一言でいうと「iTunesでの再生回数情報を記録し、グラフ化させることができる」アプリケーション。 下記のようなグラフ表示を、TableView内で、させてやりたいと考えていました。

この、

ここの部分が、どうやればいいのかわからなかった!w で、僕は、以下のようにして実現しました〜。

考え方

この棒グラフ的なものを表示させる際に、これを構成している要素に分解して、考えます。今回の場合は以下の3つ。 「大きい方の棒グラフ(赤色)」と「小さい方の棒グラフ(ピンク色)」、そして「再生回数表示」

「大きい方の棒グラフ(赤色)」は、Rectangleを使って以下のように作ることができます。

Rectangle r_to = new Rectangle();
r_to.setWidth(SaboomUtil.calcRectangleWidth(maxWidth, topCount, toCount));
r_to.setHeight(20);
r_to.setFill(Color.RED);

ここで、SaboomUtil.calcRectangleWidth()は再生回数によって変わる棒グラフの高さを計算するメソッドです。 (首位の再生回数による棒グラフの高さ(maxWidth)を1として、2位以降の高さはその再生回数により割合を算出してるだけです。)

続いて、「小さい方の棒グラフ(ピンク色)」も同様に。

Rectangle r_from = new Rectangle();
r_from.setWidth(SaboomUtil.calcRectangleWidth(maxWidth, topCount, fromCount));
r_from.setHeight(20);
r_from.setFill(Color.LIGHTPINK);

そして、「再生回数表示」のLabelを作ります。

Label l;
if (sa > 0) {
    l = new Label(thisCount + " (+" + sa + ")");
} else if (sa == 0) {
    l = new Label(thisCount + " (±" + sa + ")");
} else {
    l = new Label(thisCount + " (" + sa + ")");
}

最後の仕上げ・Groupでこれらの要素をまとめてやることで、棒グラフの完成です。

Group g = new Group();
g.setBlendMode(BlendMode.MULTIPLY);
g.getChildren().add(r_to);
g.getChildren().add(r_from);
g.getChildren().add(l);

setBlendMode(BlendMode.MULTIPLY) を忘れずに。 各要素のadd()は下になるものから順番に行うイメージでしょうか。

あとはこのできた棒グラフを、以下の様なインターフェースを持ったObservableなレコードにセット(ここでの例だとtotalPlayCountに、ですね)してやって、 そのレコードごと、TableViewにaddしてやればOKです(tableView.getItems().add(record))。

import javafx.beans.value.ObservableValue;
import javafx.scene.Group;

public interface ObservableRecordBySong{

    public ObservableValue<Integer> rank();
    public ObservableValue<String> songName();
    public ObservableValue<String> artistName();
    public ObservableValue<String> rating();
    public ObservableValue<Group> totalPlayCount();
}
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Group;

public final class RecordBySong implements ObservableRecordBySong {

    private final SimpleObjectProperty rank;
    private final SimpleStringProperty songName;
    private final SimpleStringProperty artistName;
    private final SimpleStringProperty rating;
    private final SimpleObjectProperty totalPlayCount;

    public RecordBySong(Integer rank, String songName, String artistName, String rating, Group totalPlayCount) {
        this.rank = new SimpleObjectProperty(rank);
        this.songName = new SimpleStringProperty(songName);
        this.artistName = new SimpleStringProperty(artistName);
        this.rating = new SimpleStringProperty(rating);
        this.totalPlayCount = new SimpleObjectProperty(totalPlayCount);
    }

    //アクセサは省略。
}

あとがき

このアプリ、サーバ(Google App Engine)との連携やiTunesプレイリストの出力など、結構色々と頑張ってるつもりですw iTunesユーザーの方でご興味を持たれた方は、ぜひ使ってみて頂きたいです♪

また、メイン技術がサーバーサイドな僕にとってのJavaFXは、クライアントアプリを作る際の貴重な手段。 趣味もかねて、Twitterクライアントアプリなんかを作ってみてもおもしろいかも!なんて、考えています(^^)。

・・・ふぅ。カレンダーのレベル下げ役、これにて終了です。(^^;) お目汚し、失礼しました! 明日はtaizさんによるAdvent Calendar記事です!お楽しみに!