FX GlassFish Monitorの解説 (見映え編)

Java The Nightのデモでお見せしたFX GlassFish Monitorの作りについての解説です。まずは反響が大きかった見映えのところから説明したいと思います。

一番目に付いたのはウィンドウ枠だと思います。OSのウィンドウ枠は全く見えず、周囲が何か光っていますね。
JavaFXではコンポーネントにドロップシャドウエフェクトを追加することができ、今回もそれを利用しているのですが、OSのウィンドウ枠に相当する Stage クラスには適用することはできません。

そこで次のような方法で実現しました。

  • Stage 及び Scene は透明にする。
  • その上に、ドロップシャドウエフェクトを効かせた Rectangle を貼り付ける。
    • この Rectangle の大きさは Scene よりシャドウの幅の分だけ小さくする。
  • その上にレイアウトコンテナ (BorderPaneAnchorPane) を貼り付ける。

図示するとこんな感じです。

メインウィンドウ (監視項目のツリーが並ぶウィンドウ) について、Stage の上に Scene を組み立てている部分のJavaコードと、FXML のコードを示しておきます。

    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("view/MainView.fxml"));
        loader.load();
        Parent root = loader.getRoot();
        MainViewController controller = loader.getController();
        controller.setParentStage(stage);
        Scene scene = new Scene(root, 924, 700, Color.TRANSPARENT);
        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);
        stage.show();
    }
<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="924.0" xmlns:fx="http://javafx.com/fxml" fx:controller="aoetk.fxglassfishmonitor.view.MainViewController">
  <children>
    <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#1e90ff00" height="690.0" stroke="$x1" strokeType="INSIDE" width="914.0">
      <effect>
        <DropShadow blurType="GAUSSIAN" spread="0.7">
          <color>
            <Color blue="0.878" green="1.000" red="0.000" fx:id="x1" />
          </color>
        </DropShadow>
      </effect>
    </Rectangle>
    <BorderPane fx:id="containerPane" onMouseDragged="#handleMouseDragged" onMousePressed="#handleMousePressed" prefHeight="200.0" prefWidth="200.0" styleClass="container">
      <center>
        <ScrollPane prefHeight="200.0" prefWidth="200.0">
          <content>
            <Pane fx:id="drawRegion" prefHeight="200.0" prefWidth="200.0" />
          </content>
        </ScrollPane>
      </center>
      <top>
        <HBox fx:id="boxTitle" prefHeight="50.0" prefWidth="200.0">
          <children>
            <Text fill="$x1" fontSmoothingType="LCD" stroke="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="FX GlassFish Monitor" HBox.hgrow="NEVER">
              <font>
                <Font name="System Bold Italic" size="28.0" />
              </font>
            </Text>
            <Region prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
            <Button fx:id="btnExit" mnemonicParsing="false" onAction="#handleBtnExitAction" styleClass="close-button" text="Exit" HBox.hgrow="NEVER" />
          </children>
          <padding>
            <Insets bottom="20.0" left="10.0" right="10.0" top="10.0" />
          </padding>
        </HBox>
      </top>
      <StackPane.margin>
        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
      </StackPane.margin>
    </BorderPane>
  </children>
  <stylesheets>
    <URL value="@../asset/style.css" />
  </stylesheets>
</StackPane>

Rectangle とレイアウトコンテナ (BorderPane) の貼り合わせには StackPaneを使っています。
StackPane にマージンを指定していて、その上に置いたコンテンツがドロップシャドウの幅の分だけ隙間を空けるようにしています。

BorderPane の onMousePressed と onMouseDragged にイベントハンドラを設定しています。これは Stage を透明にしたために、そのままではウィンドウ枠が消えてウィンドウの移動ができなくなるため、自分でドラッグ移動のイベントを実装しているのです。

スタイルシートはこんな感じです。

.root {
    -fx-base: #212020;
}

.container {
    -fx-background-color: rgba(0, 0, 0, 0.7);
}

.close-button {
    -fx-border-color: white;
    -fx-border-width: 2px;
    -fx-border-radius: 5px;
    -fx-background-color: transparent;
    -fx-text-fill: white;
    -fx-font-size: 16px;
    -fx-cursor: hand;
}

container クラスがレイアウトコンテナに適用するもので、色を黒で透明度を 70% に設定しているだけです。
close-button クラスはExitボタンに適用したスタイルで、背景を透明にしてあのような見た目にしているのが分かりますね。

と言うわけで見映えについての解説でした。大して手数を掛けずに実現可能だと言うことが分かりますね。

コード全体についてはもう少し整理してからGithubのURLを公開する予定です。もうしばらくお待ちを。

おまけ

解説に使った図はScene Bulderを使って描きました。が、やっぱりお絵かきにはまだまだ機能不足ですね。Rectangle を平行四辺形に変形する部分は直接FXMLを手打ちしました...。

JavaFX Advent Calendar 2012 26日目 GroovyのVetoableを使ったサンプルをJavaFXのバインディングを使って実装してみる

このエントリはJavaFX Advent Calendarの26日目のエントリとなります。前日は id:skrb / @ さんによる「JavaFX で Merry Christmas!」です。25日を過ぎちゃいました。まさか1日目と最終日を担当することになるとは。
25日分を突破するわ、海外のJavaFXエンジニアの方々に注目されてエントリを英語に翻訳するようになるわで、まさかの盛り上がりとなりましたねえ。

さて、今回のエントリですが、同じくAdvent Calendarの13日目に@さんがJavaFXでGroovyのVetoableが機能するか試してみたというエントリを書かれて、GroovyのBindable and Vetoable transformationを利用して、Model、View、Controllerを綺麗に分離する方法を紹介されていました。
ですが、JavaFX自体にもModel、View、Controllerを綺麗に分離するためのバインディングと呼ばれる機能が用意されています。このエントリではその機能を用いて上記ブログエントリのサンプルを再実装することで、バインディング機能について紹介してみたいと思います (なので先にみけさんのエントリを読んでおいてくださいね) 。

JavaFXバインディングとは?

バインディングとは、簡単に言うとあるデータと別のあるデータを結び付ける機能のことです。あるJavaBeansオブジェクトのプロパティが変化したら自動的に他のJavaBeansオブジェクトにその値を伝達できるようにします。機構の実現にはObserverパターンを利用しています。
この機構を用いることで、Modelオブジェクトのプロパティに対して、Viewに使用するコンポーネントのプロパティを「バインド」しておけば、Modelの値が変化したら自動的にViewの表示内容が変わるようになり、典型的なMVCパターンの構造を組むことができるようになりますね。

WPF等のXAMLフレームワークFlexなど、最近のGUIフレームワークは大抵このバインディング機構が備えられています。JavaScriptライブラリでもknockout.jsのようにバインディング機構を備えたライブラリが色々出てきています。

