ヘッダーをスキップ
Oracle Fusion Middleware Oracle Application Development Framework Webユーザー・インタフェース開発者ガイド
11gリリース1(11.1.1)
B52029-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

32 ドラッグ・アンド・ドロップ機能の追加

この章では、ユーザーがコンポーネントの属性の値をドラッグして別のコンポーネントにドロップしたり、あるコレクションから別のコレクションにアイテムをドラッグしたり、ドラッグしてコンポーネントの親やコンポーネントの順序(親が複数のコンポーネントを持つ場合)を変更する機能をページに追加する方法について説明します。


注意:

ADF Faces calendarコンポーネントでは、Calendarの異なる領域へのアクティビティのドラッグ・アンド・ドロップがサポートされています。Calendarでのドラッグ・アンド・ドロップ機能の使用の詳細は、15.5項「Calendarへのドラッグ・アンド・ドロップ機能の追加」を参照してください。

この章に含まれる項は次のとおりです。

32.1 ドラッグ・アンド・ドロップ機能の概要

ページ上にコンポーネントを構成して、あるコンポーネントの値を他のコンポーネントにドラッグ・アンド・ドロップできます。たとえば、図32-1に示すように、outputTextコンポーネントをそのタイトルにドラッグ・アンド・ドロップできるように、panelBoxコンポーネントを構成できます。その結果、panelBoxのtext属性は、ドラッグしたoutputTextコンポーネントのvalue属性から値を取得します。

図32-1 属性のドラッグ・アンド・ドロップ

属性値をドラッグ・アンド・ドロップできます

ドロップを受け入れるコンポーネントは、ターゲットと呼ばれます。ドラッグされ、値を含むコンポーネントは、ソースと呼ばれます。コンポーネントをターゲットとして構成するには、attributeDropTargetタグをコンポーネントの子として追加します。ソースを構成するには、attributeDragSourceタグを追加します。このタグも値の代入元の属性で構成します。

attributeDropTargetタグを使用することで、あるコンポーネントから別のコンポーネントに属性値のみをコピーできます。値のコピーではなく、移動の必要がある場合も考えられます。実際のオブジェクトをドラッグ・アンド・ドロップできる柔軟性が求められる場合も考えられます。このような場合は、attributeDropTargetタグを使用するかわりに、dropTargetタグを使用します。このタグを使用すると、コピー(コピーして貼付け)、移動(切り取って貼付け)、リンク(コピーしてリンクとして貼付け(テキストをコピーして実際のURLとして貼り付けるなど))のうち、値に有効なアクションを構成できます。

必要なロジックを追加できるDropEventイベントのリスナーを実装できるという点で、柔軟性も増します。また、ドロップ・イベントを受けてクライアントでロジックを起動するJavaScriptクライアント・リスナーを実装できます。

dropTargetタグを使用する場合、dataFlavorタグを追加してドロップできるオブジェクトのJava型を制限できます。これは、ターゲットで1つのオブジェクト型を受け入れるのに対し、ソースの値は様々な型である可能性がある場合に有用です。dataFlavorタグでは、ターゲットで複数のソースまたは複数の型を含む可能性のあるソースからオブジェクトを受け入れることができるよう、複数の型を設定することもできます。ドロップが成功するには、ターゲットとソースの両方にdataFlavorタグを含め、値が同じである必要があります。

属性値やオブジェクトのドラッグに加え、コレクションもドラッグできます。collectionDropTargetおよびcollectionDragSourceタグを使用して、あるコレクションから別のコレクションにオブジェクトをドラッグ・アンド・ドロップできます。たとえば、File Explorerアプリケーションでは、ユーザーは、ディレクトリの内容を表示する表からディレクトリ・ツリーのフォルダにファイルをドラッグできます。図32-2に、Folder0ディレクトリの内容を表示する表からFolder3ディレクトリにドラッグされるFile0.docオブジェクトを示します。ドロップが完了すると、オブジェクトはFolder3を構成するコレクションの一部になります。

図32-2 File Explorerアプリケーションでのドラッグ・アンド・ドロップ機能

ディレクトリへのファイルのドラッグ

