Swing のデータ転送

JavaTM 2 Platform, Standard Edition, v 1.4 のリリースでは、Swing にアプリケーション間でのデータ転送のサポートが加わりました。 ドラッグ & ドロップ操作はデータ転送の要求であり、グラフィカルポインティングデバイスを使用したジェスチャーで指定します。 コピーやペーストは、多くの場合、キーボードによってデータ転送が指定されます。 データ転送には、次の 2 つの形式があります。

データ転送のサポートを Swing に追加するときに、クリップボード転送と同様にドラッグ & ドロップも、開発者が Swing コンポーネントを使用して簡単に追加できるようにすることが目標でした。 ドラッグ & ドロップに対する以前の AWT サポートは非常に柔軟性がありましたが、実装するにはかなりの複雑さがありました。 また、クリップボード転送に対する AWT サポートは、自然な方法で Swing に統合されていませんでした。 このリリースでは、データ転送のサポートが実装されたので、開発者は容易にこれらの強力な機能を利用することができます。

各 Swing コンポーネントの状態はデータモデルの定義とは独立しているため、データ自体の詳細部分を気にすることなく、コンポーネント間でのデータ移動の機構を実装することが簡単でした。 データ転送は事実上、あるモデルから別のモデルへの部分転送です。 (カット、コピー、ペーストの場合は、モデルとクリップボード間でデータが転送される。) データ転送をサポートするため、次の変更が行われました。

クイックリファレンス - DnD をサポートするコンポーネント

Componentドラッグのサポートドロップのサポート
JColorChooser X X
JEditorPane X X
JFileChooser X
JFormattedTextField X X
JLabel ここを参照 ここを参照
JList X
JPasswordField X
JTable X
JTextArea X X
JTextField X X
JTextPane X X
JTree X

このドキュメントには次の節があります。

データ転送のサポート

データ転送の基本は、データをコンポーネントへ、またはコンポーネントから転送する処理のサポートです。 コンポーネントでこの機能が利用可能な場合は、ドラッグ & ドロップの管理機構が自動的に提供されます。 さらに、カット、コピー、ペーストのサポートも自動的に提供されます。 この実装で最も重要な点は、 TransferHandler 新規クラスの実装です。 JComponent プロパティメソッド、 setTransferHandler および getTransferHandler で、JComponent を拡張するすべてのコンポーネントに対するデータ転送機構のエントリポイントが提供されます。

null 以外の TransferHandler がインストールされているときに、各コンポーネントに対してサポートされる「カット」、「コピー」、「ペースト」、および「ドロップ」の範囲を次の表に示します。 このサポートは、Look & Feel コードによって有効になります。 最初はドラッグのサポートが無効にされていますが、コンポーネント上で setDragEnabled(true) を呼び出して有効にすることができます。 コンポーネントにこのようなメソッドがない場合は、何らかのジェスチャーをバインドして、ドラッグを有効にできます。

デフォルトの TransferHandler のサポート

以下に挙げたコンポーネントについては、Swing が TransferHandler のデフォルトの実装をインストールします。 デフォルトの Swing サポートがあるコンポーネントでは、transferHandler プロパティの値が null であるか、または UIResource インタフェースによってマークされている場合は、TransferHandlerComponentUI によってインストールされます。 ComponentUI にインストールされたデフォルトの TransferHandler 実装は、開発者がデフォルトの TransferHandler をオーバーライドできるようにして、UIResource インタフェースによってマークされます。 次の表に提供されるサポートを示します。

DnD をサポートするコンポーネント

