FXML Controller で Stage を使うためのアレコレ

FXML を使ってアプリケーションを作る場合、ロジック部分は Controller クラスを作って実装します。
その Controller のなかで javafx.stage.Stage オブジェクトを取得する方法をまとめます。

Stage オブジェクトを取得したい理由

すでに存在するウィンドウから新しいウィンドウを表示したい場合、次のような実装をします。

class OpenDialogController {

    void handleOpenButtonAction(ActionEvent event) {
        Stage newStage = new Stage()
        newStage.initModality(Modality.APPLICATION_MODAL)

        // ここでControllerが属する Stage のオブジェクトを渡したい…
        newStage.initOwner(????)    

        // 以下、省略
    }
}

ウィンドウにあたるのが Stage なので、新たなウィンドウを作る場合は Stage オブジェクトを生成します。
その際、Stage#initOwner(Window) を実装するのがお決まりです。このメソッドの引数には新たなウィンドウを開く際の親となるウィンドウ(Window もしくは、そのサブクラスの Stage)を指定します。

ですが、Controller クラスには自分の Stage を取得するための手段が実装されていません。
スーパークラスを実装しているわけではないので、getter があったりすることはない)

おいおい、どこから取ってくるんだ!?
という具合に今回悩みました。

よくあるダイアログを開くサンプルでは…

FXML を使っていないんですよね。
詳しくは述べませんが、FXML を使わない場合、javafx.application.Application クラスを継承したクラスの中ですべて実装できます。
そのため、親となる Stage オブジェクトを生成したものを子となる Stage に渡すのも簡単です。

FXML を使うと、View にあたる FXML のオブジェクト(?)も、Controller クラスのオブジェクトも勝手に作られます。
したがって、Controller クラスに対して Stage オブジェクトを渡す機会がないように思えます。

んで、ググって見ると同じように悩んでいる人たちがいたので参考にしてみました。

方法1:@FXML で対応づけしたノードからたどっていく

OTN Discussion Forums : How to access a stage from a FXML ...

ここのQ&Aを参考にしました。

class OpenDialogController {

    @FXML Button openButton

    void handleOpenButtonAction(ActionEvent event) {
        Stage newStage = new Stage()

        Window window = openButton.getScene().getWindow()
        newStage.initOwner(window)

        // 以下、省略
    }
}

@FXML で対応づけしたノード(javafx.scene.Node)があれば、Node#getScene() で Scene が取得できるので、その Scene#getWindow() で Window オブジェクトを取得することができます。

わかってしまえば、当然そうやってたどれるでしょうが…と思う方法です。

ただ、上記であげた参考サイトにも書かれてますが、エレガントな方法ではないですよね。

方法2:Controller オブジェクトに値を渡す方法が実は存在した

JavaFX: How to get stage from controller during initialization? - Stack Overflow

ここのQ&Aを参考にしました。

class OpenDialogController {

    Stage thisStage

    void setThisStage(Stage stage) {
        thisStage = stage
    }

    void handleOpenButtonAction(ActionEvent event) {
        Stage newStage = new Stage()
        newStage.initOwner(thisStage)

        // 以下、省略
    }
}

Controller クラスは Stage オブジェクトをプロパティとして保持するようにして、setter メソッドで外からセットできるようにします。
こうすれば、Stage オブジェクトを使いたい場合はプロパティから取得すればいいだけになります。

setter を使って Stage オブジェクトをセットするのが Application クラスになります。

class OpenDialogApp extends Application {

    @Override
    void start(Stage stage) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("OpenDialog.fxml"))
        Parent root = loader.load()

        OpenDialogController controller = (OpenDialogController) loader.getController()
        controller.setThisStage(stage)

        stage.setScene(new Scene(root))
        stage.show()
    }
}

FXMLLoader#getController() で FXML に対応する Controller オブジェクトを取得できます。
Controller オブジェクトをつかめたら、あとは Stage を setter でセットしてあげるだけです。

(Stage だけでなく、他のパラメータもこれで Controller に渡せそうです)

通常は次のように FXMLLoader クラスの static メソッドの load() を使って Parent を取ってきますが、それだと FXMLLoader#getController() を使うことができません。FXMLLoader クラスのインスタンスを一度取得するというのがミソですね。