単一オブジェクトのドラッグ・アンド・ドロップ同様、ドロップによってコピー、移動またはコピーしてリンクとして貼り付けたり(あるいは3つを組み合せたり)、dataFlavorタグを使用してターゲットでの受け入れ対象を限定できます。dropTargetタグ同様、ドロップの前後でロジックを処理するリスナーを実装する必要があります。

より複雑なページでは、複数のターゲットがあり、そのすべてで同じ型を受け入れる場合も考えられます。ターゲットでのドロップ対象をさらに制限するには、dataFlavorタグの識別子属性に一意のString値を設定します。同じ識別値を持つソースのみをドロップできます。


注意:

識別値は、コレクションのドラッグ・アンド・ドロップでのみ機能します。

コンポーネントもドラッグ・アンド・ドロップできます。たとえば、ページのpanelBoxコンポーネントをドラッグして順序を変更できます。


注意:

ドラッグ・アンド・ドロップ機能は、ウィンドウ間ではサポートされていません。ウィンドウ境界を越えるドラッグは取り消されます。ポップアップ・ウィンドウとポップアップのベース・ページとの間のドラッグ・アンド・ドロップ機能はサポートされています。

ドラッグ・アンド・ドロップ機能はアクセシブルではないことにも注意してください。ドラッグ・アンド・ドロップに使用できるキーボード・ストロークはありません。したがって、アプリケーションのすべての機能がアクセシブルである必要がある場合、このロジックを作成する必要があります。


32.2 属性のドラッグ・アンド・ドロップ機能の追加

属性のドラッグ・アンド・ドロップ機能は、あるコンポーネントの属性をターゲットとして定義し、別のコンポーネントの属性をソースとして定義して追加します。


注意:

ターゲットとソースの属性値は同じデータ型である必要があります。

次の手順では、ターゲットとソースのコンポーネントはJSFページにすでにあるものとします。

属性のドラッグ・アンド・ドロップ機能を追加する手順:

  1. コンポーネント・パレットで「操作」セクションを開き、属性ドロップ・ターゲットをターゲット・コンポーネントの子としてドラッグ・アンド・ドロップします。

  2. 「属性ドロップ・ターゲットの挿入」ダイアログで「属性」ドロップダウン・リストを使用して、ドラッグ・アンド・ドロップ・アクションによる移入先の属性を選択します。このドロップダウン・リストには、ターゲット・コンポーネントの有効な属性がすべて表示されます。

  3. ターゲットへの値の代入元のコンポーネントの子として、属性ドラッグ・ソースをコンポーネント・パレットからドラッグ・アンド・ドロップします。

  4. 「属性ドラッグ・ソースの挿入」ダイアログで「属性」ドロップダウン・リストを使用して、ターゲット属性への移入に使用される値の属性を選択します。このドロップダウン・リストには、ソース・コンポーネントの有効な属性がすべて表示されます。

32.3 オブジェクトのドラッグ・アンド・ドロップ機能の追加

ユーザーが属性値以外をドラッグできるようにする場合、またはあるコンポーネントから別のコンポーネントへ属性をコピーする以外の操作をユーザーができるようにする場合、dropTargetタグを使用します。また、DataFlavorオブジェクトを使用して、ドロップ・ターゲットに対して有効なソースのJava型を決めます。ドロップ・ターゲットとドラッグ・ソースが複数ある場合のために、識別値を使用して有効な組合せを制限できます。ドラッグ・アンド・ドロップ・アクションを受けて必要となる機能の実装も必要です。

たとえば、図32-3に示すように、outputTextコンポーネントがあり、ユーザーがoutputTextコンポーネントをpanelBoxコンポーネントにドラッグできるようにし、そのコンポーネントにarrayを表示するとします。

図32-3 配列オブジェクトのドラッグ・アンド・ドロップ

オブジェクトのドラッグ・アンド・ドロップ

outputTextコンポーネントにはattributeDragSourceタグが含まれます。ただし、(属性のString値のみでなく)arrayをドラッグするため、attributeDropTargetタグではなくdropTargetタグを使用する必要があります。また、dataFlavorタグを使用してarrayオブジェクトのみがターゲットで受け入れられるようにします。