Component エクスポート (ドラッグ、カット、コピー) インポート (ドロップ、ペースト)
JColorChooser 選択したカラーが、TransferHandler の JavaBeansTM プロパティ処理を通じて提示される。 提示されたフレーバは、application/x-java-jvm-local-objectref; class=java.awt.Color である。 転送される値は getColor によって決定される。 Color 型の挿入を受け入れる。 データは、Beans のプロパティ処理から setColor を使用してインポートされる。 class=java.awt.Color または java.awt.Color のサブクラスを持つすべてのフレーバが受け入れられる。
JEditorPane JEditorPanecontent-typetext/plain である場合、選択したテキストは、EditorKitwrite メソッドを通して text/plain として提示されます。 content-typeOTHER である場合は、getSelectedText メソッドを使って text/plain として、および、EditorKitwrite メソッドを使って OTHER としてエクスポートされます。 text/plain 型の挿入および getContentType から返された現在値をすべて受け入れる。 現在の型 (text/plain も含む) に一致する型が見つかると、EditorKitread メソッドを使ってインポートされます。 それ以外の場合は、replaceSelection メソッドを通して text/plain がインポートされます。 ペースト - 選択している内容があれば、それが置換される。 ドロップ - キャレットの位置にデータが挿入される。
JFileChooser ネイティブのファイルチューザと同じ方法で選択範囲が提示される。 インポートは受け入れられない。 マウスがリスト上に移動すると、カーソルの下のファイルやディレクトリが強調表示されて、「潜在的な」ドロップポイントが示される。 「デフォルトのドロップのサポート」を参照。
JFormattedTextField 選択したテキストが text/plain として提示される。 text/plain 型の挿入を受け入れる。 ペースト - 選択している内容があれば、それが置換される。何も選択していない場合は、キャレットの位置にデータが挿入される。 ドロップ - キャレットの位置にデータが挿入される。 「ペースト」と「ドロップ」は、キーボードからの入力時と同じコードパスを採用するので、コードの検証が保存される。
JList 1 項目が選択された場合は、text/plain として提示される。 複数の項目が選択された場合は、text/html として提示される。 text/html の形式は、<UL> タグのあとに、それぞれが <LI> タグで始まる選択したリスト項目が続く。 インポートは受け入れられない。 マウスがリスト上に移動すると、カーソルの下のリスト項目が強調表示されて、「潜在的な」ドロップポイントが示される。 「デフォルトのドロップのサポート」を参照。
JPasswordField セキュリティ上の理由から、カット、コピー、およびドラッグはサポートされない。 text/plain 型の挿入を受け入れる。 ペースト - 選択している内容があれば、それが置換される。何も選択していない場合は、キャレットの位置にデータが挿入される。 ドロップ - キャレットの位置にデータが挿入される。
JTable 1 項目が選択された場合は、text/plain として提示される。 複数の項目が選択された場合は、text/html として提示される。 text/html の形式は、<TABLE> タグのあとに各行には <TR> タグ、各セルには <TD> タグが続く。 インポートは受け入れられない。 マウスが表の上に移動すると、カーソルの下のセルが強調表示されて、「潜在的な」ドロップポイントが示される。 「デフォルトのドロップのサポート」を参照。
JTextArea 選択したテキストが text/plain として提示される。 text/plain 型の挿入を受け入れる。 ペースト - 選択している内容があれば、それが置換される。何も選択していない場合は、キャレットの位置にデータが挿入される。 ドロップ - キャレットの位置にデータが挿入される。
JTextField 選択したテキストが text/plain として提示される。 text/plain 型の挿入を受け入れる。 ペースト - 選択している内容があれば、それが置換される。何も選択していない場合は、キャレットの位置にデータが挿入される。 ドロップ - キャレットの位置にデータが挿入される。
JTextPane JEditorPane と同じ JEditorPane と同じ
JTree 1 項目が選択された場合は、text/plain として提示される。 複数の項目が選択された場合は、text/html として提示される。 text/html の形式は、JList に使用される形式と似た、入れ子されたリストである。 インポートは受け入れられない。 マウスが表の上に移動すると、カーソルの下のツリーノードが強調表示されて、「潜在的な」ドロップポイントが示される。 「デフォルトのドロップのサポート」を参照。

JLabel へのサポート追加の例

特に指定しない限り、 JLabel コンポーネントで DnD はサポートされません。 次のコーディングにより、「テキスト」のプロパティ (String) でドラッグ & ドロップをサポートする JLabel が作成されます。

JLabel componentType = new JLabel();
componentType.setTransferHandler(new TransferHandler("text"));
MouseListener ml = new MouseAdapter() {
    public void mousePressed(MouseEvent e) {
        JComponent c = (JComponent)e.getSource();
        TransferHandler th = c.getTransferHandler();
        th.exportAsDrag(c, e, TransferHandler.COPY);
    }
};
componentType.addMouseListener(ml);