void start(Stage stage) {
    Parent root = FXMLLoader.load(getClass().getResource("OpenDialog.fxml"))

    stage.setScene(new Scene(root))
    stage.show()
}

まとめ

シーングラフの構造とか、きちんと勉強しておくべきですね。
JavaFX で遊んでいるときは「困ったら調べて」の繰り返しなので、断片的な知識だけになってしまってます。

もっといい方法があるようでしたら、どなたかご教授ください。m(_ _)m

Drag&Dropで JavaFX ListView の任意の行に挿入させてみた

前回「JavaFX ListView の要素を Drag&Dropで移動させてみた - Java開発のんびり日記」で Drag&Drop でリスト間の要素を移動させる簡単なサンプルを作りました。

移動できる先がリストの末尾だけなのは物足りなくて、できればドロップした場所に要素を挿入したいですよね。
ということで、ごり押しなところもありますが、今回は任意の行に挿入するバージョンです。


f:id:hideoku:20130530224740p:plain:w450

作ったサンプルの概要

・ListDrag2.fxml(GUI部分)
・ListDragController2.groovy(MVCのControllerあたり)
・ListDragApp2.groovy(アプリ起動)
・style.css(動的なスタイルを使っているので)
の4つのファイルで構成しています。

構成はほとんど前回のシンプルなものと変わっていません。
すべて同じパッケージ内にあって、sample パッケージに配置しています。

前回と同様、まずはソースコードをすべて書き出します。

ListDrag2.fxml

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" 
    prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="sample.ListDragController2">
  <children>
    <ListView fx:id="listViewSrc" layoutX="42.0" layoutY="40.0" prefHeight="200.0" prefWidth="200.0" />
    <ListView fx:id="listViewDest" layoutX="331.0" layoutY="40.0" prefHeight="200.0" prefWidth="200.0" />
  </children>
</AnchorPane>

FXML は前回とまったく変わってません。見た目は変わらずです。

ListDragController2.groovy

class ListDragController2 implements Initializable {

    @FXML private ListView listViewSrc
    @FXML private ListView<String> listViewDest

    private javafx.collections.ObservableList<String> listRecordSrc = FXCollections.observableArrayList()
    private javafx.collections.ObservableList<String> listRecordsDest = FXCollections.observableArrayList()

    @Override
    void initialize(URL url, ResourceBundle resourceBundle) {

        (1..10).each {
            listRecordSrc.add("コーヒー" + it)
        }
        listViewSrc.setItems(listRecordSrc)

        (1..5).each {
            listRecordsDest.add("coffee" + it)
        }
        listViewDest.setItems(listRecordsDest)

        listViewSrc.setOnDragDetected(new EventHandler<MouseEvent>() {
            @Override
            void handle(MouseEvent event) {
                String selectedValue = listViewSrc.getSelectionModel().getSelectedItem()

                Dragboard dragboard = listViewSrc.startDragAndDrop(TransferMode.COPY)

                ClipboardContent content = new ClipboardContent()
                content.putString(selectedValue)
                dragboard.setContent(content)

                event.consume()
            }
        })

        listViewDest.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
            @Override
            ListCell call(ListView<String> listView) {

                ListCell<String> cell = new ListCell<String>() {
                    @Override
                    protected void updateItem(String value, boolean isEmpty) {
                        super.updateItem(value, isEmpty)
                        if (!isEmpty) {
                            setText(value)
                        }
                    }
                }

                cell.setOnDragOver(new EventHandler<DragEvent>() {
                    @Override
                    void handle(DragEvent event) {
                        event.acceptTransferModes(TransferMode.COPY)
                        event.consume()
                    }
                })

                cell.setOnDragEntered(new EventHandler<DragEvent>() {
                    @Override
                    void handle(DragEvent event) {
                        cell.getStyleClass().remove("cellExit")
                        cell.getStyleClass().add("cellOver")
                    }
                })
                cell.setOnDragExited(new EventHandler<DragEvent>() {
                    @Override
                    void handle(DragEvent event) {
                        cell.getStyleClass().remove("cellOver")
                        cell.getStyleClass().add("cellExit")
                    }
                })

                cell.setOnDragDropped(new EventHandler<DragEvent>() {
                    @Override
                    void handle(DragEvent event) {
                        Dragboard dragboard = event.getDragboard()
                        if (dragboard.hasString()) {
                            javafx.collections.ObservableList<String> items = listView.getItems()

                            int addIndex = cell.getIndex() + 1 // mouseoverしている行の次行
                            if (items.size() < addIndex) {
                                addIndex = items.size()
                            }
                            items.add(addIndex, dragboard.getString())
                        }
                        event.setDropCompleted(dragboard.hasString())

                        event.consume()
                    }
                })
                return cell
            }
        })
    }
}