JavaFXバインディングは他のGUIフレームワークには余り見られない強力な機能が備えられています。ただし、既存のJavaBeans仕様を拡張したAPIを用いて実現しているため、記述は少々面倒だったりします。サンプル実装を通して紹介していきたいと思います。

Veotableの例をバインディングを用いて実装したサンプル

それでは実際にバインディングを用いて再実装したサンプルを示します。全ソースコードはgistにアップしています。
https://gist.github.com/4363405

まずはModelからです。

public class Person {
    // nameプロパティ
    private StringProperty name = new SimpleStringProperty();
    // 外部に公開するプロパティ
    public StringProperty nameProperty() {
        return name;
    }
    // JavaBeans仕様と互換を持たせるためのgetter、setter
    public final String getName() {
        return name.get();
    }
    public final void setName(String name) {
       this.name.set(name);
    }

    // messageプロパティ (read only)
    private ReadOnlyStringProperty message = new ReadOnlyStringPropertyBase() {
        {
            name.addListener(new InvalidationListener() {
                // nameプロパティの変化を観察し、変化したらこのプロパティも値が変化したことを通知する
                @Override
                public void invalidated(Observable o) {
                    fireValueChangedEvent();
                }
            });
        }
        @Override
        public String get() {
            String value = Person.this.getName();
            if (value.length() > 0) {
                return "Hello I'm " + value + ".";
            } else {
                return "";
            }
        }
        @Override
        public Object getBean() {
            return Person.this;
        }
        @Override
        public String getName() {
            return "message";
        }
    };
    // 外部に公開するプロパティ
    public ReadOnlyStringProperty messageProperty() {
        return message;
    }
    // JavaBeans仕様と互換を持たせるためのgetter
    public final String getMessage() {
        return message.get();
    }
}

まずは簡単な name プロパティから見てみましょう。なにやらいつものJavaBeansのプロパティとはちょっと違いますね。

    // nameプロパティ
    private StringProperty name = new SimpleStringProperty();
    // 外部に公開するプロパティ
    public StringProperty nameProperty() {
        return name;
    }
    // JavaBeans仕様と互換を持たせるためのgetter、setter
    public final String getName() {
        return name.get();
    }
    public final void setName(String name) {
       this.name.set(name);
    }

JavaFXのプロパティは **Property 型 ( ** にはラップする値のクラスが入る) でラップします。そしてこの Property 型の get/set メソッドで値のやり取りをします。
外部に公開するときは "(プロパティ名)Property()" という名前のメソッドを宣言します。また、既存のJavaBeans仕様と互換性を持たせたい場合はこのように getter/setter も用意しておきます (ただし、メソッド内では Property を通してやり取りする) 。*1
Property 型は abstract クラスなので、継承して get/set 時の処理を実装することになりますが、単純に値の受け渡しだけをする場合は Simple**Property というデフォルト実装が用意されています。
この Property オブジェクトが「バインド可能」なオブジェクトになるわけです。

続いて message プロパティです。こちらは外部から値を設定するのではなく、name プロパティの値に依存したプロパティとなっています。なので、リードオンリーなプロパティとして実装してみました。

    // messageプロパティ (read only)
    private ReadOnlyStringProperty message = new ReadOnlyStringPropertyBase() {
        {
            name.addListener(new InvalidationListener() {
                // nameプロパティの変化を観察し、変化したらこのプロパティも値が変化したことを通知する
                @Override
                public void invalidated(Observable o) {
                    fireValueChangedEvent();
                }
            });
        }
        @Override
        public String get() {
            String value = Person.this.getName();
            if (value.length() > 0) {
                return "Hello I'm " + value + ".";
            } else {
                return "";
            }
        }
        @Override
        public Object getBean() {
            return Person.this;
        }
        @Override
        public String getName() {
            return "message";
        }
    };
    // 外部に公開するプロパティ
    public ReadOnlyStringProperty messageProperty() {
        return message;
    }
    // JavaBeans仕様と互換を持たせるためのgetter
    public final String getMessage() {
        return message.get();
    }

リードオンリーなプロパティの場合、オブジェクトの内部状態に応じた値を返すことになるので、多くの場合作り込みが必要になります。JavaFXでリードオンリーなプロパティを実装する方法は大きく分けて次の2つの方法があります。

  1. abstract な ReadOnly**PropertyBase を継承して実装する。
  2. 読み書き可能な ReadOnly**Wrapper 型のオブジェクトを内部で保持し、外部にはその getReadOnlyProperty() メソッドで返すオブジェクトを公開する。

ここでは前者の方法を用いて実装してみました。バインド可能なプロパティを用意する場合、次の処理を実装する必要があります。

  • 値の読み書きと、それに付随する処理。
  • 公開する値が変化した場合、それを外部に通知する処理。

まず、初期化処理で InvalidationListenerなるものを name プロパティに対して登録していますね。これは name プロパティの値の変化を観察しています。message プロパティは name プロパティの値に依存していますから name プロパティの変化を検知した場合、このように fireValueChangedEvent() メソッドをコールして、自身に登録されたリスナに対して変更を通知します。
そして、3つのabstractメソッドを実装しています。1つめの get() が値を返す処理を実装していますね。name に何も入っていない時は空文字を返し、値が入っているときは "Hello I'm " を付けて返しています。
getBean() メソッドはプロパティのオーナーのインスタンス (ここでは Person インスタンス) を返し、getName() はプロパティ名を文字列で返します。

注目すべき点は InvalidationListener です。これは観察しているプロパティの値が変化したことだけを知らせます。これがミソです。
実はJavaFXバインディングでは遅延評価が基本となっており、実際に値が取り出された時に初めて算出するようになっています。
今回は単純に1つの値を受け渡すだけですが、実際にバインディングを使用するときは複数の値を組み合わせて算出したり、あるいは取得した値から複雑な描画処理を行ったりする可能性があります。
そこで、JavaFXでは観察されている値が変化したときは、変化したことを知らせるフラグだけを立てておき、本当にその値が必要とされるときになって、フラグが立っていたら実際の値を算出するようになっているのです。実際に値を取り出す前にデータソース側が何度変化していても、計算するのはその時の1回だけです。
私はJavaFXのこの点がすごく気に入っています。以前Flexで開発していたことがあったのですが、Flexバインディングは即時評価であるため、濫用するとパフォーマンスの低下を招きやすく、折角の機能が余り使えなくて残念な思いをしていました。

さて、ここまでのコードは長かったですが、プロパティ側の準備ができたら後は簡単です。まずはFXMLから。

<AnchorPane id="AnchorPane" prefHeight="200.0" prefWidth="320.0" xmlns:fx="http://javafx.com/fxml" fx:controller="aoetk.bindingsample.SampleController">
  <children>
    <Label layoutX="35.0" layoutY="31.0" text="Your name" />
    <TextField fx:id="txtName" layoutX="129.0" layoutY="28.0" prefWidth="177.0" />
    <Label fx:id="name" layoutX="35.0" layoutY="133.0" />
    <Label fx:id="message" layoutX="129.0" layoutY="133.0" />
  </children>
