先週に引き続いて,Swingでのドラッグ&ドロップについての強化点について解説していきます。

先週は,ドラッグ&ドロップで転送するデータに関する情報をまとめて保持するためにTransferHandler.TransferSupportクラスが導入されたことを紹介しました。TransferSupportクラスを使うと,ドロップする位置を細かく設定できます。

今週はTransferSupportクラスのもう一つの機能を紹介しましょう。

強制的にコピー,移動

通常,ドラッグ&ドロップでデータを転送する場合,コピーにするか移動にするかはユーザーの選択に委ねられます。しかし,アプリケーションによっては,コピーだけにしたいとか,常に移動だけにしたいということがあるかもしれません。

TransferSupportクラスを使えば,これを簡単に実現できます。

例えば,コピーだけに限定したいのであれば,TransferHandlerクラスの派生クラスを作成し,canImportメソッドの中で次のように記述します。

// COPYがサポートされているかどうかをチェック
boolean supported =
    (COPY & support.getSourceDropActions()) == COPY;
if (supported) {
    // サポートしていたら,アクションをCOPYに設定する
    support.setDropAction(COPY);
} else {
    return false;
}

COPYはTransferHandlerクラスで定義されている定数です。COPY以外にMOVEとLINKが使用できます。

supportがTransferSupportクラスの変数になっています。

はじめにTransferSupportクラスのgetSourceDropActionメソッドをコールして,データ転送元がCOPYをサポートしているかどうかをチェックします。

このとき,COPYと論理積を取っているのは,COPYとMOVEの両方をサポートしている場合,getSourceDropActionの戻り値がCOPYとMOVEの論理和,つまりCOPY | MOVEになっているためです。

COPYがサポートされているとわかったら,TransferSupport#getDrapActionメソッドでドロップ時のアクションを設定します。もし,サポートされていなければ,ドロップができないと判定します。

このコードではCOPYに決め打ちしてますが,変更することも可能です。

このことをサンプルで確かめてみましょう。

サンプルのソースコード JListDnD3.javaListTransferHandler3.java

JListDnD3クラスを実行すると,先週のJListDnD2と同様に一番下にコンボボックスが表示されます。ここで,JListにドロップするときのアクションを指定できます。

コピーを強制 移動を強制
図1 コピーを強制 図2 移動を強制

コンボボックスで選択された値を保持させるためにフィールドを持たせました。ここではTransferHandlerクラスを派生させたListTransferHandler3クラスに定義し,デフォルト値はCOPY_OR_MOVEにしました。

private int action = COPY_OR_MOVE;

public void setAction(int action) {
    this.action = action;
}

実際にドロップのアクションを設定するのは前述したようにcanImportメソッドで行います。canImportメソッドを以下に示します。

public boolean canImport(TransferSupport support) {
    // クリップボード経由のデータ転送は扱わない
    if (!support.isDrop()) {
        return false;
    }

    // 文字列以外のフレーバーは受け入れない
    if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) {
        return false;
    }

    // コピー,移動などのアクションのサポートをチェック
    if (action != COPY_OR_MOVE) {
        boolean supported =
            (action & support.getSourceDropActions()) == action;
        if (supported) {
            // サポートしていたら,ドロップのアクションを設定する
            support.setDropAction(action);
        } else {
            // サポートしてなければ,ドロップを行わない
            return false;
        }
    }
     
    // ドロップする位置を常に表示する
    support.setShowDropLocation(true);
    
    return true;
}

赤字の部分以外は先週解説したので,そちらを参照ください。

赤字の部分は前述したコードのCOPYの部分をactionで置き換えただけです。

このようにドロップ時のアクションをカスタマイズできるのですが,そのためにはTransferHandlerクラスを派生させる必要があります。例えば,JTextComponentクラスのようにすでにTransferHandlerクラスが設定されている場合,これを破棄してわざわざ派生クラスを作らなくてはならないという問題もあります。

逆にいえば,JListクラスやJTree,JTableクラスなどTransferHandlerクラスが設定されていない場合,TransferHandlerクラスを派生する必要があるので,ドロップ時のアクションをカスタマイズすることは容易です。

コピーしか扱わないのに,ドラッグしているときのマウス・ポインタが移動を示すものになっていたら,操作するときにわかりにくくなってしまいます。ユーザーに迷いを生じさせないためにも,ドロップのアクションが限定されているのであれば,それに応じてTransferHandlerクラスもカスタマイズするようにしましょう。

著者紹介 櫻庭祐一

横河電機 ネットワーク開発センタ所属。Java in the Box 主筆

今月の櫻庭

毎年,JavaOneに参加すると,自分の英語力のなさを思い知ります。

JavaOneにはそれこそ世界中からJavaの技術者が集まってきます。ランチ会場やパビリオンなどで,参加者同士で議論が始まったりします。そういう場になかなか入っていけないのです。

毎年,英語を勉強しなくてはと思うのですが,それが長続きしたことはありません。長続きしていれば,もうペラペラになっているはずなのですが…

5月30日にJavaOne報告会が行われ,その中のライトニングトークで複数の講師がニンテンドーDSのえいご漬けを購入したと話していました。やっぱり,みな考えることは同じなのかもしれませんね。