ドラッグ元リスト(listViewSrc)は前回から変更ありません。
ドラッグされたら DragBoard に値をセットするだけです。

ドロップ先リスト(listViewDest)はかなり変わっています。
これは後述でくわしく。

ListDragApp2.groovy

class ListDragApp2 extends Application {

    @Override
    void start(Stage stage) {
        Parent root = FXMLLoader.load(getClass().getResource("ListDrag2.fxml"))
        stage.setTitle("リストDrag&Dropサンプルその2")

        def width = 600
        def height = 300
        Scene scene = new Scene(root, width, height)
        stage.setScene(scene)

        String cssPath = getClass().getResource("style.css").toExternalForm()
        scene.getStylesheets().add(cssPath)

        stage.show()
    }

    static void main(String[] args) {
        launch(ListDragApp2.class, args)
    }
}

前回からの変更点は CSS ファイルの読み込みです。
今回使用する CSS ファイルを scene.getStylesheets().add(xxxx) って感じで追加しています。

style.css

.cellOver {
    -fx-border-width: 0 0 2 0;
    -fx-border-color: black;
    -fx-border-style: solid;
}
.cellExit {
    -fx-border-width: 0;
}

Drag&Drop でドロップ先リストにマウスオーバーした際、挿入候補となる箇所を示すために、セル行間に黒いボーダーを表示しています。
その表示の際に使用するスタイルを定義しています。詳しくは後述します。

今回作成したソースはこれですべてです。
以下、要所をピックアップしてまとめていきます。

ドロップ先を ViewList ではなく、ListCell にする

前回はドロップ先として、ViewList のインスタンスを設定して setOnDragXxxx() メソッドを実装しました。
今回の一番の変更点はそのドロップ先を ViewList の各行を構成する ListCell に変更したところです。

ViewList だと、Drag&Drop でマウスオーバーしている行のインデックスを取得できません。
これが変更した理由になります。

listViewDest.getSelectionModel().getSelectedIndex()
listViewDest.getFocusModel().getFocusedIndex()

といったメソッドで行位置をとれるかなと思ったのですが、マウスオーバーでは反応しませんでした。
行位置がとれないと、要素の挿入位置がわからないのでつらいです。

一方で、ListCell はgetIndex() という自分の行位置を返すメソッドを持っています。
ListCell に対して、ドロップイベント(setOnDragXxxx)を実装すれば、行位置を取得して使うことができます。

リスト全体に対してドロップイベントを設定するのではなく、リスト内の各行に対してドロップイベントを設定するということになります。(…重くなるのかな?未検証)

ListView#setCellFactory(Callback callback) という難解なものを実装することになり大変ですが、
ListCell の動きを実装するためには必要なことのようです。

ListView#setCellFactory(CallBack callback) を実装する

listViewDest.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
    @Override
    ListCell call(ListView<String> listView) {
        /* 行を構成する ListCell のふるまいを実装して返す */
    }
})

詳しくはわかりませんが、etCellFactory() メソッドの引数で Callback インスタンスを設定する。
そのインスタンスの call() メソッドが返す ListCell インスタンスが各行のふるまいを決めることになるので、call() メソッド内で ListCell のふるまいを実装しておけよ…ってことなんだと思います。

CallBack#call() メソッドを実装する

というわけで、call() メソッドです。まずは setOnDragXxxx() 以外のところです。

@Override
ListCell call(ListView<String> listView) {

    ListCell<String> cell = new ListCell<String>() {
        @Override
        protected void updateItem(String value, boolean isEmpty) {
            super.updateItem(value, isEmpty)
            if (!isEmpty) {
                setText(value)
            }
        }
    }

    /* cell.setOnDragXxxx() を実装していく */

    return cell
}