dataFlavorタグの識別値の定義もできます。同じオブジェクト型の2つのターゲットと2つのソースがある場合に、これは有用です。識別値を作成することで、各ターゲットで有効なソースのみが受け入れられることが保証されます。たとえば、EMPLOYEEオブジェクトを受け入れるTargetAとTargetBの2つのターゲットがあるとします。EMPLOYEEオブジェクトのソースも2つあるとします。TargetAにalphaという値を使用して識別値を設定することにより、識別値alphaを持つEMPLOYEEソースのみが受け入れられます。

ドロップ・イベントのリスナーも実装する必要があります。ドロップ・イベントのオブジェクトはtransferableと呼ばれ、ドロップのペイロードを含みます。リスナーはtransferableオブジェクトにアクセスし、ここからDataFlavorオブジェクトを使用してオブジェクトがドロップ可能なことを確認します。その後、ドロップ・イベントを使用してターゲット・コンポーネントを取得し、ドロップされたオブジェクトを使用してプロパティを更新します。このリスナーの詳細は、32.3.1項「単一オブジェクトのドラッグ・アンド・ドロップ機能の追加方法」の手順で説明します。

32.3.1 単一オブジェクトのドラッグ・アンド・ドロップ機能の追加方法

ドラッグ・アンド・ドロップ機能を追加するには、まず、コンポーネントをドラッグ・アンド・ドロップ・アクションのターゲットとして定義するタグをコンポーネントに追加します。次に、ドラッグ・アンド・ドロップ・アクションのロジックを処理するイベント・ハンドラ・メソッドを実装します。最後に、ドラッグ・アンド・ドロップのソースを定義します。

この手順では、ソースとターゲットのコンポーネントはすでにページあるものとします。