JTextFieldJLabel を使用してドラッグ & ドロップを実装した小さな例も含めてあります。 テキストフィールドに値を入力し、テキストを強調表示した後に、テキストフィールドの上にマウスボタンを置いて下に押さえ、ピクセルをドラッグすることができます。 アイコンはカーソルの下に表示されます。 JLabel の上のアイコンを解放して、テキストが「Drop Here」テキストに置き換わったことを確認してください。 ドロップすると同時に、ソースのテキストフィールドからテキストが削除されます。 ドラッグ & ドロップのデフォルトの動作は MOVE です。 この動作を COPY に変更するには、テキストを選択するときに Ctrl キーを押します。 Windows では、アイコン内にプラス記号が表示されます。 テキストがターゲット上に貼り付けられても、ソース内のテキストはそのまま残ります。 同様に、JLabel からテキストフィールドへドラッグすることもできます。 JLabel にはコピーおよびペーストのバインドがなく、この機能をサポートするために必要なフォーカスを取得することができないことに留意してください。

JLabelDragNDrop.javaここの例を参照してください。

ドラッグのサポート

テキストフィールドなどのコンポーネントは、選択範囲をサポートします。 この種のコンポーネントの場合、ドラッグ操作は通常、既存の選択範囲をマウスでドラッグすることで開始されます。 この型のコンポーネントのコントローラがこの状態を認識することができ、ドラッグを開始します。 選択範囲のないコンポーネントでは、Swing がドラッグを自動的に開始できませんが、JLabel の例にあるようにドラッグが試行されると、ドラッグの「機構」を処理することはできます。

このレベルのドラッグサポートで、Swing の開発者は、実行したい転送を表現する TransferHandler の実装、および Swing コンポーネントでのプロパティの設定に集中することができます。 これで、アプリケーション内でドラッグをサポートしようとする Swing 開発者の負担が軽減されます。

現在は、ドラッグのさまざまな視覚上の表現方法 (API 内の Icon への参照) を調査中です。 今後の進展に注意していてください。

デフォルトのドラッグのサポート

サポートのもっとも単純なレベルは、開発者がデフォルトのサポートが有効にされるのを望んでいることを示すフラグの設定です。 ドラッグのデフォルトのサポートを提示するコンポーネントについては、「DnD をサポートするコンポーネント」の表の「エクスポート」の欄に説明がありますが、要約すると、 setDragEnabled および getDragEnabled を直接実装する以下のクラスがあります。 JColorChooserJFileChooserJListJTableJTree、および JTextComponent がそのクラスです。 ドラッグのジェスチャーは、選択範囲の上でマウスの左ボタンを押して、いくつかのピクセルをドラッグすることと定義されます。 そのため、dragEnabled プロパティを true に設定すると、マウスの動作に関して微妙な効果があります。

ドラッグがコンポーネント上で有効にされると、対応する ComponentUI サブクラス内の Swing コントローラがドラッグのジェスチャーの検索を開始し、transferHandler プロパティが null であるか、 UIResource インタフェースによってマークされていると、デフォルトの実装がインストールされます。 Swing によって提供された実装が UIResource インタフェースでマークされるので、開発者は、デフォルトの TransferHandler プロパティをカスタムの実装で置き換えて、新しい TransferHandler を挿入することができます。 ドラッグのジェスチャーが認識されると、ドラッグ機構を開始する TransferHandler 上で exportAsDrag メソッドが呼び出されます。 実際の転送は java.awt.dnd 機構によって処理され、開発者にとってこれ以上の労力は必要ありません。

Swing クラスのサブクラスへのドラッグのサポートの追加

Swing に提供されるドラッグのサポートは、TransferHandler.exportAsDrag メソッドによって起動します。 これによって、次のように簡単にドラッグのサポートを追加することができます。

  1. setTransferHandler メソッドを呼び出して (ドラッグの対象がある場合)、TransferHandler の実装をインストールする。
  2. ドラッグのジェスチャーを認識する。
  3. exportAsDrag メソッドを呼び出す。

これを行うシナリオには、次のようなものがあります。

TransferHandler.exportAsDrag の操作

exportAsDrag メソッドが TransferHandler 上で呼び出されると、Swing 提供の機能によってドラッグが処理されます。 このメソッドが呼び出される場合は、有効なドラッグジェスチャーが認識されたとみなされます。 このメソッドでは、次の手順が実行されます。

  1. DragSource および DragListener の Swing 実装を使用して、データ転送オブジェクトとして createTransferable から返された Transferable 実装を使用するドラッグを開始する。
  2. ドロップが終了すると、exportDone メソッドが TransferHandler 上で呼び出される。 MOVE などのいくつかの操作の場合は、ソースからデータを削除することが必要な場合もある。 たとえば、テキストベースのコンポーネントでのデフォルトの動作は、標準的なドラッグに MOVE の動作が実装される (ドロップに続いて、選択したテキストはソースコンポーネントから削除される)。 テキストをドラッグ用に選択するときに Ctrl キーを押し下げると、この動作をオーバライドできる。 Windows では、小さなプラス記号の付いたドラッグアイコンによって、コピー動作が視覚的に示される。