ListCell を返さないといけないので、ListCell インスタンスを生成して返します。
ただ、注意しないといけないのは new しただけだとダメです。

ListCell<String> cell = new ListCell<String>() 
return cell

こんな感じに実装すると、動かしたときに ListView で値が出力されなくなります。
上記であげたように ListCell#updateItem() を実装する必要があります。

セルに対する setOnDragDropped() を実装する

cell.setOnDragDropped(new EventHandler<DragEvent>() {
    @Override
    void handle(DragEvent event) {
        Dragboard dragboard = event.getDragboard()
        if (dragboard.hasString()) {
            javafx.collections.ObservableList<String> items = listView.getItems()

            int addIndex = cell.getIndex() + 1 // mouseoverしている行の次行
            if (items.size() < addIndex) {
                addIndex = items.size()
            }
            items.add(addIndex, dragboard.getString())
        }
        event.setDropCompleted(dragboard.hasString())

        event.consume()
    }
})

前回は ListView の setOnDragDropped() を実装しましたが、今回は ListCell に対して実装します。
前述しましたが、ListView だとドロップした行位置を取得できないので ListCell を使っています。

ドラッグしてきた値をリストに要素追加する主処理は変わっていません。
行位置を取ってきて、要素を挿入する位置を指定するところだけです、増えたのは。

ListView で5行しかないのに7行目とかにドロップすることができて、ObservableList に add するときに例外が発生してしまいます。例外発生を回避するための小細工もいれてたりします。

挿入先がわかるようにドラッグされているときだけスタイルを変える

この後、スタイルを変えるために setOnDragEntered() と setOnDragExited() を実装しているので、
この2つを実装しなくてもやりたいことは実現できます。見た目よくするってことです。

JavaFXCSS でスタイル定義できる

スタイルを設定するので、まずは CSS の定義です。

.cellOver {
    -fx-border-width: 0 0 2 0;
    -fx-border-color: black;
    -fx-border-style: solid;
}
.cellExit {
    -fx-border-width: 0;
}

2つスタイルクラスを定義しています。
枠線を追加するクラス(.cellOver)と枠線をなくすクラス(.cellExit)です。

やりたいのは、ドラッグされたときにセルの下部に枠線を表示して挿入先を示すということです。

JavaFX では -fx- で始まるクラスを使うことができて、スタイルを設定することができます。
ただ、border-bottom-width といった上下左右のいずれかのみ適用させるクラスがないようです。
ということで、上下左右を明示的に指定し bottom 以外を 0 にしています。

そして、.cellExit という枠線をすべて 0 にするクラスもやむなく作っています。
作りたくないのに作っている理由は後で。

ドラッグ&マウスオーバーの有無でスタイルを変える

要素にドラッグされてきたときに呼ばれる setOnDragEntered() と
要素からドラッグが外れたときに呼ばれる setOnDragExited() を実装します。

※補足ですが、要素の上でドラックされているときに呼ばれるのは setOnDragOver() です。

ということで、setOnDragEntered() で枠線を付加する cellOver クラスを ListCell に追加して、
setOnDragExited() でその cellOver クラスを ListCell から削除して、枠線を除去します。

cell.setOnDragEntered(new EventHandler<DragEvent>() {
    @Override
    void handle(DragEvent event) {
        cell.getStyleClass().add("cellOver")
    }
})
cell.setOnDragExited(new EventHandler<DragEvent>() {
    @Override
    void handle(DragEvent event) {
        cell.getStyleClass().remove("cellOver")
    }
})

こういう感じの実装になります。
が、うまくいかないです。枠線が消えません。一度付加された枠線がずっと残る事象発生…
remove() でクラスの設定は削除されているのですが、見た目が変わりません。

色々試してみて、次のような法則を見つけました。(注:あくまでも今回のコンテキストにおいて)
①クラスを remove しても、そのクラスが実現していた見た目が取り消しされるわけではない
②後で追加されたクラスのスタイルが優先される
③すでに存在するクラスを再度追加した場合、②の法則はあてはまらない

ということで、こんな感じの実装で枠線の付加と除去が実現できました。