ドラッグ・アンド・ドロップ機能を追加する手順:

  1. ターゲットを含むJSFページで、コンポーネント・パレットから(「操作」パネルにある)「ドロップ・ターゲット」タグをドラッグ・アンド・ドロップし、dropTargetタグをターゲット・コンポーネントの子として追加します。

  2. 「ドロップ・ターゲットの挿入」ダイアログで、イベントを処理するマネージドBeanのメソッド(このコードはステップ5で作成)に評価される式を入力します。


    ヒント:

    clientDropListener属性を挿入して、クライアントでドロップを捕捉することもできます。詳細は、32.3.3項「ClientDropListenerの使用について」を参照してください。

  3. 「データ・フレーバの挿入」ダイアログで、ターゲットにドロップ可能なオブジェクトのクラス(java.lang.Objectなど)を入力します。この選択は、dataFlavorタグを作成するために使用されます。このタグにより、ターゲットにドロップできるオブジェクトの型(StringDateなど)が決まります。1つのドロップ・ターゲットに複数のdataFlavorタグを使用し、ドロップ・ターゲットでこれらの型のいずれも受け入れるようにすることができます。


    ヒント:

    DataFlavorタグに型指定された配列を指定するには、java.lang.Object[]のようにクラス名に大カッコ([])を追加します。

  4. 構造ウィンドウで、dropTargetタグを選択します。プロパティ・インスペクタで、actions属性に値を選択します。これによって、ドロップ・ターゲットでサポートされるアクションが定義されます。有効な値は、COPY(コピーして貼付け)、MOVE(切り取って貼付け)およびLINK(コピーしてリンクとして貼付け)です。

    MOVE COPY
    

    アクションが指定されない場合、デフォルトはCOPYです。

    例32-1に、panelBoxコンポーネントに挿入され、配列オブジェクトをドロップ・ソースとするdropTargetコンポーネントのコードを示します。アクションが定義されていないため、許可されるアクションはCOPYのみであることに注意してください。

    例32-1 dropTargetタグのJSPコード

    <af:panelBox text="PanelBox2">
      <f:facet name="toolbar"/>
      <af:dropTarget dropListener="#{myBean.handleDrop}">
        <af:dataFlavor flavorClass="java.lang.object[]"/>
      </af:dropTarget>
    </af:panelBox>
    
  5. ステップ2で作成したEL式で参照されるマネージドBeanに、ドラッグ・アンド・ドロップ機能を処理するイベント・ハンドラ・メソッドを(EL式での名前と同じ名前を使用して)作成します。

    このメソッドはDropEventイベントをパラメータとし、DnDActionオブジェクトを返す必要があります。これは、ソースがドロップされたときに実行されるアクションです。有効な戻り値は、DnDAction.COPYDnDAction.MOVEおよびDnDAction.LINKで、手順4でターゲットの属性を定義したときに設定されます。このメソッドではDropEventイベントを確認して、ドロップを受け入れるどうかを判断します。メソッドでドロップを受け入れる場合、ドロップが実行され、実行したDnDActionオブジェクトが返されます。そうでない場合は、ドロップが拒否されたことを示すDnDAction.NONEが返されます。

    このメソッドでは、各dataFlavorオブジェクトの存在も優先順に確認される必要があります。


    ヒント:

    ターゲットに複数のdataFlavorオブジェクトが定義されている場合、Transferable.getSuitableTransferData()メソッドを使用できます。このメソッドでは、Transferableオブジェクトで使用可能なTransferDataオブジェクトのListが適合性の高いものから順に返されます。

    DataFlavorオブジェクトでは、ドロップされるデータの型(java.lang.Objectなど)が定義されます。これは、手順3で作成したJSPのDataFlavorタグでの定義と同じである必要があります。


    ヒント:

    DataFlavorオブジェクトに型指定された配列を指定するには、java.lang.Object[]のようにクラス名に大カッコ([])を追加します。

    DataFlavorオブジェクトでは多相性がサポートされるため、ドロップ・ターゲットでjava.util.Listが受け入れられる場合、転送可能オブジェクトにjava.util.ArrayListを含むドロップは正常終了します。同様に、この機能ではArraysListsとの間の自動変換もサポートされます。

    ドラッグ・アンド・ドロップ・フレームワークで、サーバーのDataFlavorオブジェクトをクライアント・コンポーネントで表す方法がわからない場合、すべてのドロップがクライアントで正常終了するようドロップ・ターゲットが構成されます。


    例32-2に、イベント・ハンドラ・メソッドでコールするprivateメソッドを示します(イベント・ハンドラ自体は、このメソッドをコールする以外は何もしません。これが必要なのは、このメソッドにもpanelBoxコンポーネントのoutputTextコンポーネントの値となるStringパラメータが必要なためです)。このメソッドでは、イベント・ペイロードから配列オブジェクトをコピーし、イベントを起動したコンポーネントに割り当てます。

    例32-2 dropListenerのイベント・ハンドラ・コード

    public DnDAction handleDrop(DropEvent dropEvent)
    {
      Transferable dropTransferable = dropEvent.getTransferable();
              Object[] drinks = dropTransferable.getData(DataFlavor.OBJECT_ARRAY_FLAVOR);
    
        if (drinks != null)
        {
          UIComponent dropComponent = dropEvent.getDropComponent();
    
    // Update the specified property of the drop component with the Object[] dropped
          dropComponent.getAttributes().put(propertyName, Arrays.toString(drinks));
    
          return DnDAction.COPY;
        }
        else
        {
          return DnDAction.NONE;
        }
      }
    
  6. コンポーネント・パレットから(「操作」パネルにある)クライアント属性をドラッグしてclientAttributeタグをソース・コンポーネントの子として追加します。このタグは、イベントに対するソースのペイロードの定義に使用されます。プロパティ・インスペクタで、clientAttributeタグに次の定義を行います。

    • 名前: ペイロードの名前を入力します。

    • 値: ペイロードの値と評価されるEL式を入力します。飲み物を例とすると、これは異なる飲み物の値を含むArrayに解決されます。

  7. ソース・コンポーネントの別の子として、パレットから(「操作」パネルにある)属性ドラッグ・ソースをドラッグ・アンド・ドロップします。「属性ドラッグ・ソースの挿入」ダイアログで、前述の手順で作成したclientAttributeタグに定義するnameをドロップダウン・リストから選択します。これにより、clientAttributeタグの値がソースのペイロードになります。例32-3に、ドラッグ・アンド・ドロップ操作のソースであるoutputTextコンポーネントのコードを示します。

    例32-3 ドラッグ・ソースのJSPコード

    <af:outputText value="Drag to see drinks">
      <af:clientAttribute name="drinks" value="#{myBean.drinks}"/>
      <af:attributeDragSource attribute="drinks"/>
    </af:outputText>
    