</AnchorPane>

特に変わったところはありませんね。注目して欲しいのはmike_neckさんのエントリでは TextField にイベントを定義していましたが、ここでは定義していません。
続いてControllerです。

public class SampleController implements Initializable {
    @FXML
    private TextField txtName;
    @FXML
    private Label name;
    @FXML
    private Label message;

    private Person person;

    // 初期化時に必要なバインドを行う
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        person = new Person();
        person.nameProperty().bind(txtName.textProperty());
        name.textProperty().bind(person.nameProperty());
        message.textProperty().bind(person.messageProperty());
    }
}

初期化処理を行う initialize() メソッド内でバインディングを行っています。値の変更を反映する側のプロパティの bind() メソッドを呼び出し、引数に変更元のプロパティを渡します。
まず最初に、Person#name に対して、テキストフィールドの text プロパティをバインドしています。こうすることで、テキストフィールドへの入力が name プロパティに勝手に反映されることになります。
次の2行でラベル name に対して Person#name を、ラベル message に対しては Person#message をバインドしています。
これだけでテキストフィールドの入力内容をラベルに反映する処理を実装することができました!

ちなみにControllerでバインドを行っているのは、現時点ではFXMLは単純なバインディングしか記述できないためです (FXML内で宣言した変数のみバインドが書ける) 。将来的には複雑なバインドも記述できるようにするそうです。

ともあれ、バインディングを活用すれば容易にModelとViewの値を結びつけ、しかも疎結合にできる (ModelはViewの事を一切知りません) ことがこのサンプルで分かると思います。

Property型についての補足

なお、プロパティの定義に使った **Property 型は **Expression 型を継承しています。Expressionという名前から想像できるように、様々な演算を行うためのメソッドが用意されています。これを利用することで、バインドする値を様々に加工したり、複数の値を組み合わせてバインドすることができるようになります。
これを利用すると、先ほどの例を次のようにして同じ機能を実現することが可能です。*2

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        person = new Person();
        person.nameProperty().bind(txtName.textProperty());
        name.textProperty().bind(person.nameProperty());
//        message.textProperty().bind(person.messageProperty());
        message.textProperty().bind(
                new When(person.nameProperty().isEqualTo(""))
                    .then("")
                    .otherwise(new SimpleStringProperty("Hello I'm ").concat(person.nameProperty()).concat("."))
        );
    }

流れるようなインターフェースで条件分岐やら文字列の結び付けが可能になっていることが分かります。ちょっと見辛いですけどね。
このようにある程度複雑なプレゼンテーションロジックも Expression 型の備えるメソッドで実現が可能になっています。詳しくはJavaFXのAPIドキュメントで Expression 型のメソッドを確認してみてください。

まとめ

これまで見てきたように、JavaFXには強力なバインディング機構があり、コンポーネント間の関係を疎結合にしつつ、関連する値を結びつけることが可能になります。遅延評価や流れるインターフェースを用いた演算など、なかなか強力な機能が備わっています。
難点はちょっと記述が面倒な点ですね。このあたりはJavaの表現力の限界なので致し方ないところではありますが。GroovyFXScalaFXでは言語の表現力を活かして書きやすくしてくれています。個人的にはScalaFXのバインディングの書き方は分かり易くていいなあと思っています。*3

*1:既存のJavaBeans仕様を満たす必要がない場合は省略するのもありです。

*2:最終的に実現する機能は同じですが、意味合いは大きく異なることに注意してください。この例では文字列を結合する処理がプレゼンテーション層に存在することになります。対して message プロパティを利用する例では、文字列を結合する処理はドメイン層に属することになります。

*3:自分のこのエントリでScalaFXのバインディングについて触れています。

Developing multi-touch application in JavaFX

This blog entry is the first entry in Japan JavaFX Advent Calendar 2012.

I made a presentation about multi-touch support in JavaFX at 8th Japan JavaFX workshop. I write additional information for the presentation in this entry.

Presentation material is below.

Developing multi-touch application in JavaFX from Takashi Aoe

And source code of sample application is below.
https://gist.github.com/4143183

Additional information for multi-touch API in JavaFX

JavaFX2.2 introduces API related multi-touch. I was very surprised when Developer Preview released because the description of the API has been added to JavaDoc casually without notification in release notes.

Multi-touch support is described as follows in the entry entitled "What's new in JavaFX 2.2" in The JavaFX Blog.

Multi-touch support for touch-enabled devices. As of today this is mostly relevant for desktop-class touch screen displays and touch pads, this will enable the adoption of sophisticated UIs on embedded devices running Java SE Embedded on ARM-based chipsets, such as kiosks, telemetry systems, healthcare devices, multi-function printers, monitoring systems, etc. This is a segment of the Java application market that is usually overseen by most application developers, but that is thriving.

I think main target of multi-touch support is embedded devices. It's my opinion, Oracle is probably going to be taking the initiative in the development of embedded devices user interface before Android move into the same domain.
Of course, I think it is intended for the development of iOS/Android/WindowsPhone/WindowsRT application.

As I explained in the workshop, TouchEvent handling the basic touch operation and GestureEvent handling more abstract events are added to javafx.scene.input package.
And properties to register a handler for these events are added to Scene and Node.

The following is a list of items I didn't speak for a time limit in workshop.

  • About TouchEvent
    • If you touch multiple points, eventSetId is allocated to individual Event.
    • Ctrl, Alt, Meta and Shift key combination can be detected.
    • It implements copyFor() method that copy event to other nodes.
    • In order to get the number of touch points, use getTouchCount() method.
    • TouchPoint class stores the information of touch point. It implements following methods.
      • getX()/getY() method to get the position of the touch point relative to the origin of the event source.
      • getSceneX()/getSceneY() method to get the position of the touch point relative to the origin of the Scene.
      • getScreenX()/getScreenY() method to get the absolute position of the touch point on screen.
      • There is a grabbing API to modify event target and you can switch by using grab() method ... I guess. (Sorry, I have not investigated enough.)
  • About RotateEvent
    • If the trackpad supports multi-touch, you can fire this event on the trackpad. When the event delivered on the trackpad, the mouse cursor location is used as the gesture coordinates.
  • About ScrollEvent
    • You can fire this event by mouse wheel. But only SCROLL event is delivered.
    • Use the deltaX/deltaY property to obtain the amount of scroll usually, but use the textDeltaX/textDeltaY property when the text-based component. The textDeltaXUnits()/textDeltaYUnits() method determine how to interpret the textDeltaX and textDeltaY values.
    • In order to get the number of touch points, use getTouchCount() method. You can implement multi-finger scrolling by using this.

