ページ

2013年4月7日日曜日

JavaFX - Dragging gestures (1)

JavaFXのドラッグの動作についてみていきたいと思います。

JavaFXのドラッグの動作には、以下の3つのタイプがあります。

  • Simple press-drag-release gesture
  • Full press-drag-release gesture
  • Platform-supported drag-and-drop gesture

これらの動作は、MOUSE_PRESSEDイベントで開始し、MOUSE_RELEASEDイベントで終了することになり、Simple press-drag-release gestureがデフォルトの動作になります。

まずは、Simple press-drag-release gestureからみていきたいと思います。

この動作については、ノードの大きさを変更したり、ノードの位置をドラッグして移動したりするときのように、対象となるノードのみの操作で、他のノードと関連しないような操作を行いたいときに利用します。

MOUSE_PRESSEDイベントが発生したノードに対して、MOUSE_RELEASEDイベントが発生するまで、そのノードに対してマウスイベントが発生しますので、基本的には、MOUSE_PRESSEDイベントで、ドラッグしたいノードに対して初期処理を行い、MOUSE_DRAGGEDイベントで、ドラッグしたときの処理を行い、MOUSE_RELEASEDイベントで、終了処理を行うことになります。

以下のような、1つのノードの位置をドラッグで移動するものを作成してみたいと思います。

シーン上に1つのCircleがあり、それを操作する単純なものになります。

まずは、FXMLで以下のようにViewを定義します。

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

    <Pane>
        <Circle fx:id="draggableCircle"
                centerX="100" centerY="100" radius="30"/>
    </Pane>
</Scene>

次に、コントローラクラスを定義し、Circleのドラッグに対する動作を実装します。

public class NodeDragDemoSceneController {
    @FXML
    private Scene scene;
    @FXML
    private Circle draggableCircle;

    private double dragAnchorX;
    private double dragAnchorY;

    public void performOn(Stage stage) {
        stage.setScene(scene);
        stage.setTitle("Node Drag Demo");
        stage.sizeToScene();
        stage.centerOnScreen();
        stage.show();
    }

    @FXML
    protected void initialize() {
        draggableCircle.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                Node node = (Node)mouseEvent.getSource();
                dragAnchorX = mouseEvent.getSceneX() - node.getLayoutX();
                dragAnchorY = mouseEvent.getSceneY() - node.getLayoutY();
            }
        });

        draggableCircle.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                Node node = (Node)mouseEvent.getSource();
                node.setLayoutX(mouseEvent.getSceneX() - dragAnchorX);
                node.setLayoutY(mouseEvent.getSceneY() - dragAnchorY);
            }
        });
    }
}

ノードの位置を変更するには、layoutXとlayoutYを変更することによって行っています。ドラッグしたときのマウスの位置の変化分をlayoutXとlayoutYに反映するようにしています。

MOUSE_PRESSEDイベントで、マウスのシーンに対する位置からノードのlayoutX, layoutYを引いた値を保持しておき、MOUSE_DRAGGEDイベントで、ドラッグしたときのマウスのシーンに対する位置から保持していた値を引くことによって、ドラッグ開始時のlayoutX, layoutYに、ドラッグしたときのマウスの移動量を追加するようにしています。今回は、終了処理として特に何も行いませんので、MOUSE_RELEASEDイベントは設定しませんでした。

あとは、Applicationクラスを作成しておきます。

public class NodeDragDemo extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        FXController.of(new NodeDragDemoSceneController())
            .fromDefaultLocation()
            .load()
            .performOn(primaryStage);
    }

    public static void main(String... args) {
        launch(args);
    }
}

FXControllerについては、こちらの記事を参照してください。

ドラッグの動作として、Simple press-drag-release gestureについてみていきました。ドラッグの動作としては基本的に、MOUSE_PRESSEDイベントで初期処理、MOUSE_DRAGGEDイベントでドラッグ中の処理、MOUSE_RELEASEDイベントで終了処理を実装すればよさそうです。次回は、このドラッグでノードを移動する動作を、コントローラクラスに直接記述するのではなく、再利用可能となるようなユーティリティクラスとして作成することを考えてみます。