32.3.2 実行時の処理

ドラッグ・アンド・ドロップ操作時、ユーザーはキーボードのキーを押して(キーボード修飾子と呼ばれます)、ドラッグ・アンド・ドロップで行われるアクションを選択できます。ドラッグ・アンド・ドロップ・フレームワークでは、次のキーボード修飾子がサポートされます。

  • [Shift]: MOVE

  • [Ctrl]: COPY

  • [Alt]: LINK

ユーザーがドラッグ・アンド・ドロップ操作を行うと、ドロップ・ターゲットで、まず、ドラッグ・ソースのデータ形式の値が受け入れ可能かどうかを判断します。ソースとターゲットがコレクションの場合、次に、フレームワークでドラッグ・ソースとドロップ・ターゲットとの間で可能なアクションの論理積を求め、論理積のアクション(COPY、MOVEまたはLINKのうち1つ)をこの順に実行します。有効なアクションが1つのみの場合、そのアクションが実行されます。複数のアクションが可能で、ユーザーのキーボード修飾子がそのうちの1つに合致する場合、それが実行されます。キーボード修飾子が使用されない、あるいはキーボード修飾子と可能なアクションが合致しない場合、可能なアクションのセットからCOPY、MOVE、LINKがこの順にフレームワークで選択されます。

たとえば、COPYとMOVEをサポートするドロップ・ターゲットがあるとします。まず、ドロップ・ターゲットで、ドラッグ・ソースが有効なデータ形式かどうかが判断されます。次に、ユーザーがドロップしたときに実行するアクションを判断します。この例では、セットはCOPYおよびMOVEです。ドラッグ中にユーザーが[Shift]キー(MOVEのキーボード修飾子)を押すと、フレームワークでMOVEアクションが選択されます。ドラッグ中にユーザーが[Shift]キーを押す以外の操作をした場合、修飾子キーが選択されていない場合のデフォルトはCOPY(順序内で最初)のため、アクションはCOPYになります。ユーザーが[Ctrl]キーを押した場合、修飾子がCOPYに対応するため、COPYが行われます。ユーザーが[Alt]キーを押した場合、アクションはCOPYになります。これは、修飾子はLINKアクションに対応しますが、許可されているアクションの論理積セットにLINKがないためです。


注意:

JavaとJavaScriptとの間のラウンドトリップで情報が失われるため、ドロップでのデータが見込みとは異なる型の場合があります。たとえば、すべての数値型はdoubleオブジェクトとして、charオブジェクトはStringオブジェクトとして、ListオブジェクトとArrayオブジェクトはListオブジェクトとして、その他の大部分のオブジェクトはMapオブジェクトとして表されます。詳細は、5.4.3項「データのマーシャリングとアンマーシャリングについて」を参照してください。

32.3.3 ClientDropListenerの使用について

dropTargetタグには、クライアントでのドロップ・イベントを処理するJavaScriptを参照できるclientDropListner属性が含まれます。クライアント・ハンドラではパラメータはとらず、AdfDnDContextアクションを返します。たとえば、メソッドでAdfDnDContext.ACTION_NONEを返すと、ドロップ操作が取り消され、サーバー・コールは行われません。メソッドでAdfDnDContext.ACTION_COPYを返すと、コピー操作が許可され、dropListenerメソッドがあればこれを実行するサーバー・コールが行われます。

たとえば、ドロップ・イベントの起動時にメッセージをログ出力するとします。メッセージの出力を処理し、サーバー・リスナーが起動されるよう適切なアクションを返すクライアント・ハンドラを作成します。例32-4に、メッセージの出力にロガーを使用するクライアント・ハンドラを示します。