Additonal information for sample application

I showed three samples in workshop. There are videos that had been recorded in preparation to the failure of the demonstration.

  • TouchEvent sample. Draw a circle on touch point. The circle can be moved with a touch and can be erased with a double tap.

  • Demonstration of Scroll/Rotate/ZoomEvent. I demonstrated you can move, transform the Rectangle object easily by using the values obtained from these events.

  • Demonstration of scrolling support for JavaFX control. I used ListView control and Pagination control introduced in JavaFX2.2. These controls supports scrolling operation without any coding.

I wrote compressed souece code in the presentation material because of space limitation. So I hope you check the source code uploaded to gist.
I describe below additional explanation.

  • Node class has properties scaleX/Y, rotate, translateX/Y for translation or transformation. Movement amount obtained from GestureEvent can be applied directly to these properties.
  • I set a list of font families supported by the system obtained from javafx.scene.text.Font.getFamilies() method to Pagination and ListView control.
  • The contents of the page displayed in Pagination control are Labels in VBox control as follows. Pagination control is my favorite because its API is performance-friendly.
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        fonts = Font.getFamilies();
        pagination.setPageCount(fonts.size() / FONTS_PER_PAGE);
        pagination.setPageFactory(new Callback<Integer, Node>() {
            @Override
            public Node call(Integer idx) {
                return createPage(idx);
            }
        });
        lvFonts.setItems(FXCollections.observableArrayList(fonts));
    }

    private VBox createPage(int idx) {
        VBox box = new VBox(5.0);
        int page = idx * FONTS_PER_PAGE;
        for (int i = page; i < page + FONTS_PER_PAGE; i++) {
            Label lblFont = new Label(fonts.get(i));
            box.getChildren().add(lblFont);
        }
        return box;
    }

As you see, API that enables to develop multi-touch application easily have been added in JavaFX2.2. Unfortunately, fully available environment is Windows8 devices only (furthermore, only in desktop mode), Let's use everyone!

JavaFX Advent Calendar 2012 1日目 JavaFXでのマルチタッチアプリケーション開発

JavaFX Advent Calendarの1日目を担当することになってしまった@ / id:aoe-tkです。

実は前日の第8回JavaFX勉強会にて、JavaFXでのマルチタッチプログラミングについての発表を行いました。このエントリではその内容のフォローアップをしたいと思います。
当日の発表資料は以下です。

JavaFXでマルチタッチプログラミング from Takashi Aoe

また、デモに使ったサンプルアプリケーションのコードもgistにアップしています。
https://gist.github.com/4143183

JavaFX2.2で追加されたマルチタッチ関連のAPIについての補足

発表でもお話ししたように、JavaFX2.2になってタッチ関連のAPIが追加されました。Developer Previewの時に、特にリリースノートでの通知がなく、さりげなくJavaDocに追記されていたので驚いたのを覚えています。
JavaFX2.2新機能紹介ページ (日本OracleのエンベデッドJavaチームによる日本語訳です) では次のように紹介されています。

タッチ操作可能な機器のためのマルチタッチサポート。これは現段階では主にデスクトップクラスのタッチスクリーンやタッチパッド向けですが、将来的にはARMベースのチップ上にJava SE Embeddedが搭載されているさまざまな組み込み機器に洗練されたUIを導入することを可能にします。例えばキオスク端末、テレメトリシステム、ヘルスケア機器、多機能プリンタ、監視システムなどです。これらは、多くのアプリケーション開発者にはまだあまり注目されていないJavaアプリケーションの市場セグメントですが、近年活況を呈しつつあります。

当日の発表でも少し触れましたが、主に組み込み機器をメインターゲットにしているように見受けられます。こういったセグメントにもこれからAndroidが進出してくるでしょうから、その前にしっかり押さえておこうとOracleは考えているのでしょう。
もちろんiOSWindows8/RT/Phone、Android向けの開発も視野には入れているとは思いますが。

さて、追加されたAPIの内容についてですが、発表でも説明したように、javafx.scene.input パッケージに基本的なタッチ操作を扱う TouchEvent と、タッチ操作を組み合わせたより抽象度の高い GestureEvent というイベントが追加されています。
そして、Scene 及び Node にこれらイベントに対するハンドラを登録するためのプロパティが追加されています。

以下に時間の関係で当日の発表で話せなかったことを列挙します。

  • TouchEvent について
    • 複数点タッチされた場合、個々のEventには eventSetId が割り振られます。
    • control、alt、meta、shiftキーとのコンビネーションも検出可能です。(isAltDown() とか isShiftDown() といったメソッドがある)
    • 他のノードにイベントをコピーする copyFor() というメソッドがあります。
    • 何点同時にタッチされているかは getTouchCount() メソッドで取得できます。
    • タッチポイントの情報は TouchPoint クラスに格納されますが、TouchPoint には次のようなAPIが用意されています。
      • getX()/getY() はタッチの対象となる Node オブジェクト上の座標を、getSceneX()/getSceneY() は Scene 上の座標を、getScreenX()/getScreenY() は画面上の絶対座標を取得でします。
      • タッチポイントのイベントターゲットとなる Node オブジェクトを切り替えることのできる grabbing API というものがあり、grab() メソッドで切り替えることができる...らしいです。(ごめんなさい、まだ調べ切れていません)
  • RotateEvent について
    • マルチタッチをサポートしているトラックパッドならば、トラックパッドでもこのイベントを起こすことができます。トラックパッドで起こした場合はマウスカーソルの位置が基準になります。
  • ScrollEvent について
    • じつはマウスホイールによるスクロールでもこのイベントは発生します。ただし、マウスホイールの場合は SCROLL タイプのイベントのみ発生します。
    • 移動量については基本的に deltaX/Y で見ますが、テキスト系コンポーネントの場合は textDeltaX/Y でも取得可能なようです。単位は行単位とページ単位があるようで、getTextDeltaUnits() でどちらの単位なのかを取得できます。
    • getTouchCount() でタッチ数を取得できます。これを使うことで2本指スクロールや3本指スクロールが実現できます。

デモアプリケーションについての補足

デモでは次の3つのサンプルをお見せしました。デモが失敗したときに備えて録画しておいた動画がありますので、それも一緒にアップしておきます。

  • TouchEvent を使い、タッチされたポイントに対してサークルを描画するデモ。描画したサークルはタッチで移動でき、ダブルタップで消去可能。

  • GestureEvent のうち、Scroll/Rotate/ZoomEvent を利用したデモ。描画した Rectangle に対して、これらイベントから取得した値を使って、簡単に移動、変形が可能であることを示しました。

  • JavaFXのコントロールのスクロール対応のデモ。ListView コントロールとJavaFX2.2になって追加された Pagination コントロールを使いました。これらは何もしなくてもスクロール操作に対応しています。

