javaでファイルのドロップを受け付ける部品を作ってみた

参考にしたのは以下のサイト

改良ポイントは、この部品をほぼ無改造で使い回せること

クラス図

クラス図は以下のような感じ。


使い方

  1. FileDropHandlerのインスタンスを作る
  2. ドロップを受け付けたいコンポーネントのsetTransferHandlerメソッドを使って、FileDropHandlerのインスタンスを登録する。
  3. ドロップイベントを処理するクラス(PropertyChangedListenerを実装)のインスタンスを作る。
  4. FileDropHandlerのインスタンスのaddPropertyChangedLisnenerメソッドを使って、ドロップイベントを処理するインスタンスを登録する。

ドロップ時の処理順序

  1. ファイルがドロップされると、FileDropHandlerのdataImport()が呼び出される。
  2. FileDropHandlerからイベント"fileList"が発火し、ドロップされたファイルのリストがドロップイベントを処理するクラスへ通知される。
  3. ドロップイベントを処理するクラスは、通知されたイベントから、ドロップされたファイルのリストを取り出し処理する。

ソースコード

package filedrophandler;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.TransferHandler;

/**
 * 非同期にファイルのドロップを処理する。
 * @author cnaos
 */
public class FileDropHandler extends TransferHandler {
    public final String FILELIST_P = "fileList";

    private Exception occurredException;
    private final PropertyChangeSupport changes;
    private List<File> fileList;

    public FileDropHandler() {
        this.changes = new PropertyChangeSupport(this);
        this.fileList = new ArrayList();
        this.occurredException = null;
    }

    /**
     * ドロップ可能かどうかを返す
     * @param support
     * @return
     */
    @Override
    public boolean canImport(final TransferSupport support) {
        if (!support.isDrop()) {
            return false;
        }
        return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
    }

    /**
     * ドロップ処理を行う
     * @param support
     * @return
     */
    @Override
    public boolean importData(final TransferSupport support) {
        // 念のためチェック
        if (!canImport(support)) {
            return false;
        }
        this.occurredException = null;
        Transferable transferable = support.getTransferable();
        List<File> importFileList;
        // ドロップ処理
        try {
            Object data =
                    transferable.getTransferData(DataFlavor.javaFileListFlavor);
            importFileList = (List<File>) data;
        } catch (UnsupportedFlavorException e) {
            this.occurredException = e;
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            this.occurredException = e;
            e.printStackTrace();
            return false;
        }


        // イベント受信側で変更されないように防御
        List<File> tmpFileList = new ArrayList(importFileList);
        this.fileList = Collections.unmodifiableList(tmpFileList);
        // 変更の有無にかかわらず発火させるため、old側をnullにしている
        changes.firePropertyChange(FILELIST_P, null, this.fileList);

        return true;
    }

    /**
     * ドロップされたファイルのリストを返す
     * @return
     */
    public List<File> getFileList() {
        return fileList;
    }

    public boolean isExceptionOccurred() {
        return this.occurredException != null;
    }

    public Exception getOccurredException() {
        return this.occurredException;
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        changes.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        changes.removePropertyChangeListener(listener);
    }
}