例32-4 clientDropListenerハンドラ

<script>
/**
 * Shows a message.
 */
function showMessage()
{
  AdfLogger.LOGGER.logMessage(AdfLogger.ALL, "clientDropListener handler,
    copying...");
  return AdfDnDContext.ACTION_COPY;
}
</script>

32.4 コレクションのドラッグ・アンド・ドロップ機能の追加

ユーザーがコレクションからアイテム(表から行など)をドラッグし、ツリーなどの別のコレクション・コンポーネントにドロップできるドラッグ・アンド・ドロップ機能を追加できます。たとえば、図32-4に示すように、1つの表があり、この表から行をドラッグして別の表にドロップできるようにするとします。

図32-4 コレクション間のドラッグ・アンド・ドロップ機能

ディレクトリへのファイルのドラッグ・アンド・ドロップ

この例では、ドラッグ・アンド・ドロップ・アクションが完了すると、ドロップ・ターゲット表のdropListener属性で、rowKeyオブジェクトを使用して行のデータを取得し、表の新しい行にコピーするハンドラにアクセスします。

ターゲット・ソースがコレクションで、移動操作がサポートされる場合、dragDropEndListener属性にメソッドを実装することもできます。これは、ソース・コンポーネントから参照され、ドラッグ・アンド・ドロップ操作後のコレクションのクリーンアップに使用されます。詳細は、32.4.2項「dragDropEndListenerについて」を参照してください。

32.4.1 コレクションのドラッグ・アンド・ドロップ機能の追加方法

コレクションのドラッグ・アンド・ドロップ機能を追加するには、dragTargetタグを使用するかわりにcollectionDropTargetタグを使用します。その後、ドラッグ・アンド・ドロップ・アクションのロジックを処理するイベント・ハンドラ・メソッドを実装する必要があります。次に、ドラッグ・アンド・ドロップ操作のソースを定義します。ソースもコレクションの場合、attributeDragSourceタグではなくcollectionDragSourceタグを使用します。

この手順では、ソースとターゲットのコンポーネントはすでにページあるものとします。

ドラッグ・アンド・ドロップ機能を追加する手順:

  1. コンポーネント・パレットからコレクション・ドロップ・ターゲットをドラッグし、collectionDropTargetタグをターゲット・コレクション・コンポーネントの子として追加します。

  2. 「コレクション・ドロップ・ターゲットの挿入」ダイアログで、イベントを処理するマネージドBeanのメソッド(このコードはステップ4で作成)に評価される式をdropListener属性に入力します。

  3. プロパティ・インスペクタで次の設定を行います。

    • actions: ドラッグ・アンド・ドロップ操作時にソースで行われるアクションを選択します。

      アクションが指定されない場合、デフォルトはCOPYです。

    • modelName: コレクションのモデルを定義します。

      modelName属性の値は、互換性目的でドラッグ・ソースの識別に使用されるStringオブジェクトです。ソースを定義する際、collectionDragSourceタグのmodelName属性が、このmodelNameまたはdataFlavorタグのdiscriminant属性と一致する必要があります。つまり、これは任意の名前で、ターゲットとソースで同じmodelName値または識別値を共有する場合に機能します。

  4. ステップ2でEL式に挿入されたマネージドBeanに、ドロップ・イベントのハンドラを実装します。

    このメソッドはDropEventイベントをパラメータとし、DnDActionを返す必要があります。DndActionは、ソースがドロップされた際に行われるアクションです。有効な値はCOPYMOVEおよびLINKで、手順3actions属性を定義すると設定されます。このメソッドではdropEventを使用してtransferableオブジェクトを取得し、ドラッグされたデータのCollectionModelオブジェクトにここからアクセスし、ここから実際のデータにアクセスします。その後、リスナーでソースのモデルにデータを追加し、実行するDnDActionDnDAction.COPYDnDAction.MOVEまたはDnDAction.LINK)を返します。そうでない場合は、ドロップが拒否されたことを示すDnDAction.NONEを返します。

    例32-5に、図32-4に示す2つの表の間での行のコピーを処理するcollectionDropTargetデモに使用されるCollectionDnd.javaマネージドBeanのイベント・ハンドラ・メソッドを示します。

    例32-5 コレクションのdropListenerのイベント・ハンドラ・コード

    public DnDAction (DropEvent dropEvent)
    {
      Transferable transferable = dropEvent.getTransferable();
    
      // The data in the transferable is the row key for the dragged component.
      DataFlavor<RowKeySet> rowKeySetFlavor =
                           DataFlavor.getDataFlavor(RowKeySet.class, "DnDDemoModel");
      RowKeySet rowKeySet = transferable.getData(rowKeySetFlavor);
      if (rowKeySet != null)
      {
        // Get the model for the dragged component.
        CollectionModel dragModel = transferable.getData(CollectionModel.class);
        if (dragModel != null)
        {
          // Set the row key for this model using the row key from the transferable.
          Object currKey = rowKeySet.iterator().next();
          dragModel.setRowKey(currKey);
    
          // And now get the actual data from the dragged model.
          // Note this won't work in a region.
          DnDDemoData dnDDemoData = (DnDDemoData)dragModel.getRowData();
    
          // Put the dragged data into the target model directly.
          // Note that if you wanted validation/business rules on the drop,
          // this would be different.
          getTargetValues().add(dnDDemoData);
        }
        return DnDAction.COPY;
      }
      else
      {
        return DnDAction.NONE;
      }
    }
    
    
  5. コンポーネント・パレットからコレクション・ドラッグ・ソースをドラッグ・アンド・ドロップして、ソースとなるコンポーネントの子としてcollectionDragSourceタグを追加します。

  6. プロパティ・インスペクタで次の値を設定します。

    • actions: これは、手順3で定義したaction値と合致する必要があります。

    • modelName: modelName属性は、ドロップ可能なコレクションの定義に使用されます。これは、手順3で設定したモデル名と合致する必要があります。

    • dragDropEndListener: これは、ソース・コレクションで必要なクリーンアップ処理を行うマネージドBeanのメソッドと評価される式です。詳細は、32.4.2項「dragDropEndListenerについて」を参照してください。