スライドではスペースの都合上、コードをかなり圧縮して載せていました。上のgistにアップしたコードの方を良く確認してもらえたらと思います。
以下にちょっと補足を。

  • Node には移動や変形を行うための scaleX/YrotatetranslateX/Y といったプロパティがあります。GestureEvent で取得できる移動量はこれらプロパティにそのまま適用できるようになっています。
  • Pagination コントロールや ListView コントロールには、javafx.scene.text.Font.getFamilies() メソッドを使って取得した、システムがサポートしているフォント名の一覧を渡しています。
  • Pagination コントロールに表示するページの内容は次のように VBox を作って、その中にラベルを並べています。Pagination コントロールはパフォーマンスに考慮した作り方ができるようなAPIになっていて、個人的に気に入っています。
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        fonts = Font.getFamilies();
        pagination.setPageCount(fonts.size() / FONTS_PER_PAGE);
        pagination.setPageFactory(new Callback<Integer, Node>() {
            @Override
            public Node call(Integer idx) {
                return createPage(idx);
            }
        });
        lvFonts.setItems(FXCollections.observableArrayList(fonts));
    }

    private VBox createPage(int idx) {
        VBox box = new VBox(5.0);
        int page = idx * FONTS_PER_PAGE;
        for (int i = page; i < page + FONTS_PER_PAGE; i++) {
            Label lblFont = new Label(fonts.get(i));
            box.getChildren().add(lblFont);
        }
        return box;
    }

このようにJavaFX2.2ではマルチタッチを容易に開発可能なAPIが追加されています。今のところまともに使える環境はWindows8くらい (しかもデスクトップモード) なのがちょっと悲しいですが、みなさんもどんどん使ってみてください。

第7回JavaFX勉強会でLTしてきました

ちょっと遅くなりましたが先日行われた第7回JavaFX勉強会に参加し、LTもしてきましたので、こちらで軽く報告します。

自分の発表資料は以下の通りです。
http://www.slideshare.net/takashiaoe/macjavafx-at-7javafx-13518044

発表内容はMacでのJavaFXの開発や配備についてのお話しです。ちょっとMacユーザーならではの自虐ネタも交えてw
内容は結構絞ったつもりですが、それでもLTの5分間となると駆け足でしゃべるしかないですね。もう少し時間があればJARバンドラーのデモをやりたいと思っていました。
JARバンドラーについては後日このブログでサポートするエントリでも書こうかな。
発表でも最後に話しましたが、MacでもJavaFXの開発環境はほぼ整いました。と言うことでMacユーザーの皆様もJavaFXでの開発を楽しんでくださいね。
JDK7がLionしかサポートしていないのはつらいですが、Macの世界はいつもこんな感じですからねえ...。iCloudとかもSnow Leopard以前はガン無視ですし。

では他の方々の発表についてもそれぞれ軽くコメントを。

  • 片貝さんのNetBeansについての発表 (発表資料)
    • NetBeans7.2ではSceneBuilderとの連携強化や、CSSの補完が効くようになったり (個人的にはこれは嬉しい!) 、とJavaFXの開発環境としての強化が順調に図られているようです。あとはFXMLの補完が効くようになってくれれば...。
    • 地味にSwingベースのJavaFXアプリケーションプロジェクトが作成できるようになっているのは嬉しいですね。今後JavaFXが最も良く使われるのがSwingのリプレースだと思いますが、印刷などまだまだAWT/Swingをカバーできていないところが多いので、これの重要性は高いと思っています。
    • みんなここぞとばかりにNetBeansへのリクエストを出してましたw 自分も懇親会でいくつかリクエストしたような。
  • 櫻庭さんのSceneBuilderについての発表 (発表資料)
    • SceneBuilderについて、デモを中心にした説明でした。相変わらずJavaFXで作った資料のクオリティが高い! 驚いていた参加者も多かったように見えます。
    • NetBeansとSceneBuilderの分担が少し中途半端とのことですが、これはMaciOSの開発でのXCodeとInterfaceBuilderの分担を真似たからこうなっているのかなと個人的には思っています。
    • 質問が沢山飛んでいました。そのやりとりの中でカスタムコンポーネントのパレットへの登録ができない、と言うの話が出てきましたが、これは確かに早く実装して欲しいですね。
      • 質問の中に、コンポーネントの整列はないか? と言うのがありましたが、コンポーネントを選択してGridPaneを適用することはできるので、これで代替できそうだと後で気付きました。
      • それからSceneBuilderは結構すいすい動きますよー。機能を純粋にFXMLの編集だけに絞っているというシンプルな作りだからというのもあるでしょうが。
  • mike_neckさんのFx-JS-JUnitについてのLT (発表資料)
    • ニャル子さんネタ全開の怒濤のスライド! これはやられましたw トークも軽妙で場の雰囲気持って行っちゃいましたね。
    • でも中身は普通に数十分掛けてやるようなとても濃い内容です。JavaFXのWebEngineを使ってJavaScriptのテストに活用するという発想は面白いと思います。期待しています。
  • taiz77さんの画面遷移ライブラリのLT
    • 大学院でJavaFXを研究の題材にされているそうです!
    • JavaFXのアプリケーションフレームワークを考えておられるようで、現時点ではJavaFXに対するフレームワークというのは発表でも紹介されていたJFX Flowくらいしか見当たらないので。こういう取り組みは貴重だと思っています。
    • ただ、タイムアップでそのフレームワーク自体の紹介までたどり着かなかったのが残念でした。
  • kimukou2628さんのGriffonについてのLT (発表資料)
    • デスクトップGUI向けGrailsのような位置付けを目指しているフレームワークであるGriffonについての紹介でした。
    • 先にJavaFXではまだフレームワークらしきものはない、と書きましたが、このGriffonも有望だなーと思いました。FXMLとの連携も考えているのがいいですね。
  • 寺田さんのJavaFXエンタープライズアプリケーションについてのLT (発表資料)
    • 何とApplication Client Containerを使ってJavaFXとサーバーのEJBを連携させる方法についての発表でした。ある意味今後JavaFXが最も使われそうな用途のようにも思えます。
    • 実は自分は過去のSI案件でSwingアプリケーションとサーバーのEJBを直接RMIで接続する形態の開発に関わったことがあります。ですがそれは古のJ2EE1.3の時代の話。JavaEE6でのEJBへのリモート接続についての情報はとても貴重だと思いました。
      • リモートEJBのインジェクション対象のフィールドはstaticである必要があったんですねえ...。

このような感じで他の方々の発表も内容が多岐にわたり、充実していました。発表資料へのリンクを張っておいたので、是非見て頂ければと。

回を重ねるごとに参加人数が増えてきているのに驚きです。Swingをやっていた方はもちろん、Flexのような他のRIAの世界から来た感じの方とかもいましたねえ。
懇親会も参加者が多くて、色んな方と交流ができて楽しかったです。どんどん盛り上がってきて嬉しい限りです。

