クスプローラ(Windows)からファイルをドラッグ&ドロップしたい,の巻
2000.11.16 新規

実は、Java アプリケーション間のドラッグ&ドロップ(以降、D&D)よりも、ネイティブ・プラットフォームからのD&Dの実装のほうがはるかに簡単です。 理由も簡単で、「受け側」だけを作成すれば良く、複雑な「送り側」を作らなくて済むからです。 当然「送り側」はネイティブ、もしくは Java VM が担当してくれます。

早速サンプルを見てみましょう。
ホントに簡単です。 下のソース・コードは単独で動作するようになっているので、無駄な部分も多くて少し長いです。 が、本質的なところはD&Dのドロップ動作に対するイベント・リスナを実装している部分です。

ドロップ動作に対するイベント・リスナ DropTargetListener は、5つのメソッドを有しています。 このうち今回使用するのは、ドラッグ操作を検出したときの dragEnter() というメソッド。 そして、実際にドロップされたときに発生するイベントを処理する drop() メソッドの、計2つです。
dragEnter() メソッドはドラッグ動作を受け入れる、という処理を記述するだけです。

そして肝心の drop() メソッドですが、このメソッドでは

という一連の処理を記述します。 詳細は、ソースコードを参考にしてください。

注意点としては、ドロップされる側をキチンと設定しておかなければいけないところです。 このサンプルでは

    // D&D受け入れコンポーネントの関連付け
    new DropTarget(this,this);
    new DropTarget(routePanel,this);
としているので、パネルとタイトル・バーどちらにもD&Dを受け付けています。 これは、ドロップ動作の受け付けコンポーネントとイベント・リスナを関連付けています。

また、勘違いをしやすい点も書いておきます。
いくらコンポーネントの関連付けを行っていても、その上に違うコンポーネントが重なっている場合にはD&Dは無効となります。 例えば、このサンプルに JButton 等を新規追加したとすると、そのボタンに対してD&Dを行っても処理はされません。 重なって前面にあるコンポーネントに対するD&Dイベントは、処理されないからといって、その後ろのコンポーネントには伝わらないようです。


「SampleDnD.java」のソースコード
import java.awt.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import javax.swing.*;
import java.io.*;

/**
 * ドラッグ&ドロップのサンプル・プログラムです.
 * 
 * @author Musi_chan
 * (Musi_chan@cool.biglobe.ne.jp)
 * @version 2000/11/16 19:49
 */
public class SampleDnD extends JFrame implements DropTargetListener {
/******************************************************************************
  フィールド
******************************************************************************/
  /**
   * メイン・パネルのインスタンス変数です.
   */
  private JPanel routePanel;

/******************************************************************************
  コンストラクタ
******************************************************************************/
  /**
   * コンストラクタです.
   */
  public SampleDnD() {
    this("");
  }

  /**
   * コンストラクタです.
   * 
   * @param args 起動時の引数(ルック&フィール名)
   */
  public SampleDnD(String args) {
    // ルック&フィール名保持用ローカル変数
    String LAFName;

    // 各種ウィンドウ設定
    setLocation(50,25);
    setSize(360,280);
    setResizable(true);
    setTitle("Drag&Drop sample program.");
    getContentPane().setLayout(new BorderLayout());
    //setIconImage(ICON.getImage());
    setBackground(SystemColor.menu);
    setForeground(SystemColor.menuText);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    // ルック&フィール名の判別
    if(args.equals("windows")) {
      LAFName="com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
    } else if(args.equals("motif")) {
      LAFName="com.sun.java.swing.plaf.motif.MotifLookAndFeel";
    } else {
      LAFName="javax.swing.plaf.metal.MetalLookAndFeel";
    }

    // メイン・パネルの設定と追加
    setRoutePanel(LAFName);
    getContentPane().add(routePanel,BorderLayout.CENTER);

    // メイン・ウィンドウの表示
    setVisible(true);
  }

/******************************************************************************
  メソッド
******************************************************************************/

/******************************************************************************
  ビュー
******************************************************************************/
  /**
   * メイン・パネルの設定をするメソッドです.
   * 
   * @param LAFName ルック&フィール名
   */
  private void setRoutePanel(String LAFName) {
    // ルック&フィール設定
    try {
      UIManager.setLookAndFeel(LAFName);
    } catch(ClassNotFoundException cnfe) {
    } catch(InstantiationException ie) {
    } catch(IllegalAccessException iae) {
    } catch(UnsupportedLookAndFeelException ulafe) {
    }

    // ルートパネルのインスタンス生成
    routePanel=new JPanel();

    // D&D受け入れコンポーネントの関連付け
    new DropTarget(this,this);
    new DropTarget(routePanel,this);
  }