32.4.2 dragDropEndListenerについて

ドロップ・イベントの後でソース・コレクションをクリーンアップする必要がある場合が考えられます。たとえば、ドラッグで移動を行った場合、移動した項目がコレクションの一部ではなくなるように、ソース・コンポーネントをクリーンアップする必要があることがあります。

collectionDragSourceタグには、ドラッグ・アンド・ドロップ操作の終了後のロジックを含むハンドラを登録できるdragDropEndListener属性が含まれます。

たとえば、ドラッグ・アンド・ドロップでオブジェクトを移動する場合、ドロップの正常終了後にソース・コンポーネントからオブジェクトを物理的に削除する必要があります。例32-6に、dragDropEndListener属性のハンドラを示します。

例32-6 dragDropEndListenerのハンドラ

public void endListener(DropEvent dropEvent)
{
  Transferable transferable = dropEvent.getTransferable();

  // The data in the transferrable is the row key for the dragged component.
  DataFlavor<RowKeySet> rowKeySetFlavor =
      DataFlavor.getDataFlavor(RowKeySet.class, "DnDDemoModel");
  RowKeySet rowKeySet = transferable.getData(rowKeySetFlavor);
  if (rowKeySet != null)
  {
    Integer currKey = (Integer)rowKeySet.iterator().next();
    Object removed = getSource2Values().remove(currKey.intValue());
  }
  // Need to add the drag source table so it gets redrawn.
    AdfFacesContext.getCurrentInstance().addPartialTarget(dropEvent.getDragComponent());
  }

32.5 コンポーネントのドラッグ・アンド・ドロップ機能の追加

ある親から別の親へのコンポーネントの移動を可能にしたり、親コンポーネントの子コンポーネントの並替えを可能にできます。例として、図32-5に、panelGridコンポーネントの最初の子で最後に移動される暗色のpanelBoxコンポーネントを示します。

図32-5 コンポーネント間のドラッグ・アンド・ドロップ機能

コンポーネントをドラッグ・アンド・ドロップできます