第3回RIAアーキテクチャ研究会に参加してきました

RIAアーキテクチャ研究会 第3回セミナーに参加してきましたので、参加レポートを軽くまとめたいと思います。
Microsoft系の勉強会だったのですが、実は自分は余りMS系の開発に縁が無かったりします。前回Visual Studioを開いたのは8年くらい前かな...。
でも以前のエントリにも書いたように、ここ最近GUIアーキテクチャにはすごく関心があり、しかもこの手の技術はいつもMSの世界が先行していた印象を持っていたので、きっと色々学ぶことがあるだろうと思い、参加することにしました。
周りを見渡したらみんなWindowsノートPCで (Win8タブレットを持っている人もいました。うらやましー。) 、MBA持ち込んで参加した自分はちょっとアウェー感を感じました。(^^;)

以下にメモったことのサマリをまとめます。なにぶんMSの世界のGUI技術 (WPF/SL/WP/WinRT) には余り詳しくないので、不正確なところがあるかもしれません。

C#次世代非同期処理概説 by 河合さん (@neuecc)

.NETで用意されている非同期処理のパターンの解説と、C#5.0で言語レベルでサポートされる非同期処理についての解説でした。

非同期処理とは何か?なぜ重要か?
  • 並列は1つの処理をばらして処理すること。並行は個別の処理を並行して実行すること。
  • 非同期処理はブロックしない処理のこと。
  • クラウドと並んでバズワード
  • UIスレッドのブロックを防ぐことはとても大切。
    • WinRTとかは非同期のメソッドしかない。
    • JSはデフォルトそうだし。
  • Webサーバー側では同時接続への対応として使われてますね。
非同期処理を書いてみると...

ASP.NET MVCで作ったサーバーとSilverlightで作ったクライアントでライブコーディング、デモを行っていました。

  • 次期ASP.NETはnodeっぽい書き方ができる!
  • SilverLightアプリからそのWebサービスに接続。
    • 最初のサービスにアクセスして、その結果を受けて次のサービスにサクセスする処理をするとコールバックがネスト。見にくい!
    • 例外処理を書き込むとますますごちゃごちゃに
  • ネストや例外処理がしんどいので、.NETではそれらを解決するためのパターンがある。
非同期処理のパターン
  • APM (Asynchronous Programming Model)
    • 一番プリミティブな方法。
    • Begin**-End**のペアでの処理。
    • コールバックをネストするスタイル。
    • コールバックはUIスレッドじゃないので注意。
    • try-catchのネストはやはり必要。
  • EAP (Event Based Asynchronous Pattern)*1
    • **Async + **Completedのパターン。(デモで書いていたパターンはこれ)
    • 先にハンドラを書くので分かりにくい。
    • UIスレッドへのディスパッチはしてくれる。
  • TAP (Task Based Asynchronous Pattern)
    • いわゆるFuture、Deferredパターン。*2
    • TPLと呼ばれるライブラリを使う。
    • メソッドチェインでどんどんつなげていくスタイル。
    • やはりUIスレッドではないが、ContinueWith メソッドに引数でタスクスケジューラのコンテキストを渡すと、UIスレッドで実行してくれる!
    • 例外処理もContinueWithで。
    • ネストが平らになる!
  • Rx (Reactive Extensions)
    • LINQベース。LINQベースなので合成処理が強力。
    • @ITで連載してるので見てね。
    • すごくLINQっぽい書き方。
    • 例外処理はSubscribeの第2引数で。
    • メソッドチェインでスレッドコントロールも。
    • 拡張メソッドでさらにすっきりと書ける。
C#5.0
  • 言語レベルでサポートが加わる。
  • イベントハンドラに async を宣言しておき、メソッド内の非同期呼び出しに await キーワードを付けると、そこでブロック -> 継続をしてくれる!
  • 同期処理と同じように try-catch で囲める!
  • こっちだとTaskの方が使い易い。
    • Rxもawaitableなので、将来的は対応されるはず。
どれを選択すればいいか?
  • SL4ではRx。
  • SL5では将来を見据えてTaskを。
  • WinRTはTaskを使っていこう。
  • ストリーム処理ではRxがよい。
  • タッチイベントではイベント合成が複雑なのでRxが威力を発揮するのでは?
感想

.NETの世界では言語、フレームワークレベルで非同期処理の実装に対するサポートが充実しているのに驚きました。JavaScriptなど他の世界ではサードパーティのライブラリが乱立している状況です。うらやましい限りです。
C#5.0みたいに言語レベルでもためらうことなくサポートをどんどん入れていくのもらしいです。
MSにとって主軸はあくまでWindowsであり、Windowsの方向性に合わせて言語やフレームワークを進化させていくからこうなるのかなと思いました。土台部分を握っているMSならではの強みですね。*3

UIパターンの捉え方 by 尾上さん (@ugaya40)

日々色々な考え方が登場するUIパターンについて、どのように捉え、自分達の開発に適用していけばいいのか、といったことについてのお話しでした。

設計パターンを学ぶ意義
  • コミュニケーションの手段。
  • パターンには想定された状況がある。
    • プラットフォームに精通していなくても罠にはまりにくくなる。
UIパターンとは?
  • GUIの責任分割のパターン。
  • プレゼンテーションとドメインの分離。
    • テストの容易性。
    • プレゼンにドメインが引きずられない。
    • プレゼンはプラットフォームへの知識が必要。
  • プレゼン層のプラットフォームには意図した用途と設計がある。
  • UIパターンのコンテキスト。
    • プラットフォームを最大限に引き出す。
    • プラットフォームによって決まる。
  • UIパターンはいっぱいある!
    • 原初的なMVCAndroid MVC、原初的なMVP、Passive MVP、原初的なMVVM、MVVM2011...
  • 実装中立?
    • 基本はプラットフォームがあってできる。
    • でも他で用意されたら実装中立に。
UIパターンとプラットフォーム
  • UIパターンとプラットフォームは相互に影響を与えている。
  • プラットフォームはある程度用途や設計 (UIパターン) を想定して作っている。
  • プラットフォームへのフィードバックが発生し、それを受けて新しいプラットフォームが。
  • プラットフォームが変わるとそれに合わせてパターンも変化する。
  • MVCをとってもプラットフォーム別に派生するので乱立するように見える。
サンプルコードのとらえ方
  • サンプルには小規模、大規模、マーケティング向けがある。
  • そもそも設計パターンは実装で苦しまないですむようにするためのもの。設計のインプットの1つにすぎないことに注意する。
  • 小規模なサンプルだと冗長さが目立つだけ。
    • 分かりやすいが、責務を分けられなかったり、ユースケースが限られるので大規模な開発には使えない。
  • 大規模なサンプルは多くのユースケースに対応しているので、きちんと責務分割されている。が、読むの大変。
  • マーケティング重視サンプルは新機能紹介を重視している。実装例としてはとらえない方がいい。
    • DnDで作れるよ、系のサンプルとかこれ。
  • つまりサンプルから学習するのは良くない。
  • 先に概念を学んでからサンプルを見ること。
    • 成り立ちを知る。
  • サンプルコードの意図を意識すること。裏に必ず目標がある。
