S-JIS[2012-04-21] 変更履歴

ドロップ処理(JavaFX Scene Builder)

JavaFX Scene Builderで(ファイルの)ドロップ処理を記述する方法。


概要

(自分のJavaFXアプリの外部から)ファイル等をドラッグして自分のアプリにドロップされた際に、それを受け取ることが出来る。
Scene Builderでドロップ関連イベントに名前を付け、Controllerでその処理を実装する。

ドロップに関連するイベントは以下の4つ。

  1. Drag Entered …ノード(コンポーネント)上に“ドラッグ中のマウスカーソル”が入ってきた時に発生する
  2. Drag Over …ノード上で“ドラッグ中のマウスカーソル”を動かすと発生する
  3. Drag Dropped …ノード上でドロップされた時に発生する
  4. Drag Exited …“ドラッグ中のマウスカーソル”がノードの外に出た時、またはドロップされた後に発生する(ドロップを受け付けても受け付けなくても)

重要なのはOverとDropped。
Overでドロップを受け付けるかどうか決定し、Droppedで受け取る。
Overでドロップを受け付けるようにしていないと、Droppedイベントは発生しない。

EnteredとExitedは、例えばドロップ対象となるノードの色を変えたい(強調したい)ような場合に使う。
Exnteredで色を変え、Exitedで元に戻す。


ドロップイベント処理

ドロップを処理するハンドラー(メソッド)名を、Scene Builderの右側のインスペクターパネルの「Events」の「On Drag Over」「On Drag Dropped」に入れる。
(この際、先頭に「#」を付ける必要がある)

FXMLのコントローラークラスにそのメソッドを記述する。


Drag Over

ドロップを受け付けるかどうかをコーディングする。

Scene Builderで「On Drag Overに入れたのが「#handleDragOver」だとすると、メソッド名は「handleDragOver」になる。

import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
	public void handleDragOver(DragEvent event) {
		// ファイルの場合だけ受け付ける例
		Dragboard db = event.getDragboard();
		if (db.hasFiles()) {
			event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
		}

		event.consume();
	}

db.hasFiles()で、ファイルがドロップ中なのかどうかを判断できる。
内部では「hasContent(DataFormat.FILES)」を呼んでおり、色々なドロップオブジェクトに対応できるようだが、
それとは別に、hasFiles()やhasString()の様に、よく使うものについてはメソッド化されている。

ドロップを受け付ける場合はacceptTransferModes()を呼び出し、受け付ける操作を指定する。
例えばCOPYだとコピー、MOVEだと移動。それに応じて、マウスカーソルのアイコンが変わる。
可変長引数で複数の操作方法を指定できるので、COPYとMOVEを両方受け入れる場合は「acceptTransferModes(TransferMode.COPY, TransferMode.MOVE)」と書くことも出来る。
COPYとMOVEを両方指定した場合、デフォルトでは「移動」扱いで、ドラッグ中にCtrlキーを押すと「コピー」になる。(もしかするとOS依存かも?)

最後にconsume()を呼んでおく。
Swingにもあったが、イベントを処理したという印なんだろう。


Drag Dropped

ドロップされた時の処理をコーディングする。

Scene Builderで「On Drag Droppedに入れたのが「#handleDragDropped」だとすると、メソッド名は「handleDragDropped」になる。

import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
	public void handleDragDropped(DragEvent event) {
		boolean success = false;

		// ファイルの場合だけ受け付ける例
		Dragboard db = event.getDragboard();
		if (db.hasFiles()) {
			List<File> list = db.getFiles();
			System.out.println(list);
			success = true;
		}

		event.setDropCompleted(success);
		event.consume();
	}

ファイルがドロップされた場合は、getFiles()でファイル一覧を取得できる。
getFiles()自体はnullを返す可能性があるが、hasFiles()がtrueなら、たぶん必ずnull以外が返るだろう。

ドロップが成功したら、setDropCompleted()でtrueをセットする。
セットしないと失敗扱いになる。
(ドラッグを開始した側へ通知される)


ドラッグ中の装飾

ドラッグ中に、ドロップ対象(ドロップ先)を強調したりすると分かり易い。
Drag Enteredで対象ノード(コンポーネント)のスタイルを変え、Drag Exitedで元に戻す。

Scene Builderの右側のインスペクターパネルの「Events」の「On Drag Entered」「On Drag Exited」に入れる。
(この際、先頭に「#」を付ける必要がある)

FXMLのコントローラークラスにそのメソッドを記述する。


Drag Entered

ドラッグ中のマウスカーソルが入ってきたときに、ドロップ対象のスタイルを変える。

event.getSource()でイベント対象(イベントハンドラーを書いたノード)のオブジェクトが取れる。
イベントハンドラーが書かれているノードとドロップ対象のノードが同じ場合はそれが使える。(が、キャストしないといけないので好みではない)

import javafx.scene.Node;

import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
	private String saveStyle = null;

	public void handleDragEntered(DragEvent event) {
		Dragboard db = event.getDragboard();
		if (db.hasFiles()) {
			Object source = event.getSource();
			if (source instanceof Node) {
				Node node = (Node)source;
				saveStyle = node.getStyle();
				node.setStyle("-fx-background-color: blue"); //背景色を変更
			}
		}

		event.consume();
	}

Drag Exited

マウスカーソルがどっか行ったとき(あるいはドロップした後)にドロップ対象のスタイルを戻す。

import javafx.scene.input.DragEvent;
	public void handleDragExited(DragEvent event) {
		Node node = (Node)event.getSource();
		node.setStyle(saveStyle);

		event.consume();
	}

ただ、スタイルを特に何も指定していないノード(コンポーネント)の場合、元々styleは何も入っていないようだ。(getStyle()で空文字列が返る)
つまり元に戻すと言っても空文字列を指定するだけ。
このとき、スタイルによっては元に戻らないこともあるようだ。(Windows版JavaFX2.1では、-fx-background-colorは元に戻ったが、-fx-border-colorは戻らなかった。バグなのかどうか分からないが)


使用法に戻る / Scene Builder目次へ戻る / JavaFXへ戻る / Javaへ戻る / 技術メモへ戻る
メールの送信先:ひしだま