cell.setOnDragEntered(new EventHandler<DragEvent>() {
    @Override
    void handle(DragEvent event) {
        cell.getStyleClass().remove("cellExit")
        cell.getStyleClass().add("cellOver")
    }
})
cell.setOnDragExited(new EventHandler<DragEvent>() {
    @Override
    void handle(DragEvent event) {
        cell.getStyleClass().remove("cellOver")
        cell.getStyleClass().add("cellExit")
    }
})

もう、ゴリ押しな感じで嫌だ。
でも、挿入先がわかるようになっていい感じになりました。

今後の改善点

かなり長くなりましたが、まとめてみました。
Drag&Drop で操作できるようになると、UI の満足度はぐっと上がりますね。

まだまだ、改善の余地はあるので、挑戦してみようと思っています。

ListView 各行の要素を String で持たせているので、ここをクラス化して各行の情報量を多く持たせるようなこともしたいと思っています。
これに関してはサンプルが色々と転がっていたので簡単にできそうです。

ドラッグしている状態だと、リストのスクロールを上下することができません。
Drag&Drop している状態でマウスカーソルがリストの下部にあるとき、スクロールバーを下に動かしてリストを先送りスクロールさせたいです。

とはいえ、ListView 以外にもやらないといけないことがあるので、まだまだ先の話になりそうです。

JavaFX ListView の要素を Drag&Dropで移動させてみた

f:id:hideoku:20130530000639p:plain:w400

Drag&Dropで参考となるサイト

JavaFX の Drag&Drop の基本を理解する上で参考になるのは次のサイトです。
英語ですが、登場人物がひとつずつ紹介されていて、サンプルコードも添付されています。

http://docs.oracle.com/javafx/2/drag_drop/jfxpub-drag_drop.htm

作ったサンプルの概要

ListViewで作ったリストの1項目をドラッグして、もうひとつのListViewにドロップすると、ドロップした側のリスト末尾にドラッグした値が追加されるサンプルを作りました。

・ListDrag.fxml(GUI部分)
・ListDragController.groovy(MVCのControllerあたり)
・ListDragApp.groovy(アプリ起動)
の3つのファイルで構成しています。
すべて同じパッケージ内にあって、sample パッケージに配置しています。

まずはソースコードをすべて書き出します。
パッケージ宣言は省略していますが、基本的に javafx パッケージ、もしくは javafx.scene パッケージになります。

ListDrag.fxml

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" 
    prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="sample.ListDragController">
  <children>
    <ListView fx:id="listViewSrc" layoutX="42.0" layoutY="40.0" prefHeight="200.0" prefWidth="200.0" />
    <ListView fx:id="listViewDest" layoutX="331.0" layoutY="40.0" prefHeight="200.0" prefWidth="200.0" />
  </children>
</AnchorPane>

ListDragController.groovy

class ListDragController implements Initializable {

    @FXML ListView listViewSrc
    @FXML ListView<String> listViewDest

    javafx.collections.ObservableList<String> listRecordsSrc = FXCollections.observableArrayList()
    javafx.collections.ObservableList<String> listRecordsDest = FXCollections.observableArrayList()

    @Override
    void initialize(URL url, ResourceBundle resourceBundle) {

        (1..10).each {
            listRecordsSrc.add("item" + it)
        }
        listViewSrc.setItems(listRecordsSrc)

        (1..5).each {
            listRecordsDest.add("アイテム" + it)
        }
        listViewDest.setItems(listRecordsDest)

        listViewSrc.setOnDragDetected(new EventHandler<MouseEvent>() {
            @Override
            void handle(MouseEvent event) {
                String selectedValue = listViewSrc.getSelectionModel().getSelectedItem()
                println "drag detected : " + selectedValue

                Dragboard dragboard = listViewSrc.startDragAndDrop(TransferMode.COPY)
   
                ClipboardContent content = new ClipboardContent()
                content.putString(selectedValue)
                dragboard.setContent(content)

                event.consume()
            }
        })

        listViewDest.setOnDragOver(new EventHandler<DragEvent>() {
            @Override
            void handle(DragEvent event) {
                if (event.getGestureSource() == listViewSrc) {
                    event.acceptTransferModes(TransferMode.COPY)
                }
                event.consume()
            }
        })

        listViewDest.setOnDragDropped(new EventHandler<DragEvent>() {
            @Override
            void handle(DragEvent event) {
                Dragboard dragboard = event.getDragboard()
                if (dragboard.hasString()) {
                    listViewDest.getItems().add(dragboard.getString())
                }
                event.setDropCompleted(dragboard.hasString())

                event.consume()
            }
        })
    }
}