ドロップのサポート

ドロップを取り扱う主要なサポートは、ペースト操作、つまり TransferHandler importData メソッドでの場合と同じです。 コンポーネントへのデータ挿入のセマンティクスは、通常、ツールキットのレベルに比べてアプリケーションレベルでの意味のほうが多くなっています。 アプリケ−ションのユーザは、Swing ツールキットにとってまったく未知のコンポーネント上ですべての種類の Transferable 実装をドロップできます。 デフォルトの TransferHandler が不十分な場合は、意味の多いインポートのセマンティクスで TransferHandler を提供するのが Swing 開発者の責任です。

ただし、ドロップの操作はペーストと多少違います。

デフォルトのドロップのサポート

ドロップのサポートのアプリケーションへの追加で Swing 開発者を支援するために、Swing によって、Swing コンポーネント上で TransferHandler プロパティを使用する DropTarget の実装が提供されます。 null 以外の値の TransferHandler プロパティがあるコンポーネントでは、dropTarget プロパティの値が null であるか、または UIResource インタフェースの存在によってマークされていれば、ドロップターゲットが ComponentUI によってインストールされます。 ComponentUI によってインストールされたデフォルトの DropTarget 実装は、開発者がデフォルトの DropTarget をオーバーライドできるように、UIResource インタフェースによってマークされます。

ドロップの完全な実装をデフォルトで提供するコンポーネントは、「DnD をサポートするコンポーネント」表の「インポート」欄に示されています。 ドロップを一部だけサポートしているコンポーネントは 4 つあります。 JFileChooserJListJTable、および JTree には、潜在的な挿入ポイントを示す DropTarget がデフォルトでインストールされています。 ただし、データのインポートを完全にサポートするには、開発者がカスタムの TransferHandler を作成してインストールする必要があります。

Swing クラスのサブクラス内でのドラッグサポートの使用

DropTarget がうまく動作する実装では、ドロップを実行するために JComponenttransferHandler プロパティが使用されています。 transferHandler プロパティが設定され、dropTarget プロパティが null の場合に、Swing の開発者は、ドロップを TransferHandler にリンクさせる単純な DropTarget 実装を自動的に取得します。

クリップボード転送のサポート

カット、コピー、およびペースト操作は、通常はキーボードから起動されるため、マウスを使用せずにより有用なデータ転送を実行します。 これらの操作は、ドラッグ & ドロップのサポートと同じ TransferHandler サービスを使用できます。 これによってデータ転送の使い易さが向上し、Swing で実際のクリップボード間とのデータ転送ができない場合であっても、このような操作のためのキーボードバインドを Swing で提供できるようになります。 クリップボード用のキーボードバインドは、インストールされている現在の Look & Feel 機能に依存しています。

デフォルトのクリップボード転送

ドラッグ & ドロップと同様に、クリップボードによる転送は、選択範囲をサポートするコンポーネントにとってもっとも役に立ちます。 「DnD をサポートするコンポーネント」表の「エクスポート」欄に示したコンポーネントのリストには、コンポーネントの入力マップ内のキーボードバインドに関する LAF に依存するセットに加えて、コンポーネントのアクションマップ内に「カット」、「コピー」、および「ペースト」のアクションがあります。 Swing でこれらのバインドが提供されるので、開発者は、現在の Look & Feel の追跡とバインドの変更をしないで済みます。

Swing クラスのサブクラス内でのクリップボード転送サポートの使用

カット、コピー、およびペーストがうまく動作する実装では、転送を実行するために JComponenttransferHandler プロパティが使用されています。 transferHandler プロパティが JComponent 上に設定されていると、現在インストールされているアクションが何もなければ、 ClipboardTransferHandler 間の転送にリンクする単純な Action 実装が、コンポーネントのアクションマップ内の cutcopy、および paste キーの下にインストールされます。

実装の詳細