UIパターンの適用
  • まず概念の調査、理解。
    • まずは公式のドキュメントを当たる。概念が説明されているはず。
    • 責務の把握が重要。
  • サンプルの理解、解釈。
    • 実装要素を把握する。実装要素が何の責務を果たしているのかを把握する。
    • プラットフォームの何の機能を使っているのか?
    • 「この実装要素を使うから○○パターン」という解釈をしないこと!
  • 実装要素の取捨選択。
    • 責務が役割を果たすための実装要素を特定する。
    • なぜ冗長なのかを見る。
  • 重要な観点。
    • プレゼンとドメインが分離できている?
    • 特定の実装要素にこだわっていないか?
  • 責務分けに悩んだら?
    • Mから考えると悩む。MはViewと中間層以外と考える。
    • それがプレゼンテーションなのか?を意識する。
感想

UIパターンに限らず、何らかの技術を学ぶ際の心構えとして大事なことを説明されているなと感じました。
どんなデザインパターンフレームワークも産まれた背景には解決したい問題があったはずで、その背景を最初に知ることが一番大切だと思います。
しかしまあUIパターンは細かく分けると色々ありますね...。

集中と分散 〜 スマートフォンクラウド連携のアプリ開発と分業の実際​ by カブドットコム証券 谷口さん&セカンドファクトリー 東海さん、高尾さん

カブドットコムにて開発中のWindowsPhone + Azure (!) なアプリケーションの開発事例を主軸に、スマートデバイス開発、クラウド開発を取り巻く状況について色んなことを語って頂きました。すさまじいマシンガントークでした。なので以下のメモは色々取りこぼしがあると思います。(^^;)
実開発に基づいた話であるため、とても説得力がありました。

谷口さんのお話
  • いろんな技術が産まれてきました。
    • 必ず背景に課題がある。
    • 早く創って早く辞めるが重要。
  • 5年続かないとブレイクしない。
  • 日本は技術動向3〜5年後の後追い。
    • でも結果としてより深掘りできていると言える。
    • サービス、課題が先行しているのは強み。
  • 金融業界は?
    • ウォーターフォール大好き。顧客第1主義だからIE6捨てられない。
    • 銀行の仕組みは元々クラウドに近い。勘定系を共有したりしている。
    • オープン勘定系出てきた。Windows系結構ある。
  • 足りないもの。
  • Azure。
    • 企業のPaaS。
    • Windows DNAの直系の子孫。
  • WP7。
    • アプリベースじゃなくてステートベース。
  • 金融の世界の変化。
    • ArrowHeadの登場で知覚では追えない。
    • UXの進化。
  • 金融をシンプルをしたい!マーケットに新機軸を!
  • 金融の仕組みはすごく複雑。
    • 情報源様々。更新頻度、タイミングがばらばら。
      • なのでデータ優先。
      • モデル設計、UI/UX設計がすごく難しい。
        • 対応するためにプロジェクト立ち上げた。
  • AzureとWPを活用。
    • Metroは金融との相性がいいのでは。
      • ザッピングに合う。
    • クラウド経由のMVVMの確立。
    • RESTful Webサービスで層を分離。
      • 会社も分ける。RIA部分を2fcさんに。
2fcさんの話
  • UIが人の体験を変える。トイレのエアタオルとかはジェスチャーUI。
    • 「コンピューターが人々の言語を覚える」
  • NUI (Natural User Interface)
    • 人間の行動をそのままインプットに。
  • RIAのハイプカーブ。
    • 2012年は安定期の入り口。
      • マーケットが市場を引っ張っている状況。
  • ゲームニクスの考え。
    • DSが実現。
    • 本来の目的に意識を集中させる。動詞で説明する
    • ゲームの世界ではNUIが基本。
    • デザインからのアプローチではなく、行動から考える。
  • 実開発のお話し。
    • 7つのロール。
    • ワークフロー。
      • 割と基本的なウォーターフォール
      • 設計フェーズで人間中心設計を行い、繰り返し評価。
      • プロト作成までを短期で
    • 1つのUIで複数のプラットフォームは無理。
    • ミッション
      • 情報主体との体験を。アクティビティやステートにユーザーは興味がある。
      • Win8へつなげる。
  • WPの特性を活かした特徴。
    • People Hubから人をピン留めしてアクションにつなげるのが基本。
    • ザッピング感を。トランプの束に見立てる。
    • 複数見せ方。タイルとリスト。
  • クラウドサービス特有のUX。
    • クラウドサービスの特徴。
      • コネクティビティが重要。オンライン、オフラインがある。
        • 分離ストレージを使った。
      • 権限管理。
        • サーバー側での認証と認証トークンを分離。
      • マルチチャンネルへの配信。
    • アプローチ
      • データコネクティビティの管理。
        • ローカル、サービスの同期タイミングの制御。クライアントサイドのストレージを利用。
          • クライアント側のモデル作成。
        • メモリ上のモデルとストレージのモデルは構造を同じ。
        • クライアントにビジネスロジックを持たせない。
          • クライアントに落ちてくるモデルはVM
        • クライアント側コードでのアクセス性を考え、クライアント側のモデルを先に作った。
        • クラサバ間の通信。
          • サーバー側でクライアント向けモデルに変換するサービスをかます感じ。VM側でプレゼンテーション固有のステートが追加されることはある。
          • エラーもモデルに囲む。
      • UIの実装。
        • 基本的に操作からアプローチする。
        • PGが楽なのはプログラムからのアプローチだけど、それだとWhyが置き去りになる。
      • 専門家が分業して作るのがいいと考える。
Azureの話
  • チャートはみんなこだわる!Azure側でF#使ってチャートを作った!
  • オンプレ側では遅延に対する設計は徹底的にやった。
    • Azureでの遅延は結構大きい。
  • オンプレからAzureのキューに対してJSONをどんどん投げ、Worker roleで拾う。
  • とにかく非同期で連携!
    • いつ接続が切れるのか分からない。切れるものなのでHTTPで!
    • 起動したら初期化コマンドなしで動くこと
  • パフォーマンスとしてはとにかくまとめる。データをまとめてやり取りする。
  • AppFabricのキャッシュを使って認証情報をキャッシュ。
  • オンプレからのアップロード。
    • バッファにためて一括アップデート。
    • CPU負荷は上がったが性能目標を達成。
  • とにかく並列化を意識すること!
感想