32.5.1 コンポーネントのドラッグ・アンド・ドロップ機能の追加方法

コンポーネントに対するドラッグ・アンド・ドロップ機能の追加は、オブジェクトに対するものと同様です。ただし、attributeDragSourceタグを使用するかわりにcomponentDragSourceタグを使用します。オブジェクトまたはコレクションのドラッグ・アンド・ドロップ同様、dropListenerハンドラを実装する必要もあります。

ドラッグ・アンド・ドロップ機能を追加する手順:

  1. コンポーネント・パレットから「ドロップ・ターゲット」 をドラッグ・アンド・ドロップし、dropTargetタグをターゲット・コレクション・コンポーネントの子として追加します。

  2. 「ドロップ・ターゲットの挿入」ダイアログで、イベントを処理するマネージドBeanのメソッド(このコードはステップ4で作成)に評価される式を入力します。

  3. dropTargetタグを選択した状態のまま、プロパティ・インスペクタでaction属性に有効なアクション・セットを選択します。

  4. ステップ2で作成したEL式で参照されるマネージドBeanに、ドラッグ・アンド・ドロップ機能を処理するイベント・ハンドラ・メソッドを(EL式での名前と同じ名前を使用して)dropListener属性に対して作成します。

    このメソッドはDropEventイベントをパラメータとし、DnDActionを返す必要があります。これは、ソースがドロップされたときに実行されるアクションです。有効な戻り値は、DnDAction.COPYDnDAction.MOVEおよびDnDAction.LINKで、ステップ2でターゲットの属性を定義したときに設定されます。

    このハンドラ・メソッドではDropEventイベントを使用してtransferableオブジェクトとそのデータを取得します。その後、移動またはコピーを完了し、必要に応じてコンポーネントを並べ替えます。メソッドでドロップを完了すると、実行したDnDActionを返します。そうでない場合は、ドロップが拒否されたことを示すDnDAction.NONEが返されます。

    例32-7に、デモ・アプリケーションのcomponentDragSource JSFページで使用されるDemoDropHandler.javaマネージドBeanのhandleComponentMoveイベント・ハンドラを示します。

    例32-7 コンポーネントの移動を処理するdropListenerのイベント・ハンドラ・コード

    public DnDAction handleComponentMove(DropEvent dropEvent)
    {
      Transferable dropTransferable = dropEvent.getTransferable();
      UIComponent movedComponent =  dropTransferable.getData
                                   (DataFlavor.UICOMPONENT_FLAVOR);
      if ((movedComponent != null) &&
           DnDAction.MOVE.equals(dropEvent.getProposedAction()))
      {
        UIComponent dropComponent = dropEvent.getDropComponent();
        UIComponent dropParent = dropComponent.getParent();
        UIComponent movedParent = movedComponent.getParent();
        UIComponent rootParent;
        ComponentChange change;
    
        // Build the new list of IDs, placing the moved component after the dropped
        //component.
        String movedLayoutId = movedParent.getId();
        String dropLayoutId = dropComponent.getId();
    
        List<String> reorderedIdList = new
                                       ArrayList<String>(dropParent.getChildCount());
    
        for (UIComponent currChild : dropParent.getChildren())
        {
          String currId = currChild.getId();
    
          if (!currId.equals(movedLayoutId))
          {
            reorderedIdList.add(currId);
            if (currId.equals(dropLayoutId))
            {
              reorderedIdList.add(movedLayoutId);
            }
          }
        }
    
        change = new ReorderChildrenComponentChange(reorderedIdList);
        rootParent = dropParent;
        // apply the change to the component tree immediately
        // change.changeComponent(rootParent);
    
        // redraw the shared parent
        AdfFacesContext.getCurrentInstance().addPartialTarget(rootParent);
    
        return DnDAction.MOVE;
      }
      else
      {
        return DnDAction.NONE;
      }
    }
    
  5. コンポーネント・パレットからコンポーネント・ドラッグ・ソースをドラッグ・アンド・ドロップし、ソース・コンポーネントの子としてcomponentDragSourceタグをソース・コンポーネントに追加します。