注 - 以下の説明は、非公開の実装の詳細についてと、変更の主題についてです。 この情報は、ソースコードにアクセスして、特に興味をお持ちの方々に対してだけ提供します。

TransferHandlerJComponent にインストールされている場合、Look & Feel は、次の TransferHandler メソッドを使った「カット」、「コピー」、および「ペースト」処理を有効にします。

イベントを TransferHandler.exportAsDrag にバインドした場合は、「ドラッグ」サポートも入手できます。 この処理は、TransferHandler クラス内部のパッケージ専用の入れ子クラス内にあります。

以下のパッケージ専用のクラスが、デフォルトのサポートを作成するために javax.swing.plaf.basic パッケージに追加されました。

BasicTransferable
さまざまな Transferable 実装の基本
BasicDropTargetListener
選択範囲をサポートするコンポーネント用にスクロールのサポートとドロップターゲットのフィードバックを追加
BasicDragGestureRecognizer
「数ピクセルの選択と移動」のドラッグジェスチャーの認識のサポート

DnD をサポートする各 ComponentUI クラスには、以下の追加された入れ子のクラスがあります。

BasicTreeUI:

TreeTransferHandler
デフォルトのインポート/エクスポートのサポート
TreeTransferable
実際のツリー構造のデータ
TreeDragGestureRecognizer
JTree.setDragEnabled によって有効にされる、ツリードラッグジェスチャーの認識
TreeDropHandler
ドラッグターゲットの位置の表示

BasicListUIBasicTableUI、および BasicTextUI はすべて、BasicTreeUI と似たパターンを持っています。

BasicFileChooserUI
リストサポートを使用するが、FileTransferHandler があり、ファイル転送で使用されるフレーバで ListTransferable を拡張する FileTransferable を作成する。
BasicColorChooserUI
TransferHandler の Beans サポートを使用し、カラーに関するプロパティをバインドする。 マウスリスナーが、マウスで押したものを TransferHandler 上の exportAsDrag メソッドにバインドするプレビュー領域に追加される。

Beta 以降の変更点

TransferHandler のアクションは UI で登録することになった

この変更に関連するバグレポート: 4460011.

ドラッグ&ドロップを実装するために、Action クラスでバインディングを提供して、データのコピーとペーストをサポートしていました。 ActionTransferHandler で自動的にインストールされなくなったため、Look & Feel などのコードでこれらの Action をオーバーライドするには、UI を通してこれらのアクションをインストールする必要があります。 TransferHandler getCutAction getCopyAction、および getPasteAction の各メソッドは、package private から public に変更されました。

DropTarget を無効にする機能

この変更に関連するバグレポート: 4485914.

ドラッグ&ドロップを実装すると、JComponentTransferHandler がインストールされている場合、JComponent に DropTarget が追加されます。 手動で DropTarget を追加しているアプリケーションは、これによって影響を受ける可能性があります。 そのため、transferHandler というシステムプロパティが追加されました。 JComponentsetTransferHandler(TransferHandler) メソッドと getTransferHandler メソッドを使って、このプロパティを有効にしたり無効にしたりできます。

既知のバグ

JTextComponent で選択したテキストをドロップするとテキストが壊れる

この問題に関連するバグレポート: 4513715.

テキストを選択し、同じテキストコンポーネントの選択領域の中にドラッグ&ドロップすると、選択したテキストが壊れます。 将来、このようなドラッグ&ドロップ操作では何の処理も実行しないように変更する予定です。

コンポーネントから出るときキャレットの復元エラーが発生する

この問題に関連するバグレポート: 4513720.

組み込みのドロップサポートにより、Swing テキストコンポーネントは、ドラッグ&ドロップのドラッグオーバーに反応して、Caret を可視に設定し、そのキャレットを使って潜在的な挿入ポイントを示します。 ドラッグがコンポーネントの外に出ると、Caret の可視性は元の状態に復元されます。 ところが、Caret で使用する visible プロパティは、これ 1 つで通常の「可視」という意味と点滅状態との両方を表すため、Caret の可視性は正しく復元されないことがあります。

RTFEditorKit の設定された JEditorPane に対するカット、コピー、ドラッグが無効になった

この問題に関連するバグレポート: 4513638.

JEditorPaneRTFEditorKit を設定するには、明示的に setEditorKit を使用する方法や、content-typetext/rtf を指定する方法があります。このようにして設定された RTFEditorKit を使用するとき、カット、コピー、およびドラッグは動作しなくなりました。