分量多くてすみません。でもそれくらい情報量があったということです。実開発に基づいたお話しだけに、説得力もありました。ものすごく聞き応えのあるセッションでした。
一番強く印象に残ったのが、iPhoneをはじめとするスマートデバイスの登場によるUXへの変化やクラウド技術の広まりに伴い、我々ソフトウェア開発者の意識の変化が重要であるという点です。後者については今自分が携わっている仕事の関係上、普段からすごく実感していることではありますが。
この変化について行けるか否かで、今後開発者として生き残れるかどうかが決まってきそうな気がします。

MVPVMパターン by 児玉さん (@ mnow)

MSDNマガジンの12月号の記事で紹介された、MVPVMパターンについてのお話しでした。

MVPVMパターンとは?
  • Prismで導入。あとから名前が付いた。
  • MVVMとMVPの組み合わせ。
    • VMは本来あるべきプロパティ公開とコマンドの受付に専念。
    • Pがプレゼンロジックの複雑さに対応。
  • VMはユーザー入力やコマンドをPに渡す。
  • Pがナビゲーション (VやVMの切り替え) をして、Modelに入力を伝える。
  • VMの役割をPに切り出すことになる。
ナビゲーションの問題
  • MVVMではどこがナビゲーション?
  • メッセージ+ビヘイビア。
    • Behaviorを継承して作る。
      • VMからのデータを受け取り、メッセージボックスを出す。
      • 結果をコールバックに返す。
      • ダイアログの場合はWindowを作り、ContextにVMを渡す。
    • VとVMが密結合になる。
  • VM + DataTemplate。
    • XAML側でフラグメントを作り、切り替える。
    • この場合XAMLがナビゲーション。
  • 結局Vにコードを書いていることになる。
    • コードビハインド書いてなくても。
  • ナビゲーションはVとVMの両方を知らないといけない。
  • あとはアプリケーションクラスに持たせちゃうとか。
Presenterの役割 *4
Presenterの校正
  • VやVMも階層構造になるから、Pも階層構造に。
  • パネル等のコンテナだと複数のPを管理。ダイアログ単位に対応するPも。
Presenterの利点
  • ApplicationやVMからプレゼンテーションロジックの解放。
  • VとVMの分離をより容易に。
  • Undo、Redoの実装場所に最適!
  • VMからVをクローズするメッセージは不要。
Presenterの親子管理
  • 親Pが子Pのインスタンスの生成、解放をする。メモリ管理が容易になる!
  • コンテナのスライドの例。
    • 普通はコンテナパネルにストーリーボードを仕掛ける。
    • 両方のVとVMを知っている存在が必要!
      • その役割をPresenterに。
        • 親P、子の2つのPを作ることになる。
感想

MVVMとMVPの長所をうまく組み合わせたパターンと言ったところでしょうか。Controllerを復活させたMVVMと言っても良いかもしれません。とても興味深かったです。
実は話を聞いていて、このパターンはJavaFX2.0にぴったりなのでは?と考えました。
JavaFXには強力なバインディング機構があり、MVVM的なアプローチが向いていると思っています。しかし、ビヘイビアなどに相当する仕組みはないので、ナビゲーション部分はこのようにPresenter (Controller) を用意して担当させると良さそうです。暇なときに何かサンプルでも書いてみよう。

全体を通しての感想

このエントリの分量が多くなったように、情報量がとても多かったです。上にも書いたように普段MS系の開発をしていないのですが、違う世界だからこそ学べることは多いなあと思いました。
体調が良くなかったので懇親会に参加できなかったのがちょっと残念でした。次の機会があればぜひ。

*1:Flash/Flexのイベントハンドリングはこのスタイルですね

*2:自分のブログでも取り上げたjQueryのDeferredオブジェクトもこのパターンですね。

*3:もちろん同じことはAppleにも言えますね。

*4:個人的にはこの責務にとどまるのなら普通にControllerと呼んでも良いように思いました。Presenterのもう1つの役割である、複雑なプレゼンテーションロジックを伴うViewの操作は行っていないようなので。

JavaFXで画像を使ったボタンを作る方法

ちょっとしたメモです。

RIAを開発するとき、デザイナさんと連携して作ることが多いと思います。
ボタンのようなコントロールについても画像として素材を作ってもらい、それを組み込んで使うというシチュエーションも結構あることでしょう。
というわけでJavaFXで画像を使ってボタンを作る方法です。

JavaFXCSSを使って装飾することができます。HTMLのCSS2.1やCSS3をベースにしており、多くの場合同じプロパティを使うことができます。ただし、ベンダープレフィックス "-fx-" を付ける必要がありますが。

画像をボタンの背景として使いたい場合、CSS3のborder-image属性を使うことができます。次のようにCSSを作成します。

.img-button {
    -fx-border-image-source: url('img/btn.png');
    -fx-border-image-slice: 4 5 4 5 fill;
    -fx-border-image-width: 4 5 4 5;
    -fx-border-image-repeat: stretch;
    -fx-border-color: null;
    -fx-background-color: null;
    -fx-text-fill: white;
}
.img-button:hover {
    -fx-border-image-source: url('img/btn_hover.png');
}
.img-button:pressed {
    -fx-border-image-source: url('img/btn_pressed.png');
}
.img-button:disabled {
    -fx-border-image-source: url('img/btn_disabled.png');
}

border-image-slice プロパティを使うことで、画像を枠の部分と中央部分に分割し、枠部分の拡大率を保持したまま中央部分のみを拡大縮小する、いわゆる「9スライス」も指定可能です。fill も指定して、真ん中も塗りつぶすようにします。

border-image-width プロパティでボーダーの幅を指定します。border-image-repeat で stretch を指定すると、中央は引き伸ばされるようになります。
ベンダープレフィックスを取り除けば、HTMLのCSSと同じになることが分かります。

文字の色については -fx-text-fill プロパティで指定します。これはHTMLとは異なるので注意が必要です。
後は、border-color、background-color を null にして、従来の描画を消せばOKです。
hover や pressed といった疑似クラスもHTMLの場合と同じように指定可能です。

Java側では次のような感じでCSSを設定します。

        final Button imgBtn = new Button("これは画像を使ったボタンです");
        imgBtn.setPrefHeight(40);
        imgBtn.getStyleClass().add("img-button");

実行するとこんな感じで画像を背景にしたボタンが表示されます。わざと縦方向も伸ばしていますがちゃんと四隅を崩さずに伸ばしているのが分かりますね。

スクリーンショットじゃ分かりませんが、マウスオーバーやマウスクリックでも用意した画像に切り替わります。

とまあこんな感じで割とHTMLでのCSSと同じように書けそうです。HTMLデザインの技術を活かすことができるというのは結構なアドバンテージではないかと思っています。*1

*1:FlexでもCSSは使えますが、プロパティはHTMLで使うCSSとはかなり異なります。