ページ

2013年5月6日月曜日

JavaFX - Dragging gestures (6)

前回は、Platform-supported drag-and-drop gestureについて、どのようにして行うかをみていきました。今回は、Platform-supported drag-and-drop gestureを利用して、以下のような、テキストボックスの文字列をドラッグアンドドロップで、他の部分に移動するものを作成してみたいと思います。

まずは、Viewを作成します。

<Scene xmlns:fx="http://javafx.com/fxml"
       fx:id="scene"
       width="640" height="480">
    <stylesheets>
        <URL value="@PlatformSupportedDragAndDropGestureDemoStyle.css"/>
    </stylesheets>

    <GridPane>
        <columnConstraints>
            <ColumnConstraints prefWidth="50"/>
            <ColumnConstraints prefWidth="50"/>
        </columnConstraints>

        <TextArea fx:id="dragSourceTextArea"
                  GridPane.columnIndex="0"
                  GridPane.halignment="CENTER" GridPane.valignment="CENTER"
                  GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS"
                  maxWidth="150" maxHeight="50"
                  text="Drag me!"/>

        <StackPane fx:id="dropTargetPane"
                   GridPane.columnIndex="1"
                   GridPane.halignment="CENTER" GridPane.valignment="CENTER"
                   GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS"
                   prefWidth="200" prefHeight="150"
                   maxWidth="-Infinity" maxHeight="-Infinity">
            <Label fx:id="dropTargetLabel"/>
        </StackPane>
    </GridPane>
</Scene>

ドラッグ元となるTextAreaとドラッグ先となるStackPaneを定義しています。

次に、ドラッグ元のTextAreaのDRAG_DETECTEDイベントで、TextAreaで選択されている文字列をClipboardに設定し、Platform-supported drag-and-drop gestureを開始します。

dragSourceTextArea.setOnDragDetected(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
        TextArea source = (TextArea)event.getSource();
        if (source.getSelectedText().isEmpty()) { return; }

        Dragboard board = source.startDragAndDrop(TransferMode.COPY_OR_MOVE);
        ClipboardContent content = new ClipboardContent();
        content.putString(source.getSelectedText());
        board.setContent(content);

        event.consume();
    }
});

ドラッグ先のStackPaneのDRAG_OVERイベントで、Dragboardに文字列が設定されている場合に、ドロップを受け入れるように設定します。

dropTargetPane.setOnDragOver(new EventHandler<DragEvent>() {
    @Override
    public void handle(DragEvent event) {
        Dragboard board = event.getDragboard();
        if (board.hasString()) {
            event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
        }

        event.consume();
    }
});

ドラッグ先のStackPaneのDRAG_DROPPEDイベントで、Dragboardに設定されている文字列を取得して、Labelにその文字列を設定します。

dropTargetPane.setOnDragDropped(new EventHandler<DragEvent>() {
    @Override
    public void handle(DragEvent event) {
        Dragboard board = event.getDragboard();
        if (board.hasString()) {
            dropTargetLabel.setText(board.getString());
            event.setDropCompleted(true);
        }

        event.consume();
    }
});

また、ドラッグ中にカーソルがStackPane内に入ってきたときに、StackPaneの外枠を強調するために、DRAG_ENTEREDとDRAG_EXITEDイベントで、スタイルの変更をします。

dropTargetPane.setOnDragEntered(new EventHandler<DragEvent>() {
    @Override
    public void handle(DragEvent event) {
        ((Node)event.getSource()).getStyleClass().add("drop-target");
    }
});
dropTargetPane.setOnDragExited(new EventHandler<DragEvent>() {
    @Override
    public void handle(DragEvent event) {
        ((Node)event.getSource()).getStyleClass().remove("drop-target");
    }
});

最後に、ドラッグ元のTextAreaのDRAG_DONEイベントで、TransferMode.MOVEでドロップした場合は、ドラッグしているTextAreaで選択されている文字列を削除します。

dragSourceTextArea.setOnDragDone(new EventHandler<DragEvent>() {
    @Override
    public void handle(DragEvent event) {
        if (event.getTransferMode() == TransferMode.MOVE) {
            TextArea source = (TextArea)event.getSource();
            source.deleteText(source.getSelection());
        }

        event.consume();
    }
});

Platform-supported drag-and-drop gestureでは、基本的には、ドラッグ元のノードでは、DRAG_DETECTEDとDRAG_DONEイベントで、ドラッグ開始時とドロップ完了時の処理を記述し、ドラッグ先のノードでは、DRAG_OVERとDRAG_DROPPEDイベントで、ドロップに対する処理を記述することになります。また、必要に応じて、DRAG_ENTEREDとDRAG_EXITEDイベントを記述します。

同じようにして、以下のような、ListViewの項目の位置をドラッグアンドドロップで変更するものを作成してみました。

今回は、同じアプリケーション内で、Platform-supported drag-and-drop gestureを利用しましたが、通常は、クリップボートを利用して、他のアプリケーションとデータをやり取りする場合に利用する機会が多いと思います。

JavaFXでは、ドラッグの動作として、3種類のタイプが用意されていますので、状況に応じて使い分けていくとよいようです。