  /**
   * 情報ダイアログを表示するメソッドです.
   * 
   * @param message メッセージ
   */
  public void showInformationMessageDialog(String message) {
    // 情報ダイアログの表示
    JOptionPane.showMessageDialog(this,new JLabel(message)
      ,"Information...",JOptionPane.INFORMATION_MESSAGE);
    repaint();
  }

  /**
   * 警告ダイアログを表示するメソッドです.
   * 
   * @param message 警告メッセージ
   */
  public void showWarningMessageDialog(String message) {
    // 警告ダイアログの表示
    JOptionPane.showMessageDialog(this,new JLabel(message)
      ,"Warning...",JOptionPane.WARNING_MESSAGE);
    repaint();
  }

  /**
   * エラーダイアログを表示するメソッドです.
   * 
   * @param message エラーメッセージ
   */
  public void showErrorMessageDialog(String message) {
    // エラーダイアログの表示
    JOptionPane.showMessageDialog(this,new JLabel(message)
      ,"Error...",JOptionPane.ERROR_MESSAGE);
    repaint();
  }

/******************************************************************************
  コントローラ
******************************************************************************/
  /**
   * メインメソッドです.起動時に呼び出されます.
   * 
   * @param args コマンドライン引数
   */
  public static void main(String[] args) {
    // 自身のインスタンス生成(=アプリの開始)
    if(args.length==0) {
      new SampleDnD();
    } else {
      new SampleDnD(args[0]);
    }
  }

  /**
   * ドラッグ&ドロップ(ドロップ時)イベント用メソッドです.
   * ドラッグ操作を検出したときに呼び出されます.
   * 
   * @param dtde ドラッグ&ドロップイベント
   */
  public void dragEnter(DropTargetDragEvent dtde) {
    dtde.acceptDrag(DnDConstants.ACTION_COPY);
  }

  /**
   * ドラッグ&ドロップ(ドロップ時)イベント用メソッドです.
   * ドラッグ操作がドロップなしのときに呼び出されます.
   * 
   * @param dte ドラッグ&ドロップイベント
   */
  public void dragExit(DropTargetEvent dte) {
  }

  /**
   * ドラッグ&ドロップ(ドロップ時)イベント用メソッドです.
   * ドラッグ操作が進行中のときに呼び出されます.
   * 
   * @param dte ドラッグ&ドロップイベント
   */
  public void dragOver(DropTargetDragEvent dtde) {
  }

  /**
   * ドラッグ&ドロップ(ドロップ時)イベント用メソッドです.
   * ドラッグ操作がドロップで終了したときに呼び出されます.
   * 
   * @param dte ドラッグ&ドロップイベント
   */
  public void drop(DropTargetDropEvent dtde) {
    try {
      // 転送されたデータの取得
      Transferable tr=dtde.getTransferable();
      // 転送データがサポート可能なデータかどうか判定
      if(tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
        // ドロップ動作を受け入れる
        dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

        // 転送データからファイルのリストを抽出
        java.util.List myList=(java.util.List)tr.getTransferData(
          DataFlavor.javaFileListFlavor);
        // リスト要素が単一かどうか判定
        // 複数ファイルのドロップを受け入れるのであれば,判定不要
        if(myList.size()==1) {
          // リストからファイルの取り出し
          File myFile=(File)myList.get(0);
          // ファイルの絶対パス名を表示
          // 実際には,この1行を変更すると良い
          showInformationMessageDialog(myFile.getAbsolutePath());
          // ドロップ動作を正常終了
          dtde.getDropTargetContext().dropComplete(true);
        } else {
          // 「要素が多い」という警告を表示
          showWarningMessageDialog("Too much elements.");
          // ドロップ動作を異常終了
          dtde.getDropTargetContext().dropComplete(false);
        }
      } else {
        showWarningMessageDialog("Unsupported.");
        // ドロップ動作をはねつける
        dtde.rejectDrop();
      }
    } catch (IOException ioe) {
      showErrorMessageDialog("I/O exception.");
      // ドロップ動作をはねつける
      dtde.rejectDrop();
    } catch (UnsupportedFlavorException ufe) {
      showErrorMessageDialog("Unsupported");
      // ドロップ動作をはねつける
      dtde.rejectDrop();
    }
  }

  /**
   * ドラッグ&ドロップ(ドロップ時)イベント用メソッドです.
   * ユーザが現在のドロップジェスチャーを変更したときに呼び出されます.
   * 
   * @param dte ドラッグ&ドロップイベント
   */
  public void dropActionChanged(DropTargetDragEvent dtde) {
  }
}

戻る