ListDragApp.groovy

class ListDragApp extends Application {

    @Override
    void start(Stage stage) {
        Parent root = FXMLLoader.load(getClass().getResource("ListDrag.fxml"))
        stage.setTitle("リストDrag&Dropサンプル")

        def width = 600
        def height = 300
        stage.setScene(new Scene(root, width, height))
        stage.show()
    }

    static void main(String[] args) {
        launch(ListDragApp.class, args)
    }
}

※ Groovy で書いているので、Java としてコピペするとそのまま動かない所はあります。

ドラッグ元リストの挙動を実装する

Drag&Drop を行う上でキモとなっているのは、Controller クラスです。

setOnDragDetected() といった、setOnDragXxxx() というメソッドに EventHandler をセットしていくことで Drag&Drop の挙動を実装していきます。
今回は、3つの setOnDragXxxx() を使いました。

まず、ドラッグ元のリスト(listViewSrc)で setOnDragDetected() を実装しています。

listViewSrc.setOnDragDetected(new EventHandler<MouseEvent>() {
    @Override
    void handle(MouseEvent event) {
        String selectedValue = listViewSrc.getSelectionModel().getSelectedItem()
        println "drag detected : " + selectedValue

        Dragboard dragboard = listViewSrc.startDragAndDrop(TransferMode.COPY)
   
        ClipboardContent content = new ClipboardContent()
        content.putString(selectedValue)
        dragboard.setContent(content)

        event.consume()
    }
})

これはリストの上でドラッグが開始されたときに行う処理です。
リストの選択行の値を取得して、Drag&Dropでつかんでいる内容として Dragboard にセットしています。
今 Drag&Drop しているのはこういう情報だというのを Dragboard や ClipboardContent を使って実装します。
ClipboardContent はその内容として文字列だけでなくファイルなどもセットできるので、ファイルの Drag&Drop も簡単に実現できそうです。

Dragboard に ClipboardContent をセットすると、Drag&Drop したときのマウスアイコンがファイルイメージに変わるようになります。

ちなみに、この Drag&Drop している内容は JavaFX だけに閉じているわけではありません。
たとえば、今回のように JavaFX 上で Drag 選択した文字列をそのままテキストエディタなどに Drop すると、その文字列がペーストされます。
試してはないですが、逆の Drag&Drop もおそらくできて JavaFX 上の要素にペーストできます(そのはず)。

こんなの簡単にできるなんて、なんかすごいなと。

ドロップ先リストの挙動を実装する

次に、ドロップ先のリスト(listViewDest)で setOnDragOver() と setOnDragDropped() を実装しています。

setOnDragOver() で Drag されてマウスオーバーしているのを検知した時の処理、setOnDragDropped() で Drop されたときの処理を実装します。

listViewDest.setOnDragOver(new EventHandler<DragEvent>() {
    @Override
    void handle(DragEvent event) {
        if (event.getGestureSource() == listViewSrc) {
            event.acceptTransferModes(TransferMode.COPY)
        }
        event.consume()
    }
})

listViewDest.setOnDragDropped(new EventHandler<DragEvent>() {
    @Override
    void handle(DragEvent event) {
        Dragboard dragboard = event.getDragboard()
        if (dragboard.hasString()) {
            listViewDest.getItems().add(dragboard.getString())
        }
        event.setDropCompleted(dragboard.hasString())

        event.consume()
    }
})

DragEvent#acceptTransferModes() メソッドで、Dragされてきた内容を受け取る方法を設定するみたいです。
メソッド引数に TransferMode.COPY や TransferMode.MOVE といった挙動を指定します。
COPY も MOVE も実行結果は見た目変わらないのですが…(詳細は違うのでしょうね)

Dragboard で保持されている文字列をドロップ先のリストの要素として末尾に追加しています。

まとめ

setOnDragXxxx() メソッドを適当にいじってみると、結構簡単に Drag&Drop が実現できました。
細やかな挙動を作りこんでいくとなると、悩むポイントが多くなりそうです。

今回はドロップ先のリストの末尾に追加するサンプルを作りましたが、
リストの任意の行に挿入することもできたので近々まとめたいと思います。

◯2013/05/31 追記:行挿入パターンもまとめました。
Drag&Dropで JavaFX ListView の任意の行に挿入させてみた - Java開発のんびり日記

『SCRUM BOOT CAMP THE BOOK』を読んでみた

実は…

出版されてすぐに読み終わってたのですが、
ずるずるとブログに書けずじまい。

デブサミ2013で、著者お三方にサインをいただいてたので
(「ブログとかで感想書いてくださいねー」ってお願いされた)
ちゃんとフィードバックせねば!と思いながら…はや2ヶ月。

書きます。φ(`д´)

スクラム実践のための書籍です

基礎編と実践編に分かれています。
前半の基礎編ではスクラムの全体像とルールについて説明されています。
後半の実践編では架空のプロジェクトを題材にしてマンガを交えながら時系列で説明されています。

スクラムの "実践" を意識した内容です。
理論からではなく、実践から生み出されたスクラム本です。(たぶん…)

アジャイルサムライ』はバイブル的存在ですが、理論として・あるべき論として書かれています。
したがって、「実際はこんなのやりたいけど、できねえや」で、なかなか実践へのステップが踏み切れなかったりします。

『SCRUM BOOT CAMP THE BOOK』はそういう点では実践からスタートした書籍だと言えます。
実際にアジャイルスクラムを実践し、コーチされている著者のノウハウが詰まっていますし、実践からのフィードバックも反映されていると思います。

また、外国のスクラム書籍の翻訳本ではなく、日本人が書いたスクラム書籍であるというのもポイントですね。
(マンガに出てくる、ブチョーと営業部長がなんだか日本っぽい)

実践編のタイトルをあげてみる

実践編は26のシーンから成っています。そのタイトルだけ列挙します。
(No.00とNo.25 は、前説とまとめなので除いてます)

No.01 ロールを現場に当てはめる
No.02 プロジェクトを理解する
No.03 プロダクトバックログをつくる
No.04 見積りをしていく
No.05 見積りをより確実にする
No.06 プロジェクトの計画を立てる
No.07 詳細な計画を立てる
No.08 素早くリスクに対処する
No.09 何が終わったか明確にする
No.10 状況をうまく把握する
No.11 先を予測しやすくする
No.12 次にやることを明確にする
No.13 みんなの自律を促していく
No.14 ベロシティを上げていく
No.15 問題を見つけやすくする
No.16 意図を明確にしておく
No.17 プロジェクトを支援していく
No.18 より良い状態にしていく
No.19 先のことをいつも明確にする
No.20 手戻りをなくしていく
No.21 あふれそうなものを調整する
No.22 さまざまな状況に対応する
No.23 より確実な判断をしていく
No.24 リリースに必要なことをする

ふむふむ。
なんだか妙に柔らかい表現で、プロジェクトでやることを列挙されています。
実際やるとなると難しいことばかり。

行動の主体がだれなのか?

上記で列挙したやることには主語が書かれていません。
その主語がだれなのか?

主語が、チームになっているのがポイントだと私は思ってます。

プロジェクトリーダーではなく、スクラムマスターではなく。
チーム(スクラムチーム)です。

マンガを見ても、ボクくん(スクラムマスター)が一人黙りこくって
単独行動しているケースはありません。
チームで話し合っているシーンばかりです。

それに気づいたとき、当たり前でしょと思うかもしれませんが、感動を覚えました。

スクラム(プロジェクト運営)はプロジェクトリーダやスクラムマスターが一人で抱え込むものではなく、チーム全員で考えないといけないものだということを改めて認識しました。
私がこの本を好きなのは、これを感じられるからです。

あと、進行形(〜していく)という表現も多いですね。
プロジェクトの間、継続的にやっていかないとダメだぞということです。

この本片手に、がんばる勇気を

スクラムを実践するにあたって、障害はたくさんあります。

あまりに多くの抵抗に、せっかく高まったモチベーションはすぐに萎えてしまいます。
輝いていたタスクボードは腐っていきます。

それはお前の意志の弱さだと言われればそれまでなのですが、
納得が全くいかない旧来の管理手法に打ち勝とうと思うと大変なのです。

いくら理論武装しても、上司が理解してくれなかったり、チームがやる気になってくれなかったり。
誤解、曲解、やってるつもりで実はやっていない、そして元の木阿弥。そんな残念なことも。

そういう中でよりよい開発の現場を作るために背中を押してくれる、元気をくれる本がやっと出たかなと思っています。

Chef はじめました

『入門 Chef Solo』おすすめ

きのう『入門 Chef Solo』を買って、写経しながら Chef 触ってみています。

これまで「Chef すごいなあ、Chef やってみたいなあ」がずっと続いてました。
でも、調べてみると Chef についての情報が散在していて、取っ掛かりが見つかりませんでした。

そんな中、『入門 Chef Solo』が Kindle で出ました。
TL を見てみると、いい感じだということで購入にいたりました。


『入門 Chef Solo』のよい点、3つあげてみます。

・たぶん、他に Chef の入門書がない。
Hello World 的な最初のアウトプットまでがしっかりしている。
ユースケースごとに章立てされていて実践しやすい。写経しやすい。

ちなみに、これが私にとって初めての電子書籍です。
ちっこい Android スマホで読んでます。

…やっぱり本のほうがいい。

というのは置いておいて…
『入門 Chef Solo』は Chef の初めの一歩のハードルをものすごく低くしてくれる、とてもよい入門書だと思います。

Vagrant がすごい

色々と Chef のすごさをかみしめてますが、それは置いておいて。
Vagrant についてです。

Vagrant は仮想マシン VirtualBox のフロントエンドツールです。
CUI で簡単に仮想環境を作れるスグレモノです。

ほんと数行コマンドを叩くだけで Linux が手に入ります。
数百 MB の VM イメージをダウンロードする時間はかかりますが、ほぼ迷うことなく Linux が使えるようになります。

簡単に環境が手に入るので変なことやって、ぶっ潰しても大丈夫なわけで。
現に vagrant destroy というコマンドがあって、作った環境を意図的にぶっ潰せます。

また作りなおせばいいのです。
そして、作りなおすときに便利なのが、Chef です。

あと、Sahara ってのもあって…これもすごい。

近寄りがたかった VMWareLinux

昔から仮想環境といえば、VMWare があるわけです。

だれかが作ってくれた仮想イメージを「いつかまた使えるように〜」とか永年保存してたりします。
VMWare の仮想イメージの作り方とかよくわからないです)

確かに仮想イメージをコピペして使えば、使い捨て環境ができていました。
でも、仮想環境って特権階級な人たちが作る使うもの、みたいなイメージでした。

サーバルームで仮想マシンを何台も立てて…ということをインフラ屋さんがやっている。
けれど、すぐにメモリなどのリソースが枯渇してトラブルになったり。
正直なところ、インフラ構築や仮想化というのは近寄りがたい領域でした。

それに、Windows ユーザからすると Linux に触ること自体が恐怖。

インフラ構築の劇的な変化

ここ数年で、その辺が大分変わってきていると感じています。
仮想イメージもゴロゴロ落ちてますし、仮想環境構築についての情報もたくさんあります。

また、PC の性能もよくなってきています。
ストレス無く、ホストもゲストも動作させることができます。

そしてそして、クラウドに行くと AWS がいるわけです。

…すごい世界ですよね。

自分のPCに仮想環境つくるのであれば、コストはゼロです。
AWS を使った場合でも、うまくやればお小遣いレベルに抑えられます(不意な課金が怖いので、まだやらないですが)。

時間軸で見ても圧倒的です。ほぼ即時で構築できますよね。

クラウド時代のインフラ技術を身につけておく

実際に Vagrant や Chef を触ってみると、そういう時代に来ているんだと実感できます。

敷居が高いところも確かにありますが、従来通りのやり方ままだと早晩「爆撃機と竹槍でやり合おうとしている」レベルに陥ってしまうのではないかと思います。
懇切丁寧な手順書を作ってビクビクしながらマシンを操作する横で、数百台の構築を一気に自動化していたりするわけです。

乗り遅れると恐ろしい話ですが、乗りこなせれば楽しいことこの上ないはず。
手遅れにならないよう、仕事の幅を広げられるよう、このあたりの技術も身につけておきたいです。