ヘッダーをスキップ

Oracle® Mobile Application Framework Oracle Mobile Application Frameworkでのモバイル・アプリケーションの開発 2.1.1.0.0 E64867-01 |
| 目次

前 | 次 |

# 14 MAF AMXでのバインディングの使用とデータ・コントロールの作成

この章では、MAF AMXアプリケーション機能内でデータ・バインディング、データ・コントロールおよびデータ・バインディング式言語(EL)を使用する方法について説明します。また、オブジェクト・スコープ・ライフサイクル、マネージドBean、UIヒント、検証およびデータ変更イベントについても説明します。

この章の内容は次のとおりです。

- 第14.1項「バインディングおよびデータ・コントロールの概要」

- 第14.2項「オブジェクト・スコープ・ライフサイクルについて」

- 第14.3項「EL式の作成」

- 第14.4項「マネージドBeanの作成と使用」

- 第14.5項「データ・コントロールを使用したビジネス・サービスの公開」

- 第14.6項「「データ・コントロール」パネルでのデータバインドされたUIコンポーネントの作成」

- 第14.7項「実行時に行われる処理: バインディング・コンテキストの動作方法」

- 第14.8項「データ・コントロールの構成」

- 第14.9項「属性の使用」

- 第14.10項「Beanデータ・コントロールの作成および使用」

- 第14.11項「DeviceFeaturesデータ・コントロールの使用方法」

- 第14.12項「属性の検証」

- 第14.13項「データ変更イベントについて」

## 14.1バインディングおよびデータ・コントロールの概要

モバイル・アプリケーション・フレームワークによって、ユーザー・インタフェース(UI)・テクノロジとビジネス・サービス実装の分離を可能にする2つの概念(データ・コントロールと宣言的バインディング)が実装されています。データ・コントロールでは、関連するプロパティ、メソッド、タイプの情報を含め、サービスの操作とデータ・コレクションを表す標準のメタデータ・インタフェースを使用してビジネス・サービスの実装技術を抽象化します。JDeveloperを使用すると、その情報をアイコンとして表示し、ページ上にドラッグ・アンド・ドロップできます。宣言的なバインディングによって、データ・コントロール内のデータ・コレクションからのデータ・アクセスとその操作の実行の詳細が抽象化されます。実行時に、モデル・レイヤーによって、適切なXMLファイルからデータ・コントロールおよびバインディングを記述した情報が読み取られ、ユーザー・インタフェースとビジネス・サービスの双方向の結合が実装されます。

ページのユーザー・インタフェース・コンポーネントをサポートするバインディングのグループは、ページ定義ファイルというページ固有のXMLファイルに記述されます。モデル・レイヤーでは実行時にこのファイルが使用され、ページのバインディングがインスタンス化されます。これらのバインディングは、バインディング・コンテナと呼ばれるリクエスト・スコープ・マップに保持され、EL式`#{bindings}`を使用して各ページ・リクエスト中にアクセスできます。この式により、現在のページのバインディング・コンテナが常に評価されます。「データ・コントロール」パネルから項目をドラッグし、それを特定のUIコンポーネントとしてページ上にドロップすることで、データバインドされたユーザー・インタフェースを設計できます。データ・コントロールを使用してUIコンポーネントを作成すると、そのコンポーネントを選択したデータ・コントロールにバインドするために必要なコードおよびオブジェクトがJDeveloperによって自動的に作成されます。

モバイル・アプリケーション・フレームワークには、即時利用可能な2つのデータ・コントロール(DeviceFeaturesデータ・コントロールとApplicationFeaturesデータ・コントロール)が用意されています。JDeveloperの「データ・コントロール」パネル内に表示されるDeviceFeaturesデータ・コントロールを使用すると、データ・コントロールの主なデータ属性を(テキスト)フィールドとして、またデータ・コントロールの操作をコマンド・オブジェクト(ボタン)として、アプリケーションにドラッグ・アンド・ドロップできます。これらのドラッグ・アンド・ドロップ・アクションによって、アプリケーション内のELバインディングおよび作成されるコントロールに適したプロパティが生成されます。バインディングは、データ・コントロール・ソースを指すために、`DataControls.dcx`ファイルによって表され、ページ・バインディングによって、特定のページの参照がデータ・コントロールにリンクされます。ApplicationFeaturesデータ・コントロールの詳細は、第4.6項「MAF AMXコンテンツによるカスタムSpringboardアプリケーション機能に関する必知事項」を参照してください。

データ・コントロールおよびバインディングの詳細は、次を参照してください。

- 第14.5項「データ・コントロールを使用したビジネス・サービスの公開」

- 第14.6項「「データ・コントロール」パネルでのデータバインドされたUIコンポーネントの作成」

- 第14.7項「実行時に行われる処理: バインディング・コンテキストの動作方法」

- 第14.8項「データ・コントロールの構成」

- 第14.9項「属性の使用」

- 第14.10項「Beanデータ・コントロールの作成および使用」

- 第14.11項「DeviceFeaturesデータ・コントロールの使用方法」

## 14.2 オブジェクト・スコープ・ライフサイクルについて

実行時、ページがデータにアクセスできるオブジェクト・スコープに必要なデータを格納することで、データをページに渡します。スコープによってオブジェクトの存続期間が決まります。オブジェクトをスコープに置くと、EL式を使用してスコープからアクセスできます。たとえば、`foo`という名前のマネージドBeanを作成し、Beanをビュー・スコープに存続させるよう定義するとします。このBeanにアクセスするには、`#{viewScope.foo}`という式を使用します。

モバイル・アプリケーション・フレームワーク変数とマネージドBean参照は、変数の存続期間と可視性を決定する様々なオブジェクト・スコープ内で定義されます。MAFは、次のスコープ(可視性が高い順に表示)をサポートしています。

- アプリケーション・スコープ: オブジェクトは、アプリケーションが存続するかぎり(複数の機能にまたがって)使用可能です。

- ページ・フロー・スコープ: オブジェクトは、機能が存続するかぎり(その機能内でのみ)使用可能です。

- ビュー・スコープ: オブジェクトは、ビューが存続するかぎり(機能の単一ページ内で)使用可能です。

オブジェクト・スコープは、プログラミング言語のグローバル変数およびローカル変数のスコープに相当します。スコープが広いほど、オブジェクトの可用性が高くなります。存続期間の間、これらのオブジェクトで特定のインタフェースの公開、情報の保持および他のオブジェクトへの変数とパラメータの受渡しを行うことができます。たとえば、アプリケーション・スコープに定義されているマネージドBeanは、アプリケーションが存続するかぎり、複数のページ・リクエストの間使用できます。ただし、ビュー・スコープに定義されているマネージドBeanは、1つの機能で1つのページ・リクエストの間のみ使用できます。

アプリケーション・スコープの名前空間で定義されたEL式は、アプリケーションが存続するかぎり、複数の機能にまたがって使用できます。アプリケーションの一方のビューでアプリケーション・スコープを定義し、そのスコープをもう一方のビューで参照できます。ページ・フロー・スコープの名前空間で定義されたEL式は、機能が存続するかぎり、単一の機能内で使用できます。ビュー・スコープの名前空間で定義されたEL式は、ビューが存続するかぎり、機能の単一ページ内で使用できます。このような変数を含むスコープに加えて、MAFでは、デバイス・プロパティとアプリケーション・プリファレンスに関する情報を公開できるスコープが定義されています。これらのスコープによって、アプリケーション・レベルの存続期間と可視性が決まります。詳細は、第14.3.5.2項「マネージドBeanカテゴリについて」および第14.3.5.3項「モバイル・アプリケーション・フレームワーク・オブジェクト・カテゴリについて」を参照してください。

マネージドBeanを登録するスコープまたは値を格納するスコープを決める場合、可能なかぎり狭いスコープを常に使用するようにします。アプリケーション・スコープは、ユーザー情報やコンテキスト情報などのアプリケーション全体に関係する情報にのみ使用します。アプリケーション・スコープは、ページ間での値の受渡しには使用しないでください。

注意: メモリー・スコープに配置するすべてのオブジェクトは、JSON `DataChangeEvent`にシリアライズされ、このオブジェクト内のgetterメソッドによって返されるオブジェクトもシリアライズされます。これによって、シリアライズされたオブジェクト・ツリーが深くネストされることがあり、パフォーマンスが低下します。一連のネストされたオブジェクトをシリアライズしないようにするには、それらを一時オブジェクトとして定義する必要があります。詳細は、第14.10.1項「Beanクラス変数のシリアライズに関する必知事項」を参照してください。

### 14.2.1 オブジェクト・スコープとタスク・フローに関する必知事項

タスク・フロー内の変数にどのスコープを使用するかを決定するときは、ビュー・スコープまたはページ・フロー・スコープのみを使用する必要があります。アプリケーション・スコープは、タスク・フローの存続期間を超えてオブジェクトをメモリーに保存するため、タスク・フローのカプセル化および再利用で障害が発生します。さらに、アプリケーション・スコープでは、オブジェクトがメモリーに必要以上に長い時間保持されるため、不要なオーバーヘッドが発生します。

タスク・フロー内のアクティビティ間でデータ値を渡す必要がある場合は、ページ・フロー・スコープを使用する必要があります。現在のビュー・アクティビティ内でのみ必要とされ、ビュー・アクティビティ間にまたがっていない変数の場合、ビュー・スコープを使用する必要があります。

## 14.3 EL式の作成

EL式は、属性と実行時に決まるオブジェクト値をバインドするために、MAFアプリケーションで使用されます。たとえば、`#{UserList.selectedUsers}`は選択したユーザーのセットを参照でき、`#{user.name}`は特定のユーザー名を参照でき、`#{user.role == 'manager'}`はユーザーがマネージャかどうかを判断します。実行時、汎用式エバリュエータによって、これらの式に対してそれぞれ`List`、`String`および`boolean`の値が返され、コードなしで自動的に各オブジェクトとそのプロパティにアクセスします。

式は、値のレンダリングに必要になるまで評価されません。MAF AMXでは遅延評価のみがサポートされるため、即時構成式(`${}`)を使用する式は解析を行いますが、その動作は遅延式(`#{}`)と同じになります。実行時、特定のUIコンポーネント(`inputText`コンポーネント、`outputText`コンポーネントなど)の値は、その`value`属性によって決まります。コンポーネントにはその値として静的テキストを割り当てることができますが、通常、`value`属性には、表示データを決定するために実行時インフラストラクチャにより評価されるEL式が含まれます。たとえば、現在ログイン中のユーザーの名前を表示する`outputText`コンポーネントには、`value`属性に`#{UserInfo.name}`という式を設定できます。コンポーネントの任意の属性(`value`属性だけでなく)にEL式を使用して値を割り当てることができるため、動的、かつデータ駆動型のユーザー・インタフェースの作成が容易になります。たとえば、UIコンポーネントの`rendered`属性に`#{not empty UserList.selectedUsers}`などのブール値の式を使用すると、表示するオブジェクト・セットが空のときにはそのコンポーネントを非表示にできます。`UserList`というオブジェクトの選択ユーザーのリストが空の場合、`rendered`属性は`false`と評価されるため、コンポーネントはページに表示されません。

通常のアプリケーションでは、`UserList`のようなオブジェクトはマネージドBeanとして作成します。ランタイムでは、EL式でBeanを最初に参照した際に、Beanのオンデマンドのインスタンス化が管理されます。値を表示する場合、ページを表示する際にランタイムでEL式が評価され、マネージドBeanから値を得て、コンポーネントにデータが移入されます。ユーザーがUIコンポーネントのデータを更新すると、同じEL式に基づいて、ランタイムで該当するマネージドBeanに値が戻されます。マネージドBeanの作成および使用の詳細は、第14.4項「マネージドBeanの作成および使用」を参照してください。EL式の詳細は、`http://www.oracle.com/technetwork/java/index.html`のJava EEチュートリアルを参照してください。

注意: EL式を編集可能コンポーネントの`value`属性に使用している場合は、そのコンポーネントに対応する`set`メソッドが必要です。そうでないと、EL式は読取り専用と評価され、値の更新は許可されません。 たとえば、ページに`inputText`コンポーネント(IDは`it1`)があり、その値が`#{myBean.inputValue}`に設定されているとします。`inputText`値を更新するには、`myBean`マネージドBeanに次のような`get`メソッドと`set`メソッドが必要です。
        public void setIt1(RichInputText it1) {
            this.it1 = it1;
        }

        public RichInputText getIt1() {
            return it1;
        }

### 14.3.1 データ・バインディングEL式について

「データ・コントロール」パネルを使用してコンポーネントを作成すると、MAFデータ・バインディング式が自動的に作成されます。この式は、バインディング・オブジェクトのデータを表示するか、バインディング・オブジェクトのプロパティを参照する、すべてのコンポーネント属性に追加されます。事前作成済のそれぞれの式は、ページ定義ファイルで定義された、適切なバインディング・オブジェクトを参照します。これらのバインディング式は編集可能ですが、MAFバインディング式の基本構文に従っていれば、独自に作成することもできます。MAFデータ・バインディング式は、属性がELをサポートする場合、バインディング・オブジェクトからのデータを移入する任意のコンポーネント属性に追加できます。

一般的なMAFデータ・バインディングEL式は、次の構文を使用して、バインディング・コンテナ内のタイプの異なるバインディング・オブジェクトを参照します。

    #{bindings.BindingObject.propertyName}

ここでは、次のようになります。

- `bindings`は、式によって参照されているバインディング・オブジェクトが現在のページのバインディング・コンテナにあることを識別する変数です。すべてのMAFデータ・バインディングEL式は、`bindings`変数で始まる必要があります。

- `BindingObject`は、ページ定義ファイルで定義される、バインディング・オブジェクトのID、または属性については名前です。バインディング`objectID`または名前は、そのページ定義ファイル内で一意なものです。EL式は、パラメータ・バインディング、実行可能バインディングまたは値バインディングなど、ページ定義ファイル内のどのバインディング・オブジェクトでも参照できます。

- `propertyName`は、各データバインドUIコンポーネントのデフォルトの表示方法を決定し、実行時のバインディング・オブジェクトのプロパティを設定する変数です。各タイプのバインディング・オブジェクトに対して、異なるバインディング・プロパティがあります。バインディング・プロパティの詳細は、第14.3.3項「MAFバインディング・プロパティに関する必知事項」を参照してください。

たとえば、次の式を例にしてみます。

    #{bindings.ProductName.inputValue}

`bindings`変数は、現在のページのバインディング・コンテナでバインドされた値を参照します。参照されているバインディング・オブジェクトは`ProductName`であり、これは属性バインディング・オブジェクトです。バインディング・プロパティは`inputValue`で、最初の`ProductName`属性の値を戻します。

ヒント: ページ定義ファイルのバインディング式ではドル記号(`$`)またはハッシュ記号(`#`)の接頭辞を使用できますが、MAFページのEL式で使用できるのは、ハッシュ記号(`#`)の接頭辞のみです。

前述のように、「データ・コントロール」パネルを使用してUIコンポーネントを作成する場合、これらの式が組み込まれます。ただし、必要に応じて手動で式を作成することもできます。JDeveloperの式ビルダーは、UIコンポーネントがバインドされるその他の有効なオブジェクトに加え、ページ定義ファイルに定義されたバインディング・オブジェクトのリストを提供することにより、EL式の構築を支援するダイアログです。また、MAFバインディング・オブジェクトの階層リストやMAFバインディング・オブジェクトの最も一般的に使用されるプロパティが提供されるため、MAFデータバインド式の作成や編集に特に便利です。バインディング・プロパティの詳細は、第14.3.3項「MAFバインディング・プロパティに関する必知事項」を参照してください。

### 14.3.2 EL式の作成方法

JDeveloperの式ビルダーを使用してEL式を宣言的に作成できます。「プロパティ」ウィンドウから「式ビルダー」にアクセスできます。

始める前に

EL式に関する知識が役立つ場合があります。詳細は、第14.3項「EL式の作成」を参照してください。

式ビルダーの使用手順:

1. 「プロパティ」ウィンドウで、変更する属性を見つけて、右端のドロップダウン・メニューを使用して「式ビルダー」を選択します。

2. 次の機能を使用して式を作成します。

   - 「変数」ドロップダウンを使用して、式に含める項目を選択します。これらの項目は、階層形式のバインディング・オブジェクトであるツリーに表示されます。ツリーの各アイコンは、式で使用できる各種タイプのバインディング・オブジェクトを表します。

     ツリーの絞り込みには、ドロップダウン・フィルタを使用するか、検索フィールドに検索条件を入力します。MAFで公開されているELでアクセス可能なオブジェクトは、「ADFマネージドBean」ノードの「モバイル・アプリケーション・フレームワーク・オブジェクト」ノードにあります。

     ヒント: これらのオブジェクトの詳細は、MAF Javadocを参照してください。第14.3.5項「式ビルダーのカテゴリについて」も参照してください。

     ツリーで項目を選択すると、その項目がEL式内の「式」ボックスに移動されます。「式」ボックスに式を直接入力することもできます。

   - 演算子ボタンを使用して、式に論理演算子または算術演算子を追加します。

   図14-1は、「ADFマネージドBean」カテゴリからEL式を作成する方法の例を示しています。第14.3.5項「式ビルダーのカテゴリについて」で説明するカテゴリのいずれかからEL式を作成できます。

   図14-1 「式ビルダー」ダイアログ

   ヒント: EL式を作成する正しい構文の使用の詳細は、`http://www.oracle.com/technetwork/java/index.html`のJava EEチュートリアルを参照してください。

   表14-1 式ビルダーのバインディング・ノードの下にあるアイコン

   アイコン | 説明
   --- | ---
   | 現在のページのバインディング・コンテナを参照する、`bindings`コンテナ変数を示します。`bindings`ノードを開くと、現在のページのすべてのバインディング・オブジェクトが公開されます。
   | バインディング・コンテキスト全体を参照する、`data`バインディング変数を示します(アプリケーション内の`.cpx`ファイルから作成)。`data`ノードを開くと、アプリケーション内のページ定義ファイルが公開されます。
   | アクション・バインディング・オブジェクトを表します。このアイコンを使用するノードを開くと、有効なアクション・バインディング・プロパティのリストが公開されます。
   | イテレータ・バインディング・オブジェクトを表します。このアイコンを使用するノードを開くと、有効なイテレータ・バインディング・プロパティのリストが公開されます。
   | 属性バインディング・オブジェクトを表します。このアイコンを使用するノードを開くと、有効な属性バインディング・プロパティのリストが公開されます。
   | リスト・バインディング・オブジェクトを表します。このアイコンを使用するノードを開くと、有効なリスト・バインディング・プロパティのリストが公開されます。
   | 表バインディング・オブジェクトまたはツリー・バインディング・オブジェクトを表します。このアイコンを使用するノードを開くと、有効な表バインディング・プロパティおよびツリー・バインディング・プロパティのリストが公開されます。
   | MAFバインディング・オブジェクト・プロパティを表します。MAFプロパティの詳細は、第14.3.3項「MAFバインディング・プロパティに関する必知事項」を参照してください。
   | パラメータ・バインディング・オブジェクトを表します。
   | Beanクラスを表します。
   | メソッドを表します。

#### 14.3.2.1 メソッド式ビルダーについて

表14-2は、「プロパティ」ウィンドウで「式ビルダー」オプションではなく「メソッド式ビルダー」オプションが提供されるプロパティを示しています。これらの唯一の違いは、メソッド式ビルダーが、選択されたプロパティに応じてマネージドBeanをフィルタして除外することです。

表14-2 メソッド式ビルダーのプロパティ

プロパティ | 要素
--- | ---
`action` | `amx:commandButton`
`action` | `amx:commandLink`
`action` | `amx:listItem`
`action` | `amx:navigationDragBehavior`
`action` | `dvtm:chartDataItem`
`action` | `dvtm:ieDataItem`
`action` | `dvtm:timelineItem`
`action` | `dvtm:area`
`action` | `dvtm:marker`
`actionListener` | `amx:listItem`
`actionListener` | `amx:commandButton`
`actionListener` | `amx:commandLink`
`binding` | `amx:actionListener`
`mapBoundsChangeListener` | `dvtm:geographicMap`
`mapInputListener` | `dvtm:geographicMap`
`moveListener` | `amx:listView`
`rangeChangeListener` | `amx:listView`
`selectionListener` | `amx:listView`
`selectionListener` | `amx:filmStrip`
`selectionListener` | `dvtm:areaDataLayer`
`selectionListener` | `dvtm:pointDataLayer`
`selectionListener` | `dvtm:treemap`
`selectionListener` | `dvtm:sunburst`
`selectionListener` | `dvtm:timelineSeries`
`selectionListener` | `dvtm:nBox`
`selectionListener` | `dvtm:areaChart`
`selectionListener` | `dvtm:barChart`
`selectionListener` | `dvtm:bubbleChart`
`selectionListener` | `dvtm:comboChart`
`selectionListener` | `dvtm:horizontalBarChart`
`selectionListener` | `dvtm:lineChart`
`selectionListener` | `dvtm:funnelChart`
`selectionListener` | `dvtm:pieChart`
`selectionListener` | `dvtm:scatterChart`
`valueChangeListener` | `amx:inputDate`
`valueChangeListener` | `amx:inputNumberSlider`
`valueChangeListener` | `amx:inputText`
`valueChangeListener` | `amx:selectBooleanCheckbox`
`valueChangeListener` | `amx:selectBooleanSwitch`
`valueChangeListener` | `amx:selectManyCheckbox`
`valueChangeListener` | `amx:selectManyChoice`
`valueChangeListener` | `amx:selectOneButton`
`valueChangeListener` | `amx:selectOneChoice`
`valueChangeListener` | `amx:selectOneRadio`
`valueChangeListener` | `dvtm:statusMeterGauge`
`valueChangeListener` | `dvtm:dialGauge`
`valueChangeListener` | `dvtm:ratingGauge`
`viewportChangeListener` | `dvtm:areaChart`
`viewportChangeListener` | `dvtm:barChart`
`viewportChangeListener` | `dvtm:comboChart`
`viewportChangeListener` | `dvtm:horizontalBarChart`
`viewportChangeListener` | `dvtm:lineChart`

#### 14.3.2.2 非ELプロパティについて

表14-3は、ELに対応していないため「プロパティ」ウィンドウでEL式ビルダー・オプションが提供されないプロパティを示しています。

表14-3 非ELプロパティ

プロパティ | 要素
--- | ---
`id` | すべての要素
`facetName` | `amx:facetRef`
`failSafeClientHandler` | `amx:loadingIndicatorBehavior`
`failSafeDuration` | `amx:loadingIndicatorBehavior`
`group` | `amx:validationBehavior`
`name` | `amx:attribute`
`name` | `amx:attributeList`
`name` | `amx:attributeListIterator`
`name` | `amx:facet`
`ref` | `amx:attributeList`
`type` | `dvtm:attributeGroups`
`var` | `amx:carousel`
`var` | `amx:filmStrip`
`var` | `amx:iterator`
`var` | `amx:listView`
`var` | `amx:loadBundle`
`var` | `dvtm:areaChart`
`var` | `dvtm:barChart`
`var` | `dvtm:bubbleChart`
`var` | `dvtm:comboChart`
`var` | `dvtm:funnelChart`
`var` | `dvtm:horizontalBarChart`
`var` | `dvtm:lineChart`
`var` | `dvtm:pieChart`
`var` | `dvtm:scatterChart`
`var` | `dvtm:sparkChart`
`var` | `dvtm:geographicMap`
`varStatus` | `amx:attributeListIterator`

### 14.3.3 MAFバインディング・プロパティに関する必知事項

式ビルダーを使用してデータバインドされたコンポーネントを作成すると、EL式によって、特定のMAFバインディング・プロパティが参照される場合があります。実行時には、これらのバインディング・プロパティによって、データバインドUIコンポーネントのデフォルトの表示方法や、イテレータ・バインディング固有のパラメータなどが定義されます。ADFバインディング・プロパティは、Oracle APIによって定義されます。各バインディング・タイプで使用可能なプロパティの完全なリストは、表14-4「MAFバインディングの実行時ELプロパティ」を参照してください。

特定のプロパティに割り当てられる値は、ページ定義ファイルで定義されます。たとえば、イテレータ・バインディングには、イテレータが一度に表示する行数を指定する、`RangeSize`というプロパティがあります。`例14-1`のように、RangeSizeに割り当てられる値は、ページ定義ファイルで指定されます。

例14-1 RangeSizeプロパティを使用したイテレータ・バインディング・オブジェクト

    <iterator Binds="ItemsForOrder" RangeSize="25"
                  DataControl="BackOfficeAppModuleDataControl"
                  id="ItemsForOrderIterator" ChangeEventPolicy="ppr"/>

### 14.3.4 バインディング・コンテナを参照する方法

ルートEL式`#{bindings}`で、アクティブな画面のバインディング・コンテナを参照できます。式`#{data.PageDefName}`で、別の画面のバインディング・コンテナを参照できます。モバイル・アプリケーション・フレームワークAMXバインディング・オブジェクトは、バインディング・コンテナ`#{bindings.Name}`から名前で参照されます。

表14-4は、実行時に、EL式でモバイル・アプリケーション・フレームワークAMXバインディング・オブジェクトの値にアクセスするために使用できるプロパティのリストの一部を示しています。プロパティは、アルファベット順に並んでいます。

表14-4 MAFバインディングの実行時ELプロパティ

実行時プロパティ | 説明 | イテレータ | アクション | 属性値 | ツリー
--- | --- | --- | --- | --- | ---
`class` | 実行時バインディング用のJavaクラス・オブジェクトを戻します。 | はい | はい | はい | はい
`collectionModel` | データのコレクションを公開します。`collectionModel`にバインドされているコンポーネント内で使用するEL式は、コレクションの各要素の式を解決する`row`変数脚注 1 を使用して参照できます。 | いいえ | いいえ | いいえ | はい
`collectionModel.makeCurrent` | このバインディングのイテレータ内で、選択した行を現在の行にします。 | いいえ | いいえ | いいえ | はい
`collectionModel.selectedRow` | 選択した行への参照を戻します。 | いいえ | いいえ | いいえ | はい
`currentRow` | イテレータが指している現在の行またはデータ・オブジェクト(組込みナビゲーション・アクションなど)への参照を戻します。 | はい | いいえ | いいえ | いいえ
`currentRow.dataprovider` | イテレータが指している現在の行またはデータ・オブジェクトへの参照を戻します。(これは`currentRow`から戻されるものと同じオブジェクトで、構文のみが異なります)。 | はい | いいえ | いいえ | いいえ
`enabled` | アクション・バインディングの状態に応じて、`true`または`false`を戻します。たとえば、アクション・バインディングは、ユーザーが「先頭へ」、「次へ」、「前へ」、「最後へ」などのナビゲーション・ボタンをクリックした場合に決定される現在位置に基づいて、有効化(`true`)または無効化(`false`)されます。 | いいえ | はい | いいえ | いいえ
`execute` | 解決されたときに、名前付きアクションまたは`methodAction`バインディングを起動します。 | いいえ | はい | いいえ | いいえ
`format` | これは`hints.format`のショートカットです。 | いいえ | いいえ | はい | はい
`hints` | バインディングが関連付けられているすべての表示属性について、UIヒントの名前/値ペアのリストを戻します。 | いいえ | いいえ | はい | はい
`inputValue` | バインディングが関連付けられている最初の属性の値を戻します。 | いいえ | いいえ | はい | いいえ
`items` | 現在のリストの有効な属性に関連付けられた値のリストを戻します。 | いいえ | いいえ | はい | いいえ
`label` | `hints`の子または属性の直接の子として使用できます。バインディングの最初の属性のラベルを戻します(コントロール・`ヒント`で指定されている場合)。 | いいえ | いいえ | はい | はい
`name` | `PageDef.xml`ファイル内で宣言されたバインディングの`id`を戻します。 | はい | はい | はい | はい
`rangeSize` | イテレータ・バインディングの行セットの範囲サイズを戻します。これにより、データソースからバインドされるデータ・オブジェクトの数を確認できます。 | はい | いいえ | いいえ | はい
`result` | メソッド・アクション・バインディングによりバインドおよび起動されるメソッドの結果を戻します。 | いいえ | はい | いいえ | いいえ
`updateable` | `hints`の子または属性の直接の子として使用できます。バインディングが関連付けられている最初の属性が更新可能である場合、`true`を戻します。それ以外の場合は`false`を戻します。 | いいえ | いいえ | はい | はい
`viewable` | `Tree`の子として使用できます。このバインディングおよび関連コンポーネントをレンダリングするかどうかを実行時に解決します。 | いいえ | いいえ | いいえ | はい

脚注 1 EL語句の`row`は、コレクション・コンポーネントのコンテキスト内で使用されます。`row`は単に、コレクションをレンダリングするときに、MAF AMXバインディング・オブジェクトからアクセスできる属性を含むコレクションの各要素に対する反復変数として機能します。属性とリスト・バインディングには、`row`変数を使用してアクセスできます。そのような式の構文は、コレクションに含まれないバインディング・オブジェクトへのアクセスに使用される構文と同じで、`#{row.bindings.Name.property}`のように`row`変数を先頭の語句として追加します。

### 14.3.5 式ビルダーのカテゴリについて

MAF AMXページの式ビルダーでは、次のカテゴリを使用できます。

- バインディング・カテゴリについて

- マネージドBeanカテゴリについて

- モバイル・アプリケーション・フレームワーク・オブジェクト・カテゴリについて

#### 14.3.5.1 バインディング・カテゴリについて

この項では、「バインディング」カテゴリで使用できるオプションを表示します。`bindings`ノードと`data`ノードは、同じ一連のサポートされているバインディングとプロパティを表示します。表14-5に、使用できるバインディング・タイプと、各バインディング・タイプでサポートされるプロパティをリストします。`securityContext`ノードは次のプロパティをサポートしています。

- `authenticated`

- `userGrantedPrivilege`

- `userInRole`

- `userName`

次に例を示します。

    #{securityContext.authenticated}
    #{securityContext.userGrantedPrivilege['submit_privilege']}
    #{securityContext.userInRole[&rsquor;manager_role']}
    #{securityContext.userName}

表14-5 サポートされるバインディング・タイプ

バインディング・タイプ | プロパティ
--- | ---
`accessorIterator` | `class` `currentRow: dataProvider` `name` `rangeSize`
`action` | `class` `enabled` `execute` `name`
`attributeValues` | `autoSubmit` `category` `class` `controlType` `displayHeight` `displayHint` `displayWidth` `filedorder` `format` `hints: allows.read, allows.update, autoSubmit, category, controlType, displayHeight, displayHint, displayWidth, filedorder, format, label, mandatory, precision, tooltip, updateable` `inputValue` `items` `iteratorBinding` `label` `mandatory` `name` `precision` `tooltip` `updateable`
`button` | `autoSubmit` `category` `class` `controlType` `displayHeight` `displayHint` `displayWidth` `filedorder` `format` `hints: allows.read, allows.update, autoSubmit, category, controlType, displayHeight, displayHint, displayWidth, filedorder, format, label, mandatory, precision, tooltip, updateable` `inputValue` `items` `iteratorBinding` `label` `mandatory` `name` `precision` `tooltip` `updateable`
`invokeAction` | `always` `deferred`
`iterator` | `class` `currentRow: dataProvider` `name` `rangeSize`
`list` | `autoSubmit` `category` `class` `controlType` `displayHeight` `displayHint` `displayWidth` `filedorder` `format` `hints: format, allows.read, allows.update, autoSubmit, category, controlType, displayHeight, displayHint, displayWidth, filedorder, format, label, mandatory, precision, tooltip, updateable` `inputValue` `items` `iteratorBinding` `label` `mandatory` `name` `precision` `tooltip` `updateable`
`methodAction` | `class` `enabled` `execute` `name` `operationEnabled` `operationInfo` `paramsMap` `result`
`methodIterator` | `class` `currentRow: dataProvider` `name` `rangeSize`
`searchAction` | `class` `enabled` `execute` `name` `operationEnabled` `operationInfo` `paramsMap` `result`
`tree` | `category` `class` `collectionModel: bindings, makeCurrent, selectedRow, <AttrName>` `displayHeight` `displayHint` `displayWidth` `filedorder` `format` `hints: category, displayHeight, displayHint, displayWidth, filedorder, format, label, mandatory, precision, tooltip, updateable`, <AttrName> `iteratorBinding` `label` `mandatory` `name` `precision` `rangeSize` `tooltip` `updateable` `viewable`
`variable` | `class` `currentRow: dataProvider` `name`
`variableIterator` | `class` `currentRow: dataProvider` `name`

#### 14.3.5.2 マネージドBeanカテゴリについて

この項では、「マネージドBean」カテゴリで使用できるオプションを表示します。

- `applicationScope`: 「マネージドBean」の`applicationScope`ノードには、アプリケーション・レベルで定義されたものがすべて含まれます(アプリケーション・スコープのマネージドBeanなど)。

- `pageFlowScope`: 「マネージドBean」の`pageFlowScope`ノードには、ページ・フロー・レベルで定義されたものがすべて含まれます(ページ・フロー・スコープのマネージドBeanなど)。

- `viewScope`: 「マネージドBean」の`viewScope`ノードには、ビュー・レベルで定義されたものがすべて含まれます(ビュー・スコープのマネージドBeanなど)。

MAFランタイムでは、自身をマネージドBeanプロパティの変更通知のリスナーとして登録することによって、Beanプロパティを参照するUIコンポーネントにバインドされたEL式が、プロパティの値が変更されたときに自動的に更新されるようにします。これらの通知をソースとして参照するには、Beanのプロパティ・アクセッサにコードを追加する必要があります。Beanのプロパティ・アクセッサから、通知をソースとして参照するために必要なコードを自動的に生成するには、「アクセッサの生成」ダイアログの「プロパティの変更時にリスナーに通知」チェック・ボックスを選択します(図14-2を参照)。

図14-2 プロパティの変更時にリスナーに通知

ELを通じて単にBeanメソッドまたはプロパティを参照する場合、このコードを追加する必要はありませんが、Beanに格納された値が変化する場合や、特にその変更が、1つ以上のプロパティ値を変更するBeanメソッドの実行による二次的な影響など、間接的なものである場合、それらの値に依存するEL式のアクティブ・フォームへのレンダリングを最新の状態に保つ必要があります。プロパティの変更と`PropertyChangeSupport`クラスについては、第14.13項「データ変更イベントについて」を参照してください。

例14-2は、プログラムで別のマネージドBean属性にバインドされた値を取得する方法を示しています。

例14-2 プログラムでマネージドBeanから取得されるオブジェクト値

    public void someMethod() {
       Object value = AdfmfJavaUtilities.evaluateELExpression(
                           "#{applicationScope.MyManagedBean.someProperty}");
       ...
    }

例14-3は、プログラムでマネージドBeanからバインディングを実行する方法を示しています。

例14-3 プログラムでマネージドBeanから実行されるバインディング

    public void someMethod() {
       Object value = AdfmfJavaUtilities.evaluateELExpression(
                         "#{bindings.someDataControlMethod.execute}");
     ...
    }

注意: 機能の`applicationScope`内でマネージドBeanを宣言し、デザインタイムで別の機能のELを通じてそのBeanを参照しようとすると、そのデザインタイムで無効なELに関する警告が表示されます。この警告は、デザインタイムがそのBeanの現在のプロジェクトで参照を検出できないことが原因で表示されます。そのBeanを実行時に参照できるのは、Beanが宣言されている最初の機能に初めてアクセスし、さらに別の機能のELを通じてアクセスする前にそのBeanをインスタンス化した場合のみです。これは、`Name`属性の値をノード・ラベルとして使用する`PreferenceValue`要素には当てはまりません。

#### 14.3.5.3 モバイル・アプリケーション・フレームワーク・オブジェクト・カテゴリについて

「モバイル・アプリケーション・フレームワーク・オブジェクト」カテゴリには、オブジェクト・スコープなどのELを使用して参照できるMAFに定義されている様々なオブジェクトが一覧表示されます。

MAF変数とマネージドBean参照は、変数の存続期間と可視性を決定する様々なオブジェクト・スコープ内で定義されます。これらのスコープには、可視性が高い順に、アプリケーション・スコープ、ページ・フロー・スコープおよびビュー・スコープがあります。様々なオブジェクト・スコープの詳細は、第14.2項「オブジェクト・スコープ・ライフサイクルについて」を参照してください。

このような変数を含むスコープに加えて、MAFでは、デバイス・プロパティとアプリケーション・プリファレンスに関する情報を公開できるスコープが定義されています。これらのスコープによって、アプリケーション・レベルの存続期間と可視性が決まります。

「モバイル・アプリケーション・フレームワーク・オブジェクト」カテゴリで使用できるものは、次のとおりです。

- `applicationScope`: `applicationScope`ノードには、アプリケーション・レベルで定義されたものがすべて含まれます(アプリケーション・スコープのマネージドBeanなど)。アプリケーション・スコープで定義されたEL変数は、アプリケーションが存続するかぎり、複数の機能にまたがって使用できます。

- `pageFlowScope`: `pageFlowScope`ノードには、ページ・フロー・レベルで定義されたものがすべて含まれます(ページ・フロー・スコープのマネージドBeanなど)。ページ・フロー・スコープの名前空間で定義されたEL変数は、機能が存続するかぎり、単一の機能内で使用できます。

- `preferenceScope`: `preferenceScope`ノードには、アプリケーションと機能のすべてのプリファレンスが含まれます。

  プリファレンス要素では、`PreferenceValue`要素を除き、`Id`属性の値を式ビルダーのノード・ラベルとして使用します。`PreferenceValue`要素では、`Name`属性の値を式ビルダーのノード・ラベルとして使用します。

  注意: EL式の文字列トークンにドット(.)や特殊文字または`default`などの予約語が含まれている場合、式ビルダーではそのような文字列トークンを一重引用符と大カッコで囲みます。機能IDやプリファレンス・コンポーネントIDにドットが含まれている場合、式ビルダーでは、ドットによって区切られたIDの各部分を別のプロパティとして`preferenceScope`階層に表示します。生成された式でも、ドットによって区切られたIDの各部分を別のプロパティとして認識します。

  `preferenceScope`のいくつかのサンプルEL式を次に示します。

  例14-4 ドットを含む機能ID

      "#{preferenceScope.feature.oracle.hello.SampleGroup1.label}"

  例14-5 属性名が予約語である場合

      "#{preferenceScope.application.OracleMobileApp.Edition['default']}"

- `viewScope`: このノードには、ビュー・レベルで定義されたものがすべて含まれます(ビュー・スコープのマネージドBeanなど)。ビュー・スコープの名前空間で定義されたEL変数は、ビューが存続するかぎり、機能の単一ページ内で使用できます。

- `row`: `row`オブジェクトは、`collectionModel`に含まれる単一のプロバイダへのショートカットである中間変数です。その名前は、親コンポーネント(リスト・ビューまたはカルーセルなど)の`var`属性の値です。

  注意: `AdfmfJavaUtilities.evaluateELExpression`を使用して`#{row}`または`row`のプロパティを評価することはできません。これらの式は、Null値を返します。

- `viewControllerBundle`

  これは、プロジェクト・レベルで定義されたリソース・バンドルを指すリソース・バンドル変数の名前です。このノードは、`amx:loadBundle`要素がドロップされ、リソース・バンドルが作成された後にのみ表示されます。このノードの名前は、`amx:loadBundle`の変数名によって異なります。このノードには、バンドル内で宣言されたすべての文字列が表示されます。

  例14-6は、`viewControllerBundle`のAMXコードの例を示しています。

  例14-6 loadBundle要素のAMXコード・サンプル

      <amx:loadBundle basename="mobile.ViewControllerBundle" var="viewcontrollerBundle"/>

### 14.3.6 ELイベントについて

ELイベントは、共通語句を含む式が相互に同期して更新できるようにするため、MAF AMX UIの動作において重要な役割を果たします。

EL式は、様々なコンテキストで値を参照できます。例14-7は、2つの入力数値スライダ・コンポーネントの作成を示しており、各コンポーネントは`applicationScope`の値に関連付けられています。出力テキストでは、ELを使用して単純な追加式を計算結果とともに表示します。フレームワークでは、出力テキスト・ラベル内のEL式を解析するときに、その式に2つの値への参照が含まれていることを確認し、それら2つの値に基づく出力テキストのイベント・リスナー(第13.10項「イベント・リスナーの使用方法」を参照)を作成します。基礎となる式の値が変化すると、イベントがその値のすべてのリスナーに対して生成されます。

注意: (スコープ・オブジェクトではなく)マネージドBeanのプロパティを参照している場合、リスナーを追加する必要があります。詳細は、第14.3.5.2項「マネージドBeanカテゴリについて」を参照してください。

例14-7 2つのコンポーネントを含むELイベントの生成

    <amx:inputNumberSlider id="slider1" label="X" value="#{applicationScope.X}"/>
    <amx:inputNumberSlider id="slider2" label="Y" value="#{applicationScope.Y}"/>
    <amx:outputText id="ot1" value="#{applicationScope.X} +
           #{applicationScope.Y} = #{applicationScope.X + applicationScope.Y}"/>

例14-7では、2つのコンポーネントがそれぞれ1つの値を更新し、1つのコンポーネントが両方の値を使用します。例14-8は、いずれかの既存の値を参照する3つ目の入力数値スライダ・コンポーネントが追加された場合、動作は同じになることを示しています。

例14-8 3つのコンポーネントを含むELイベントの生成

    <amx:inputNumberSlider id="slider1" label="X" value="#{applicationScope.X}"/>
    <amx:inputNumberSlider id="slider2" label="Y" value="#{applicationScope.Y}"/>
    <amx:outputText id="ot1" value="#{applicationScope.X} +
           #{applicationScope.Y} = #{applicationScope.X + applicationScope.Y}"/>
    <amx:inputNumberSlider id="slider3" label="X" value="#{applicationScope.X}"/>

例14-8では、いずれかの入力数値スライダ・コンポーネントが`#{applicationScope.X}`を更新すると、もう一方が出力テキストとともに自動的に更新されます。

### 14.3.7 EL式のマネージドBean内での使用方法

必要なEL式はJDeveloperで作成され、作成されない式は式ビルダーを使用して作成できますが、マネージドBean内でEL式にアクセスしたり、EL式を設定または起動する必要がある場合もあります。

例14-9に、EL式への参照を取得し、適合するオブジェクトを返す(または作成する)方法を示します。

例14-9 マネージドBeanからのEL式の解決

    public static Object resolveExpression(String expression) {
      return AdfmfJavaUtilities.evaluateELExpression(expression);
    }

例14-10に、メソッド式を解決する方法を示します。

例14-10 マネージドBeanからのメソッド式の解決

    public static Object resloveMethodExpression(String expression,
                                                 Class returnType,
                                                 Class[] argTypes,
                                                 Object[] argValues) {
      MethodExpression methodExpression = AdfmfJavaUtilities.getMethodExpression(expression,
                                                                                 returnType,
                                                                                 argTypes);
      return methodExpression.invoke(AdfmfJavaUtilities.getAdfELContext(), argValues);
    }

例14-11に、マネージドBeanで新たにオブジェクトを設定する方法を示します。

例14-11 マネージドBeanでの新たなオブジェクトの設定

    public static void setObject(String expression, Object newValue) {
      AdfmfJavaUtilities.setELValue(expression, newValue);
    }

## 14.4 マネージドBeanの作成と使用

マネージドBeanは、様々な構成ファイルを使用してアプリケーションに登録するJavaクラスです。MAFアプリケーションを起動すると、これらの構成ファイルが解析されてBeanが使用可能になり、EL式で参照できるため、Beanのプロパティとメソッドにアクセスできます。マネージドBeanが初めて参照されたときにまだ存在していない場合、マネージドBean作成機能によりBeanのデフォルトのコンストラクタ・メソッドがコールされて、Beanがインスタンス化されます。また、プロパティが宣言されると、宣言されたデフォルトの値が移入されます。

多くの場合、マネージドBeanは、フロント・エンドで処理することが最適なイベントやデータ操作を処理します。マネージドBeanの使用方法の詳細は、`http://www.oracle.com/technetwork/java/index.html`のJava EEチュートリアルを参照してください。

ベスト・プラクティス: マネージドBeanを使用して、現在のユーザーなどのブックキーピング情報のみを格納します。すべてのアプリケーション・データおよび処理データは、アプリケーションのビジネス・レイヤーのロジックによって処理される必要があります。

注意: EL式には、Beanを参照するために明示的にスコープを指定する必要があります。たとえば、`pageFlowScope`スコープから`MyBean`マネージドBeanを参照するには、`#{pageFlowScope.MyBean}`という式になります。

### 14.4.1 JDeveloperでのマネージドBeanの作成方法

`adfc-mobile-config.xml`ファイルに概要エディタを使用して、マネージドBeanの作成とMAFアプリケーションへの登録を同時に行うことができます。

始める前に

マネージドBeanに関する知識が役立つ場合があります。詳細は、第14.4項「マネージドBeanの作成と使用」を参照してください。

マネージドBeanの作成および登録手順:

1. 「アプリケーション」ウィンドウで、adfc-mobile-config.xmlをダブルクリックします。

2. エディタ・ウィンドウで、「概要」タブをクリックします。

3. 概要エディタで、「マネージドBean」ナビゲーション・タブをクリックします。

   図14-3は、`adfc-mobile-config.xml`ファイルのエディタを示しています。

   図14-3 adfc-mobile-config.xmlファイル内のマネージドBean

4. 「追加」アイコンをクリックして「マネージドBean」表に行を追加します。

5. 「マネージドBeanの作成」ダイアログで値を入力します。ダイアログの使用の詳細が必要な場合は、「ヘルプ」をクリックします。JDeveloperでクラス・ファイルを作成する場合は、「クラスが存在しない場合は生成」オプションを選択します。リスナー・プロパティのいずれかを選択して「編集」ボタンをクリックすることで、「プロパティ」ウィンドウから「マネージドBeanの作成」ダイアログを開くこともできます。そこから、新しいマネージドBeanと対応するメソッドを作成できます。

   注意: マネージドBeanを登録するスコープまたは値を格納するスコープを決める場合、可能なかぎり狭いスコープを常に使用するようにします。様々なオブジェクト・スコープの詳細は、第14.2項「オブジェクト・スコープ・ライフサイクルについて」を参照してください。

6. オプションでBeanに管理プロパティを追加できます。Beanがインスタンス化される際、指定した値が管理プロパティに設定されます。「Managed Bean」表でBeanを選択した状態で「新規」アイコンをクリックし、「管理プロパティ」表に行を追加します。「プロパティ」ウィンドウで、プロパティ名を入力します(他のフィールドはオプションです)。

   注意: このエディタを使用して管理プロパティを宣言することはできますが、対応するコードはJavaクラスで生成されません。適切なタイプのプライベート・メンバー・フィールドを作成し、コード・エディタのポップアップ・メニューの「アクセッサの生成」メニュー項目を選択して、これらのBeanプロパティに対応する`get`および`set`メソッドを生成することにより、コードを追加する必要があります。

### 14.4.2 JDeveloperを使用してマネージドBeanを作成する場合の処理

マネージドBeanを作成する際、Javaファイルを生成するよう選択すると、指定された名前とデフォルト・コンストラクタを使用してスタブ・クラスが自動的に作成されます。例14-12に、viewパッケージに格納された`MyBean`クラスに追加されるコードを示します。

例14-12 マネージドBean用に生成されたコード

    package view;

    public class MyBean {
        public MyBean() {
        }
    }

ここでページに必要なロジックを追加する必要があります。このロジックは、マネージドBeanの`managed-bean-name`を参照するEL式を使用して参照できます。たとえば、`my_bean`マネージドBeanの`myInfo`プロパティにアクセスする場合、EL式は次のようになります。

    #{my_bean.myInfo}

JDeveloperでは、`managed-bean`要素も`adfc-mobile-config.xml`ファイル(または編集されるタスク・フロー・ファイル)に追加されます。例14-13は、`MyBean`クラスに対して作成された`managed-bean`要素を示しています。

例14-13 adfc-mobile-config.xmlファイルでのマネージドBeanの構成

    <managed-bean>
      <managed-bean-name>my_bean</managed-bean-name>
      <managed-bean-class>view.MyBean</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope>
    </managed-bean>

## 14.5 データ・コントロールを使用したビジネス・サービスの公開

アプリケーションのサービスの準備ができたら、JDeveloperを使用して、UIコンポーネントとサービスとの宣言的バインドに必要な情報を提供するデータ・コントロールを作成できます。

データ・コントロールの生成には、「データ・コントロールの作成」メニュー・アイテムを使用します。データ・コントロールは、1つ以上のXMLメタデータ・ファイルで構成され、これらのファイルでは、実行時にバインディングと連動するサービスの機能が定義されます。データ・コントロールは、基礎となるサービスと連動します。

### 14.5.1 データ・コントロールの作成方法

アダプタ・ベースのデータ・コントロールは、JDeveloperの「アプリケーション」ウィンドウ内で作成します。

始める前に

データ・コントロール使用の一般的な知識があると役立ちます。詳細は、第14.5項「データ・コントロールを使用したビジネス・サービスの公開」を参照してください。

次のタスクを完了する必要があります。

  アプリケーション・ワークスペースを作成し、データ・コントロールの基礎にするビジネス・サービスを追加します。アプリケーション・ワークスペースの作成の詳細は、第2.2項「MAFアプリケーションの作成」を参照してください。

データ・コントロールを作成する手順は、次のとおりです。

1. アプリケーション・ワークスペース内でデータ・モデル・プロジェクトの最上位ノードを右クリックして、「新規」を選択し、「ギャラリから」を選択します。

2. 「新規ギャラリ」で、「ビジネス層」を展開し、「データ・コントロール」を選択して、作成するデータ・コントロールのタイプを選択し、「OK」をクリックします。

3. ウィザードの残りのステップを完了します。

注意: 場合によっては、データ・コントロールの基礎にするクラスまたはオブジェクトを右クリックし、「データ・コントロールの作成」を選択することによってデータ・コントロールを作成できます。

### 14.5.2 データ・コントロール作成時のプロジェクト内の処理

データ・コントロールを作成する際に、JDeveloperによってデータ・コントロール定義ファイル(`DataControls.dcx`)が作成され、概要エディタでファイルが開き、ファイルの階層が「データ・コントロール」パネルに表示されます。このファイルにより、データ・コントロールがサービスおよびバインディングと直接連動して動作することが可能になります。

エディタ・ウィンドウの「ソース」タブをクリックすることによって、対応するXMLファイルからのコードを表示できます。

#### 14.5.2.1 DataControls.dcx概要エディタ

`DataControls.dcx`ファイルの概要エディタは、データ・コントロール・オブジェクトの階層と、データ・モデルの公開メソッドを表示します。

概要エディタおよび「データ・コントロール」パネルで使用されるアイコンの説明は、表14-6を参照してください。

データ・コントロール・オブジェクトの設定を変更するには、オブジェクトを選択し、「編集」アイコンをクリックします。データ・コントロールの編集方法の詳細は、第14.8.1項「データ・コントロールの編集方法」を参照してください。

図14-4は、概要エディタでの`DataControls.dcx`ファイルを示しています。

図14-4 概要エディタでのDataControls.dcxファイル

#### 14.5.2.2 データ・コントロール・パネル

「データ・コントロール」パネルはパレットとして機能し、ノードを「データ・コントロール」パネルからページのデザイン・エディタにドラッグすることにより、データバインドされたUIコンポーネントを作成できます。データ・コントロールを作成した後、「データ・コントロール」パネルが「アプリケーション」ウィンドウに表示されます。図14-5は、サンプル・アプリケーションの「データ・コントロール」パネルを示しています。

図14-5 「データ・コントロール」パネル

### 14.5.3 データ・コントロールの組込み操作

データ・コントロール・フレームワークは、データ・コントロールの標準操作のセットを定義します。これらの操作は、基礎となるビジネス・サービスの機能を使用して実装されます。実行時に、これらのデータ・コレクション操作の1つがデータ・バインディング・レイヤーによって名前を指定して起動されると、データ・コントロールは、組込み機能を処理する適切なサービス・メソッドにそのコールを委譲します。たとえば、Beanデータ・コントロールでは、`Next`操作はBeanコレクションのイテレータに依存します。

ほとんどの組込み操作は、現在の行に作用します。ただし、`execute`操作がデータ・コントロール自体をリフレッシュします。

使用可能な操作は、データ・コントロールのタイプと、基礎となるビジネス・サービスの機能によって異なります。次に、組込み操作すべてのリストを示します。

- `Create`: 現在の行になる新規行を作成。この新規行は、行セットにも追加されます。

- `CreateInsert`: 現在の行となる新規行を作成し、その行を行セットに挿入。

- `Create With Parameters`: 名前付きパラメータを使用して現在の行になる新規行を作成し、その行を行セットに挿入。

- `Delete`: 現在の行を削除。

- `Execute`: アクセッサ・メソッドを実行または再実行することによって、データ・コレクションをリフレッシュします。

  `ExecuteWithParams`: パラメータとして渡された変数に新しい値を割り当てた後で、関連した問合せを実行または再実行することにより、データ・コレクションをリフレッシュ。この操作は、パラメータ化された問合せに基づくデータ・コントロール・コレクション・オブジェクトに対してのみ使用できます。

- `First`: 行セット内の最初の行を現在の行に設定します。

- `Last`: 行セット内の最後の行を現在の行に設定します。

- `Next`: 行セット内の次の行を現在の行に設定します。

- `Next Set`: 1つ後の行のセットに移動します。

- `Previous`: 行セット内の前の行を現在の行に設定します。

- `Previous Set`: 1つ前の行のセットに移動します。

- `removeRowWithKey`: パラメータとして渡された行キーのシリアライズされた文字列表現を使用して行の検索を試行します。検出されると、その行が削除されます。

- `setCurrentRowWithKey`: パラメータとして渡された行キーのシリアライズされた文字列表現を使用して行の検索を試行します。検出されると、その行が現在の行になります。

- `setCurrentRowWithKeyValue`: パラメータとして渡された主キーの属性値を使用して行の検索を試行します。検出されると、その行が現在の行になります。

## 14.6 「データ・コントロール」パネルでのデータバインドされたUIコンポーネントの作成

「データ・コントロール」パネルから項目をドラッグし、それを特定のUIコンポーネントとしてページ上にドロップすることで、データバインドされたユーザー・インタフェースを設計できます。データ・コントロールを使用してUIコンポーネントを作成すると、そのコンポーネントを選択したデータ・コントロールにバインドするために必要な、様々なコードおよびオブジェクトがJDeveloperによって自動的に作成されます。

「データ・コントロール」パネルでは、各データ・コントロール・オブジェクトが特定のアイコンで表されます。表14-6は、各アイコンが表すデータ・コントロール・オブジェクト、「データ・コントロール」パネルの階層内で表示される場所、そのアイコンを使用して作成できるコンポーネントを示しています。

表14-6 「データ・コントロール」パネルのアイコンおよびオブジェクト階層

アイコン | 名前 | 説明 | 作成できるコンポーネント
--- | --- | --- | ---
| データ・コントロール | データ・コントロールを表します。 | 他のオブジェクトのコンテナとして機能し、作成には使用されません。
| コレクション | アクセッサ・メソッドまたは操作から返される名前付きデータ・コレクションを表します。 | フォーム、表、グラフ、ツリー、レンジ・ナビゲーションの各コンポーネント、マスター/ディテール・コンポーネントおよび選択リストの各コンポーネント。
| 構造化された属性 | Javaプリミティブ・タイプ(属性として示される)または任意のタイプのコレクションのいずれでもない、戻りオブジェクトを示します。 | フォーム、ラベル、テキスト・フィールド、日付、値リストおよび選択リストの各コンポーネント。
| 属性 | オブジェクト内の個別のデータ要素(行の属性など)を表します。 | ラベル、テキスト・フィールド、日付、値リスト、および選択リストの各コンポーネント。
| キー属性 | データ・コントロール構造ファイル、またはビジネス・サービス自体で、主キー属性として宣言されたオブジェクト属性を表します。 | ラベル、テキスト・フィールド、日付、値リスト、および選択リストの各コンポーネント。
| メソッド | データ・コントロールのメソッドまたは操作、またはその公開された構造のいずれかを表し、パラメータの受入れや、ビジネス・ロジックの実行を行ったり、オプションで単一の値、構造、またはコレクションを戻したりすることができます。 | コマンド・コンポーネント。 パラメータを受け入れるメソッドの場合: コマンド・コンポーネントおよびパラメータ付きフォーム。
| メソッド戻り値 | メソッドまたはその他の操作から返されたオブジェクトを表します。戻されたオブジェクトは、単一の値またはコレクションです。 メソッド戻りは、これを戻すメソッドの下に、子として表示されます。メソッド戻りの下に子として表示されるオブジェクトは、コレクションの属性、親コレクションに関連するアクションを実行する他のメソッド、または親コレクションで実行できる操作などです。 | 単一の値の場合: テキスト・フィールドおよび選択リスト。 コレクションの場合: フォーム、表、ツリー、レンジ・ナビゲーションの各コンポーネント。 単一値のメソッド戻り値がドロップされると、メソッドはフレームワークにより自動的に起動されなくなります。メソッドを起動するために、対応するメソッドをボタンとしてドロップできます。フォームがタスク・フローの一部の場合、メソッドを起動するためのメソッド・アクティビティを作成できます。
| 操作 | 親オブジェクトに対してアクションを実行する、組込みデータ・コントロール操作を表します。 | ボタンおよびリンクなどのUIコマンド・コンポーネント。
| パラメータ | メソッドまたはその下に表示される操作によって宣言されたパラメータ値を表します。 | ラベル、テキストおよび選択リストの各コンポーネント。

### 14.6.1 「データ・コントロール」パネルの使用方法

JDeveloperは、ドロップするデータ・コントロール項目に対してそれぞれ選択できるUIコンポーネントの事前定義済セットを備えています。

始める前に

「データ・コントロール」パネルの各種オブジェクトに関する知識があると役立つ場合があります。詳細は、第14.6項「「データ・コントロール」パネルでのデータバインドされたUIコンポーネントの作成」を参照してください。

次のタスクを完了する必要があります。

- 第14.5.1項「データ・コントロールの作成方法」の説明に従って、データ・コントロールを作成します。

- 第12.3.1.2項「MAF AMXページの作成」の説明に従って、MAF AMXページを作成します。

「データ・コントロール」パネルを使用してUIコンポーネントを作成するには:

1. 「データ・コントロール」パネルで項目を選択し、ページのビジュアル・エディタにその項目をドラッグします。パネル内の各アイテムの定義は、表14-6「「データ・コントロール」パネルのアイコンおよびオブジェクト階層」を参照してください。

2. ポップアップ・メニューから、UIコンポーネントを選択します。

   「データ・コントロール」パネルから項目をドラッグしてページにドロップすると、ドロップした項目に使用できるすべてのデフォルトUIコンポーネントのポップアップ・メニューが表示されます。表示されるコンポーネントは、プロジェクト内のライブラリに基づきます。

   図14-6は、「データ・コントロール」パネルのデータ・コレクションがページにドロップされる際に表示されるポップアップ・メニューを示しています。

   図14-6 「データ・コントロール」パネルからのコンポーネントのドロップ

   ポップアップ・メニューから選択したコンポーネントに応じて、そのコンポーネントの外観を定義するためのダイアログが自動的に表示されます。たとえば、ポップアップ・メニューから「フォーム」を選択すると、「フォーム・フィールドの編集」ダイアログが開きます。コンポーネントを選択すると、JDeveloperではビジュアル・エディタのページにそのUIコンポーネントが挿入されます。

   デフォルトで選択されたUIコンポーネントはまず、対応するビジネス・オブジェクト上で設定されているUIヒントで決定されます。UIヒントが設定されていない場合、JDeveloperでは標準のフォームおよび表の入力コンポーネントと読取り専用のフォームおよび表の出力コンポーネントが使用されます。リストのコンポーネントは、データ・コントロール・オブジェクトをドロップするときに選択するリストの型に基づいて決定されます。

   デフォルトでは、データ・コントロールの使用時に作成されるUIコンポーネントは、MAFデータ・コントロール内の属性にバインドされ、次のような組込み機能を装備できます。

   - データバインド・ラベル

   - ツールチップ

   - 書式設定

   - 基本的なナビゲーション・ボタン

   - 検証(検証規則が特定の属性にアタッチされている場合。)

   デフォルトのコンポーネントは、そのまま使用しても十分機能します。ただし、特定のニーズに合せて変更することができます。

   ヒント: ページで使用するMAFデータバインド・コンポーネントのタイプを変更する場合は、ビジュアル・エディタまたは構造ウィンドウを使用してそのコンポーネントを削除し、「データ・コントロール」パネルから新しいコンポーネントをドラッグ・アンド・ドロップする方法が最も簡単です。データバインドされたコンポーネントをページから削除するときにビジュアル・エディタまたは構造ウィンドウを使用する場合、ページ定義ファイルの関連するバインディング・オブジェクトが他のどのコンポーネントからも参照されていなければ、これらのバインディング・オブジェクトはJDeveloperによって自動的に削除されます(ソース・エディタを使用する場合は、バインディング・オブジェクトの自動削除は行われません)。

### 14.6.2 「データ・コントロール」パネルの使用時の処理

「データ・コントロール」パネルを使用してアプリケーションを構築すると、JDeveloperによって次の処理が行われます。

- `DataBindings.cpx`ファイルがプロジェクトのデフォルト・パッケージに作成されます(このファイルが存在しない場合のみ)。また、そのページ用にエントリが追加されます。

  `DataBindings.cpx`ファイルは、アプリケーションのバインディング・コンテキストを定義します。バインディング・コンテキストはコンテナ・オブジェクトの1つで、使用可能なデータ・コントロールおよびデータ・バインディング・オブジェクトのリストを含みます。`DataBindings.cpx`ファイルによって、個々のページがページ定義ファイルに含まれるバインディング定義にマップされ、これらのページが使用するデータ・コントロールが登録されます。詳細は、第12.3.3.5項「生成されるドラッグ・アンド・ドロップ・アーティファクトに関する必知事項」を参照してください。

- META-INFディレクトリに`adfm.xml`ファイルが作成されます。このファイルにより、`DataBindings.cpx`ファイルのレジストリが作成され、バインディング・コンテキストを作成できるように、実行時にアプリケーションで特定できます。

- パッケージ定義ファイルがページ定義サブパッケージに追加されます(ページに対してファイルが存在しない場合)。デフォルトのサブパッケージは、`adfmsrc`ディレクトリにある`mobile.pageDefs`です。

  ヒント: 「プロジェクト・プロパティ」ダイアログ(プロジェクト・ノードをダブルクリックするとアクセスできる)のADFモデル設定ページで、パッケージ構成(名前と場所など)を設定できます。

  ページ定義ファイル(`pageName` `PageDef.xml`)は、アプリケーションのビュー・レイヤーにある各ページのバインディング・コンテナを定義します。バインディング・コンテナでは、ページのすべてのバインディング・オブジェクトへのランタイム・アクセスが行われます。ページ定義ファイルの詳細は、第12.3.3.5項「生成されるドラッグ・アンド・ドロップ・アーティファクトに関する必知事項」を参照してください。

  ヒント: 現在のバインディング・コンテナは、プログラム的なアクセス用の`AdfContext`からも入手できます。

- ページ定義ファイルが構成されます。この処理では、ページによって参照されるバインディング・オブジェクトの定義も追加されます。

- ページに指定のコンポーネントを追加します。

  これらの事前作成済のコンポーネントには、ページ定義ファイルのバインディング・オブジェクトを参照するデータ・バインディング式言語(EL)式が含まれます。詳細は、第14.3.1項「データ・バインディングEL式について」を参照してください。

- UIコンポーネントが必要とするすべてのライブラリ、ファイル、および構成要素が追加されます。データバインドされたコンポーネントに必要なアーティファクトの詳細は、第2.2.2項「MAFアプリケーションの作成時に行われる処理」を参照してください。

## 14.7 実行時に行われる処理: バインディング・コンテキストの動作方法

ページにMAFバインディングが含まれている場合、実行時には、クライアントまたはコントローラから起動されたビジネス・サービスとのやり取りが、バインディング・コンテキストと呼ばれるシングル・オブジェクトを介して、アプリケーションによって管理されます。バインディング・コンテキストは、アプリケーション内のすべてのデータ・コントロールおよびページ定義のランタイム・マップ(名前付きdataでEL式`#{data}`を使用してアクセス可能)です。

図14-7に示すように、MAFでは、アプリケーション、`DataBindings.cpx`およびページ定義ファイルからバインディング・コンテキストが作成されます。設計時に使用可能なデータ・コントロールは、すべての`DataControls.dcx`ファイルとワークスペース内のすべてのアプリケーション・モジュールの集合体によって定義されますが、実行時にアプリケーションで使用可能なデータ・コントロールは、`DataBindings.cpx`ファイルで定義されます。`DataBindings.cpx`ファイルは、アプリケーションのページで使用されているすべてのデータ・コントロールを一覧表示し、ページ定義ファイルに定義したバインディング・オブジェクトが格納されているバインディング・コンテナをWebページのURLにマップします。ページ定義ファイルは、アプリケーション・ページで使用するバインディング・オブジェクトを定義します。各ページにページ定義ファイルが1つあります。

バインディング・コンテキストには、これらのオブジェクトのライブ・インスタンスは含まれません。かわりに、マップには必要に応じてデータ・コントロールまたはバインディング・コンテナ・オブジェクトになる参照が含まれます。たとえば、オブジェクト(ページ定義など)がアプリケーションから解放されるか、タスク・フローが終了するか、リクエストの終了時にバンディング・コンテナまたはデータ・コントロールが解放されると、データ・コントロールおよびバンディング・コンテナは参照オブジェクトに戻ります。`DataBindings.cpx`ファイルの詳細は、第12.3.3.5項「生成されるドラッグ・アンド・ドロップ・アーティファクトに関する必知事項」を参照してください。

図14-7 ファイル・バインディング・コンテキストの実行時の使用

注意: コンポーネントを構成するときは、使用するバインディング・スタイルを十分に考慮してください。より具体的に言うと、標準的なバインディングをマネージドBeanバインディングと組み合せると、解釈を誤った動作が発生することがよくあります。これは、バインディング・インフラストラクチャとマネージドBeanインフラストラクチャとの間でクラス・インスタンスが一致する可能性が低いためです。バインディングが混在すると、UIに直接リンクされていないインスタンスの動作がコールされる場合があります。

MAFでのバインディングの使用の詳細は、次の項を参照してください。

- 第12.3.3.4項「生成されるバインディングに関する必知事項」

- 第12.3.3.6項「MAF AMXエディタの「バインディング」タブの使用方法」

- 第12.3.3.7項「未使用のバインディングの削除に関する必知事項」

## 14.8 データ・コントロールの構成

データ・コントロールを作成すると、データ・コントロールについて、値と動作の標準的なセットが設定されます。たとえば、属性のラベルのクライアントでの表示方法がデータ・コントロールによって決定されます。データ・コントロールの要素に対応するデータ・コントロール構造ファイルを作成および変更することによって、これらの値と動作を構成できます。まず、`.dcx`ファイルの概要エディタを使用して、データ・コントロール構造ファイルを生成します。

### 14.8.1 データ・コントロールの編集方法

データ・コントロールを構成可能にするには、`DataControls.dcx`ファイルの概要エディタを使用し、データ・コントロールに含まれるオブジェクトに対応するデータ・コントロール構造ファイルを作成します。個別のデータ・コントロール構造ファイルを編集できるようになります。

始める前に

データ・コントロール構成の一般的な知識があると役立ちます。詳細は、第14.8項「データ・コントロールの構成」を参照してください。

次のタスクを完了する必要があります。

  第14.5.1項「データ・コントロールの作成方法」の説明に従って、データ・コントロールを作成します。

データ・コントロールを編集するには:

1. 「アプリケーション」ウィンドウで、DataControls.dcxをダブルクリックします。

2. 概要エディタで、構成するオブジェクトを選択し、「編集」アイコンをクリックしてデータ・コントロール構造ファイルを生成します(図14-8を参照)。

   図14-8 データ・コントロール・レジストリの「編集」ボタン

3. データ・コントロール構造ファイルの概要エディタで、必要な変更を加えます。

### 14.8.2 データ・コントロール編集時の処理

データ・コントロールを編集すると、JDeveloperにより、影響を受けたコレクションのメタデータが含まれているデータ・コントロール構造ファイルが作成され、そのファイルが概要エディタで開かれます。このファイルには、そのコレクション固有のデータ・コントロールの構成データ(データ・オブジェクトに対して指定したUIヒントまたはバリデータなど)が保存されています。

データ・コントロール構造ファイルのベース名は、対応するデータ・オブジェクトのものと同じです。たとえば、Customer.javaエンティティBeanに対応するコレクション・ノードが選択されている状態で`「編集」`アイコンをクリックすると、データ・コントロール構造ファイルの名前は`Customer.xml`となります。データ・コントロール構成ファイルはBeanクラスのパッケージに対応するパッケージに生成され、パッケージ名に`persdef`が付加されます。たとえば、`Customer.java` Beanが`model`パッケージ内にある場合、`Customer.xml`データ・コントロール定義ファイルは`persdef.model`パッケージ内に生成されます。データ・コントロール構成ファイルが生成された後、概要エディタを使用してそのファイルをさらに構成できます。

データ・コントロール構造ファイルには、次の情報が含まれます。

- 属性: サービスのすべての属性を表します。たとえば、エンティティBeanの場合は、データベース列にマップされたBeanプロパティごとに属性が存在します。また、一時属性も追加できます。これらの属性のUIでの表示方法を定義するUIヒントを設定できます。属性値が必須か、一意である必要があるか、表示可能かどうかなどの他のプロパティを設定することもできます。詳細は、第14.9項「属性の使用」を参照してください。

  属性の検証を設定したり、カスタム・プロパティを作成することもできます。検証の詳細は、第14.12項「属性の検証」を参照してください。

- アクセッサ: 結果セットを返すデータ・コントロール要素を表します。

- 操作: データ・コントロールの組込み操作で使用されるデータ・オブジェクト上のメソッドを表します。たとえば、それぞれ「作成」および「削除」組込み操作で使用される`add`および`remove`メソッドなどです。

図14-9に、`Item` Beanのデータ・コントロール構造ファイルを示します。

図14-9 概要エディタでのデータ・コントロール構造ファイル

注意: データ・コントロール構造ファイルの概要エディタには、データ・オブジェクトに関連付けられているすべての属性、アクセッサおよび操作が表示されます。ただし、データ・コントロール構造ファイルのXMLソースには、編集した要素の定義のみが含まれます。基本要素は、データ・オブジェクトからイントロスペクションされます。また、基礎となるデータ・オブジェクトに変更を加えると、データ・コントロールはそれらの変更を継承します。

### 14.8.3 データ・コントロールのMDSカスタマイズに関する必知事項

データ・コントロールに含まれるすべてのオブジェクトをOracle Metadata Services (MDS)カスタマイズ可能にする必要がある場合は、パッケージされたアプリケーションにこれらのオブジェクトのデータ・コントロール構造ファイルが含まれている必要があります。

アダプタ・フレームワークに基づくデータ・コントロールを作成する際には、データ・コントロール構造ファイルはデフォルトでは生成されません。これは、特定のオブジェクトにメタデータを追加しない場合、データ・コントロールにこれらのファイルは必要ないからです。通常、データ・コントロール構造ファイルがデータ・コントロール・オブジェクトに対して生成されるのは、データ・コントロールを編集して宣言的メタデータ(UIヒントやバリデータなど)をそのオブジェクトに追加した後のみです(第14.8.1項「データ・コントロールを編集する方法」を参照)。データ・コントロール・オブジェクトごとにデータ・コントロール構造ファイルを作成するには、データ・コントロール・オブジェクトごとにその手順を繰り返す必要があります。

MDSの詳細は、第10章「MDSを使用したMAFアプリケーションのアーティファクトのカスタマイズ」を参照してください。

## 14.9 属性の使用

ビジネス・サービスのデータ・コントロールを作成する際、個々のデータ・オブジェクトについて作成するデータ・コントロール構造ファイルで宣言的にデータ・オブジェクトの永続属性の機能を拡張できます。たとえば、UIコンポーネントの属性のデフォルト表示を制御する検証ルールを作成し、UIヒントを設定できます。

これらのプロパティはデータ・コントロール構造ファイルの概要エディタの「属性」ページで設定します。データ・コントロール構造ファイルの作成の詳細は、第14.8.1項「データ・コントロールの編集方法」を参照してください。

### 14.9.1 属性を主キーとして指定する方法

データ・オブジェクトのデータ・コントロール構造ファイルの概要エディタで、そのデータ・オブジェクトの主キーとして属性を指定できます(データ・オブジェクトの基礎クラスでまだ指定していない場合)。

始める前に

属性プロパティの設定方法の知識があると役立ちます。詳細は、第14.9項「属性の使用」を参照してください。

次のタスクを完了する必要があります。

  第14.8.1項「データ・コントロールの編集方法」の説明に従って、必要なデータ・コントロール構造ファイルを作成します。

属性を主キーとして設定する手順は次のとおりです。

1. 「アプリケーション」ウィンドウで、使用するデータ・コントロール構造ファイルをダブルクリックします。

2. 概要エディタで、「属性」ナビゲーション・タブをクリックします。

3. 「属性」ページで、主キーとして指定する属性を選択して、「詳細」タブをクリックします。

4. 「詳細」ページで、「キー属性」プロパティを設定します。

注意: 属性がクラス内ですでに主キーとして指定されている場合、データ・コントロールはその設定を継承し、「キー属性」チェックボックスが選択されます。ただし、この場合は、「キー属性」オプションを選択解除することはできません。

### 14.9.2 属性の静的なデフォルト値を定義する方法

値のタイプが「リテラル」に設定されている場合、「詳細」セクションの「値」フィールドには、属性のデフォルト値を静的に指定できます。たとえば、`ServiceRequest`エンティティBeanの`Status`属性のデフォルト値を`Open`に設定したり、`User` Beanの`UserRole`属性のデフォルト値を`user`に設定できます。

始める前に

属性プロパティの設定方法の知識があると役立ちます。詳細は、第14.9項「属性の使用」を参照してください。

属性の静的なデフォルト値を定義する手順:

1. 「アプリケーション」ウィンドウで、使用するデータ・コントロール構造ファイルをダブルクリックします。

2. 概要エディタで、「属性」ナビゲーション・タブをクリックします。

3. 「属性」ページで編集する属性を選択し、「詳細」タブをクリックします。

4. 「詳細」ページで、「リテラル」オプションを選択します。

5. 「リテラル」オプションの下のテキスト・フィールドに、属性のデフォルト値を入力します。

### 14.9.3 属性へのUIヒントの設定方法

属性のUIヒントを設定し、それらの属性を使用するすべてのUIコンポーネントでそれらの属性が一貫したローカライズ可能な方法で表示およびラベル付けされるように設定できます。UIヒントにより、属性、ラベル、ツールチップ、フィールドを自動的に送信する必要があるかどうかを表示するために使用するUIコンポーネントのタイプなどが決定されます。指定された属性を表示するか非表示にするかも決定できます。属性に対してUIヒントを作成するには、「アプリケーション」ウィンドウからアクセス可能な、データ・オブジェクトのデータ・コントロール構造ファイルの概要エディタを使用します。

始める前に

属性プロパティの設定方法の知識があると役立ちます。詳細は、第14.9項「属性の使用」を参照してください。

次のタスクを完了する必要があります。

  第14.8.1項「データ・コントロールの編集方法」の説明に従って、必要なデータ・コントロール構造ファイルを作成します。

UIヒントを設定する手順:

1. 「アプリケーション」ウィンドウで、使用するデータ・コントロール構造ファイルをダブルクリックします。

2. 概要エディタで、「属性」ナビゲーション・タブをクリックします。

3. 「属性」ページで編集する属性を選択し、「UIヒント」タブをクリックします。

4. 「UIヒント」セクションで、目的のUIヒントを設定します。

### 14.9.4 属性へのUIヒントの設定時の処理

属性に対してUIヒントを設定すると、それらのヒントはプロパティとして格納されます。プロパティのタグがデータ・オブジェクトのデータ・コントロール構造ファイルに追加され、プロパティの値はリソース・バンドル・ファイルに保存されます。リソース・バンドル・ファイルが存在しない場合は、データ・コントロールのパッケージに生成され、初めてUIヒントを設定した際にプロジェクト名に従って名前が設定されます。

例14-14に、`Item.xml`データ・コントロール構造ファイルにおける`price`属性のコードを示します。属性に対して設定されているラベルおよびフォーマット・タイプ・ヒントのタグも含まれます。

例14-14 UIヒントのXMLコード

    <PDefAttribute
      Name="price">
      <Properties>
        <SchemaBasedProperties>
          <LABEL
            ResId="${adfBundle['model.ModelBundle']['model.Item.price_LABEL']}"/>
          <FMT_FORMATTER ResId="${adfBundle['model.ModelBundle']
                                           ['model.Item.price_FMT_FORMATTER']}"/>
        </SchemaBasedProperties>
      </Properties>
    </PDefAttribute>

例14-15に、`ModelBundle.properties`リソース・バンドル・ファイルにおけるラベルおよびフォーマット・タイプ・ヒントの対応するエントリを示します。これらにはプロジェクトのローカライズ可能なプロパティすべての値も含まれています。

例14-15 UIヒントのリソース・バンドル・コード

    model.Item.price_LABEL=Price
    . . .
    model.Item.price_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter

### 14.9.5 EL式を使用してUIヒントにアクセスする方法

EL式を使用してUIヒントにアクセスし、ページにデータとしてヒント値を表示できます。UIヒントには、データバインドされたコンポーネントをページにドロップした後に作成するバインディング・インスタンスを通じてアクセスできます。

例14-16は、DeviceFeaturesデータ・コントロールを使用して生成されました。この例は、MAFフォームとして接続をドラッグ・アンド・ドロップし、`displayName`フィールドと`nickname`フィールドのみを保持することによって生成されるEL式を示しています。太字で表示されるラベルは、ELを使用したUIヒント取得の例です。

例14-16 ELを使用したUIヒントへのアクセス

    <amx:panelFormLayout id="pfl2">
          <amx:inputText value="#{row.bindings.displayName.inputValue}"
                         label="#{bindings.Contact.hints.displayName.label}" id="it9"/>
          <amx:inputText value="#{row.bindings.nickname.inputValue}"
                         label="#{bindings.Contact.hints.nickname.label}"
                         id="it10"/>
    </amx:panelFormLayout>af:panelHeader id="ph1"

## 14.10 Beanデータ・コントロールの作成および使用

Java Beanデータ・コントロールでは、POJO (Plain Old Java Object)から自身のデータ構造を取得します。Java Beanデータ・コントロールを作成するには、(「アプリケーション」ウィンドウで)Javaクラス・ファイルを右クリックし、「データ・コントロールの作成」を選択します。

注意: Java Beanがバックグラウンド・スレッドを使用してUI内のデータを更新する場合、手動で`oracle.adfmf.framework.api.AdfmfJavaUtilities.flushDataChangeEvent`をコールする必要があります。`flushDataChangeEvent`メソッドの詳細は、第14.13項「データ変更イベントについて」を参照してください。

### 14.10.1 Beanクラス変数のシリアライズに関する必知事項

MAFでは、一時変数として宣言されたJavaScript Object Notation (JSON)データBeanクラス変数にはシリアライズしません。一連のネストされたオブジェクトをシリアライズしないようにするには、それらを一時オブジェクトとして定義する必要があります。この方法を使用すると、オブジェクトのネストが原因で循環オブジェクトが作成されるのを防ぐこともできます。

次のシナリオを考えてみましょう。`Employee`オブジェクトが、その子として従業員の上司を表す`Employee`オブジェクトを保持しているとします。その子を一時オブジェクトとして宣言しなかった場合、実行時に子の`Employee`オブジェクトを計算しようとすると、一連のネストされたシリアライズ・オブジェクトが作成されます。

JavaオブジェクトをJSONオブジェクトにシリアライズおよびシリアライズ解除するには、`JSONBeanSerializationHelper`クラスを使用します。`JSONBeanSerializationHelper`クラスを使用すると、独自のカスタムJSONシリアライズおよびシリアライズ解除を実装できます。また、このクラスでは、JSONシリアライズ(およびシリアライズ解除)処理後にJSONオブジェクトを変更するためのフックが提供されます。`JSONBeanSerializationHelper`クラスは`GenericTypeSerializationHelper`クラスに似ており、RESTおよびSOAPベースのWebサービス内で、`GenericType`オブジェクトのシリアライズおよびシリアライズ解除に使用できます。詳細は、MAF Javadocの`oracle.adfmf.framework.api.JSONBeanSerializationHelper`クラスを参照してください。

MAFでは、`GregorianCalendar`クラスのオブジェクトのシリアライズはサポートされません。`JSONBeanSerializationHelper`クラスでは、`GregorianCalendar`クラスのオブジェクトをシリアライズできません。これは、`GregorianCalendar`クラスに循環参照が含まれるためです。かわりに、`java.util.Dateまたはjava.sql.Date`をデータ操作に使用します。次の例は、`java.util.Date`を使用して`GregorianCalendar`オブジェクトを変換する方法を示しています。

    Calendar calDate = new GregorianCalendar();
    calDate.set(1985, 12, 1); // "January 1, 1986"
    Date date = calDate.getTime();

## 14.11 DeviceFeaturesデータ・コントロールの使用方法

MAFでは、アプリケーション内でDeviceFeaturesデータ・コントロールを通じて使用できるデバイス固有の機能が公開されます。DeviceFeaturesデータ・コントロールは、新しいMAFアプリケーションを作成するときに「データ・コントロール」パネルに表示されるコンポーネントです。Cordova Java APIは、このデータ・コントロールを通じて抽象化されます。これにより、MAF AMXとして実装されたアプリケーション機能から、デバイスに埋め込まれた様々なサービスにアクセスできるようになります。DeviceFeaturesデータ・コントロールから提供される操作をMAF AMXページ内にドラッグ・アンド・ドロップすることによって、デバイスに格納されたユーザーのコンタクト先を管理する機能の追加、電子メール・メッセージとSMSテキスト・メッセージの両方の作成と送信、デバイスの位置の確認、デバイスのカメラの使用およびデバイスのファイル・システムに格納されたイメージの取得を行うことができます。次の項では、これらの操作を宣言的に使用する方法や、JavaコードとJavaScriptを使用して実装する方法など、各操作について詳しく説明します。

図14-10 概要エディタでのMAFのDeviceFeaturesデータ・コントロール

MAFアプリケーション・テンプレートを使用してアプリケーションを作成すると、自動的にDeviceFeaturesデータ・コントロールが「データ・コントロール」パネルに表示されます。図14-10は、「概要」エディタ内のDeviceFeaturesデータ・コントロールを示しています。次のメソッドを使用できます。

- `addLocalNotification`

- `cancelLocalNotification`

- `createContact`

- `displayFile`

- `findContacts`

- `getPicture`

- `removeContact`

- `sendEmail`

- `sendSMS`

- `startLocationMonitor`

- `updateContact`

ページを作成したら、DeviceFeaturesデータ・コントロールのメソッド(またはそれらのメソッド内にネストされたその他のオブジェクト)を「データ・コントロール」パネルからMAF AMXビューにドラッグして、関連する機能にバインドされたコマンド・ボタンやその他のコンポーネントを作成できます。デフォルトのバインディングを受け入れるか、ELを使用してそれらのバインディングを変更できます。また、JavaScriptやJavaを使用して、機能を実装または構成することもできます。データ・コントロールをMAFアプリケーションに含める方法については、第12.3.3項「MAF AMXページへのデータ・コントロールの追加方法」を参照してください。

`DeviceManager`は、デバイスの機能へのアクセスを可能にするオブジェクトです。このオブジェクトのハンドルは、`DeviceManagerFactory.getDeviceManager`をコールすることによって取得できます。次の項では、`DeviceManager`オブジェクトを使用して`getPicture`や`createContact`などのメソッドを起動する方法について説明します。

ネットワーク・アクセスを除き、すべてのApache Cordova対応デバイス機能へのアクセスは、MAFアプリケーションの場合デフォルトでは有効になっていません。DeviceFeaturesデータ・コントロールによって公開されている操作が実行時に正常に機能するには、関連するプラグインがMAFアプリケーションで有効になっている必要があります。たとえば、DeviceFeaturesデータ・コントロールの`sendSMS`操作を使用する場合は、MAFアプリケーションでSMSプラグインを有効にする必要があります。プラグインは手動で有効にするか、MAFアプリケーションで関連するプラグインが有効になっていない操作をドラッグ・アンド・ドロップしたときにJDeveloperによって表示されるダイアログで該当するオプションを選択して有効にすることができます。たとえば、まだSMSプラグインが有効になっていないMAFアプリケーションのMAF AMXページに`sendSMS`操作をドラッグ・アンド・ドロップすると、JDeveloperによって図14-11のダイアログが表示されます。

図14-11 DeviceFeaturesデータ・コントロールの操作用プラグインの有効化

操作に必要なプラグインが有効になっていない場合、MAF AMXページのソース・ファイル内に警告メッセージが表示されます。たとえば、MAFアプリケーションでSMSプラグインが有効になっていないと仮定しましょう。MAF AMXページの、アプリケーションが`sendSMS`操作を起動する箇所に、図14-12に示した警告メッセージが表示されます。この問題を解決するには、第9章「MAFアプリケーションでのプラグインの使用方法」の説明に従って、プラグインを手動で有効にします。

図14-12 DeviceFeaturesデータ・コントロールの操作に必要なプラグイン

### 14.11.1 getPictureメソッドを使用して写真撮影を可能にする方法

DeviceFeaturesデータ・コントロールに含まれる`getPicture`メソッドをMAFアプリケーションで使用すると、デバイスのカメラとフォト・ライブラリを活用し、エンド・ユーザーが写真を撮影したり、既存のイメージを取得できるようにすることが可能です。例14-17は、エンド・ユーザーがデバイスのカメラを使用して写真を撮影することを可能にするJavaScriptコードを示しています。例14-18と例14-19は、エンド・ユーザーが写真を撮影したり、保存済イメージを取得することを可能にするJavaコードを示しています。`getPicture`メソッドについては、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。

次のパラメータによって、イメージの取得元と、そのイメージを戻す方法が制御されます。

注意: 撮影される写真に`targetWidth`、`targetHeight`および`quality`を指定しないと、使用されるデフォルト値が最大値となり、メモリー障害が発生する可能性があります。

- `quality:` 保存済イメージの質を設定します。範囲は1から100 (100を含む)です。大きい数値を指定すると質は高くなりますが、ファイル・サイズも増大します。JPEGイメージ(`encodingType`で指定します)にのみ適用されます。

- `destinationType:` 戻り値の形式を次から選択します。

  - `DeviceManager.CAMERA_DESTINATIONTYPE_DATA_URL (0)`: Base64でエンコードされた文字列としてイメージを戻します。この値は、プログラムで使用される場合、`DeviceManager`.`CAMERA_DESTINATION_DATA_URL`を使用して`enum`としても指定されます。イメージをイメージ・コンポーネント内に表示するには、戻された値の前に`data:image/gif;base64`を付ける必要があります。

  - `DeviceManager.CAMERA_DESTINATIONTYPE_FILE_URI` `(1)`: イメージ・ファイルのパスを戻します。この値は、プログラムで使用される場合、DeviceManager.`CAMERA_DESTINATION_FILE_URI`を使用する列挙としても指定されます。

  注意: ファイルURIが`getPicture`メソッドによって戻される場合、ファイルのサイズを決定するために使用する前にファイルURIから問合せパラメータを削除する必要があります。次に例を示します。 `String fileURI = ...getPicture(...);` `fileURI = fileURI.substring(0, result.lastIndexOf("?"));`

- `sourceType:` 写真のソースを次から選択して設定します。

  - `DeviceManager.CAMERA_SOURCETYPE_PHOTOLIBRARY (0)`: ユーザーは保存済のイメージから選択できます。この値は、プログラムで使用される場合、`DeviceManager.CAMERA_SOURCETYPE_PHOTOLIBRARY`を使用する列挙としても指定されます。

  - `DeviceManager.CAMERA_SOURCETYPE_CAMERA (1)`: ユーザーはデバイスのカメラを使用して写真を撮影できます。この値は、プログラムで使用される場合、`DeviceManager.CAMERA_SOURCETYPE_CAMERA`を使用する列挙としても指定されます。

  - `DeviceManager.CAMERA_SOURCETYPE_SAVEDPHOTOALBUM` `(2)`: ユーザーは既存のフォト・アルバムから選択できます。この値は、プログラムで使用される場合、`DeviceManager.CAMERA_SOURCETYPE_SAVEDPHOTOALBUM`を使用する列挙としても指定されます。

- `allowEdit:` 選択する前にイメージの単純な編集を許可するかどうかを選択します(ブール)。

- `encodingType:` 戻されるイメージ・ファイルのエンコーディングを次から選択します。

  - `DeviceManager.CAMERA_ENCODINGTYPE_JPEG` (0): 戻されるイメージをJPEGファイルとしてエンコードします。この値は、プログラムで使用される場合、`DeviceManager.CAMERA_ENCODINGTYPE_JPEG`を使用する列挙としても指定されます。

  - `DeviceManager.CAMERA_ENCODINGTYPE_PNG` (1): 戻されるイメージをPNGファイルとしてエンコードします。この値は、プログラムで使用される場合、`DeviceManager.CAMERA_ENCODINGTYPE_PNG`を使用する列挙としても指定されます。

- `targetWidth:` 幅をピクセル単位で設定してイメージのスケールを変更します。アスペクト比は維持されます。負の値または0の場合、イメージの元のディメンションが使用されます。

- `targetHeight:` 高さをピクセル単位で設定してイメージのスケールを変更します。アスペクト比は維持されます。負の値または0の場合、イメージの元のディメンションが使用されます。

DeviceFeaturesデータ・コントロールを使用してgetPicture操作をカスタマイズするには:

1. 「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールからgetPicture操作をドラッグし、ボタンとしてページにドロップします。

   ユーザーがよりきめ細かく制御できるようにするには、getPicture操作をパラメータ・フォームとしてドロップします。これにより、エンド・ユーザーは写真を撮影したり既存のイメージを選択する前に、設定を指定できるようになります。

2. アクションの編集ダイアログで、前述のすべてのパラメータの値を設定します。イメージがファイル名として戻されるように、`destinationType` `= 1`を指定するようにします。

3. getPictureの戻り値をドラッグし、それを出力テキストとしてページ上にドロップします。

4. 「共通コンポーネント」パネルで、「コンポーネント・パレット」から「イメージ」をドラッグし、ページ上にドロップします。

5. イメージの`source`属性を、`getPicture`操作の戻り値に設定します。バインディングの式は、`#{bindings.Return.inputValue}`である必要があります。

図14-13は、エンド・ユーザーのフォト・ライブラリからイメージを表示するためのバインディングを示しています。

図14-13 デザインタイムでフォト・ライブラリからイメージを表示するためのバインディング

このアプリケーションを実行すると、イメージ・チューザが自動的に表示され、エンド・ユーザーは表示するイメージを選択できます。イメージ・チューザが自動的に表示される理由は、イメージ・コントロールが`getPicture`操作の戻り値にバインドされており、それによって`getPicture`操作が起動するためです。

注意: `getPicture`メソッドのタイムアウト値は、5分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

次に示すプラットフォーム固有の問題に留意してください。

- iOS

  - いくつかのデバイスでメモリー・エラーが発生しないようにするには、qualityを50未満に設定します。

  - `destinationType` `FILE_URI`を使用すると、写真はアプリケーションの一時ディレクトリに保存されます。

  - アプリケーションの一時ディレクトリの内容は、アプリケーションの終了時に削除されます。記憶域の空き領域が問題になる場合は、`navigator.fileMgr` APIを使用してこのディレクトリの内容を削除することもできます。

  - `targetWidth`と`targetHeight`を使用するには、これらを両方とも指定する必要があります。一方または両方の値が負の値または0の場合、イメージの元のディメンションが使用されます。

- Android

  - `allowEdit`パラメータを無視します。

  - `Camera.PictureSourceType.PHOTOLIBRARY`と`Camera.PictureSourceType.SAVEDPHOTOALBUM`では、どちらも同じフォト・アルバムが表示されます。

  - `Camera.EncodingType`はサポートされていません。パラメータは無視され、常にJPEGイメージが生成されます。

  - `targetWidth`と`targetHeight`は、個別に指定できます。一方のパラメータが正の値で、もう一方が負の値またはゼロを使用して元のサイズを表す場合、正の値がそのディメンションに使用され、もう一方のディメンションは元のアスペクト比を維持するようにスケール変更されます。

  - `destinationType` `DATA_URL`を使用すると、イメージが大きい場合は使用可能なメモリーがすべて使用され、メモリー不足のエラーが発生する場合があります。通常は、デフォルトのイメージ・サイズを使用すると、この動作が発生します。`targetWidth`と`targetHeight`を設定してイメージ・サイズを制限します。

例14-17は、ユーザーがデバイスのカメラを使用して写真を撮影することを可能にするJavaScriptコードを示しています。結果は、保存済イメージへのフルパスになります。

例14-17 getPictureのJavaScriptコード例

    // The camera, like many other device-specific features, is accessed
    // from the global 'navigator' object in JavaScript.
    // Note that in the Cordova JavaScript APIs, the parameters are passed
    // in as a dictionary, so it is only necessary to provide key-value pairs
    // for the parameters you want to specify.

    navigator.camera.getPicture(onSuccess, onFail, { quality: 50 });

    function onSuccess(imageURI) {
        var image = document.getElementById('myImage');
        image.src = imageURI;
    }

    function onFail(message) {
        alert('Failed because: ' + message);
    }

例14-18は、ユーザーがデバイスのカメラを使用して写真を撮影することを可能にするJavaコードを示しています。結果は、保存済イメージへのフルパスになります。

例14-18 getPictureを使用して写真を撮影するためのJavaコード例

    import oracle.adf.model.datacontrols.device;

    // Access device features in Java code by acquiring an instance of the
    // DeviceManager from the DeviceManagerFactory.
    // Take a picture with the device's camera.
    // The result will be the full path to the saved PNG image.
    String imageFilename = DeviceManagerFactory.getDeviceManager().getPicture(100,
                                   DeviceManager.CAMERA_DESTINATIONTYPE_FILE_URI,
                                   DeviceManager.CAMERA_SOURCETYPE_CAMERA, false,
                                   DeviceManager.CAMERA_ENCODINGTYPE_PNG, 0, 0);

例14-19は、ユーザーが保存済イメージを取得することを可能にするJavaコードを示しています。結果は、Base64でエンコードされたJPEGになります。

例14-19 getPictureを使用してイメージを取得するためのJavaコード例

    import oracle.adf.model.datacontrols.device;

    // Retrieve a previously-saved image. The result will be a base64-encoded JPEG.
    String imageData = DeviceManagerFactory.getDeviceManager().getPicture(100,
      DeviceManager.CAMERA_DESTINATIONTYPE_FILE_URL,
      DeviceManager.CAMERA_SOURCETYPE__PHOTOLIBRARY, false,
      DeviceManager.CAMERA_ENCODINGTYPE_JPEG, 0, 0);

### 14.11.2 SendSMSメソッドを使用してテキスト・メッセージングを可能にする方法

DeviceFeaturesデータ・コントロールに含まれる`sendSMS`メソッドをMAFアプリケーションで使用すると、デバイスのショート・メッセージ・サービス(SMS)テキスト・メッセージング・インタフェースを活用し、エンド・ユーザーがSMSメッセージを送受信できるようにすることが可能です。MAFでは、デバイスのSMSインタフェースを表示し、必要に応じて次のフィールドに事前にデータを移入できます。

- `to:` 受信者をリストします(カンマ区切り)。

- `body:` メッセージの本文を追加します。

SMSテキスト・メッセージング・インタフェースが表示されたら、エンド・ユーザーはSMSを送信するか、それを取り消すことができます。デバイスとキャリアの制限により、SMSは自動的に送信できません。実際にSMSを送信できるのは、エンド・ユーザーのみです。

注意: `sendSMS`メソッドのタイムアウト値は、5分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

注意: Androidでは、エンド・ユーザーがSMSメッセージの編集中にアプリケーションを切り替えて、その後戻すと、SMS編集画面は表示されなくなります。かわりに、そのメッセージは下書きとして保存され、手動で選択して編集を続けることができます。

DeviceFeaturesデータ・コントロールを使用してsendSMS操作をカスタマイズするには:

SMSの送信のためページに対話型フォームを表示するには、`sendSMS`操作を「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールからドラッグし、ページ・デザイナ上にパラメータ・フォームとしてドロップします。その後、「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズできます。実行時に、アプリケーション・ユーザーが前述の様々なフィールドの値を入力できる、編集可能なフォームがページに表示されます。このフォームの下にあるボタンを使用すると、デバイスのSMSインタフェースを表示できます。このインタフェースには、指定したすべてのフィールドに事前に値が移入され、送信準備が整ったSMSが表示されます。

図14-14は、ページ上の編集可能なフォームを使用してSMSを送信するためのバインディングを示しています。

図14-14 デザインタイムで編集可能なフォームを使用してSMSを送信するためのバインディング

例14-20と例14-21は、エンド・ユーザーがデバイスのテキスト・メッセージング・インタフェースを使用してSMSメッセージを送信することを可能にするコード例を示しています。

`sendSMS`メソッドについては、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。

例14-20 sendSMSのJavaScriptコード例

    adf.mf.api.sendSMS({to: "5551234567", body: "This is a test message"});

例14-21 sendSMSのJavaコード例

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

    // Access device features in Java code by acquiring an instance of the
    // DeviceManager from the DeviceManagerFactory.
    // Send an SMS to the phone number "5551234567"
    DeviceManagerFactory.getDeviceManager().sendSMS("5551234567", "This is a test message");

### 14.11.3 sendEmailメソッドを使用して電子メールを可能にする方法

DeviceFeaturesデータ・コントロールに含まれる`sendEmail`メソッドをMAFアプリケーションで使用すると、デバイスの電子メール・メッセージング・インタフェースを活用し、エンド・ユーザーが電子メール・メッセージを送受信できるようにすることが可能です。MAFでは、デバイスの電子メール・インタフェースを表示し、必要に応じて次のフィールドに事前にデータを移入できます。

- `to:` 受信者をリストします(カンマ区切り)。

- `cc:` CC受信者をリストします(カンマ区切り)。

- `subject:` メッセージの件名を追加します。

- `body:` メッセージの本文を追加します。

- `bcc:` BCC受信者をリストします(カンマ区切り)。

- `attachments:` 電子メールに添付するファイルの名前をリストします(カンマ区切り)。

- `mimeTypes:` 添付ファイルに使用するMIMEタイプをリストします(カンマ区切り)。MAFで自動的にMIMEタイプを決定するには、nullを指定します。例14-22と例14-23のように、選択した添付ファイルのMIMEタイプのみを指定することもできます。

デバイスの電子メール・インタフェースが表示されたら、ユーザーは電子メールを送信するか、それを取り消すことができます。デバイスとキャリアの制限により、電子メールは自動的に送信できません。実際に電子メールを送信できるのは、エンド・ユーザーのみです。また、電子メールを送信するには、1つ以上の電子メール・アカウントを構成する必要があります。アカウントが構成されていない場合、電子メール・アカウントが見つからないことを示すエラーが表示されます。

注意: `sendEmail`メソッドのタイムアウト値は、5分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

注意: Androidでは、エンド・ユーザーが電子メールの編集中にアプリケーションを切り替えて、その後元に戻すと、電子メールの編集画面は表示されなくなります。かわりに、そのメッセージは下書きとして保存され、手動で選択して編集を続けることができます。

DeviceFeaturesデータ・コントロールを使用してsendEmail操作をカスタマイズするには:

JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールから`sendEmail`操作をページ・デザイナ上にドラッグし、パラメータ・フォームとしてドロップします。その後、「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズできます。実行時に、アプリケーション・ユーザーが前述の様々なフィールドの値を入力できる、編集可能なフォームがページに表示されます。このフォームの下にあるボタンを使用すると、デバイスの電子メール・インタフェースを表示できます。このインタフェースには、指定したすべてのフィールドに事前に値が移入され、送信準備が整った電子メールが表示されます。

図14-15は、ページ上の編集可能なフォームを使用して電子メールを送信するためのバインディングを示しています。

図14-15 デザインタイムで編集可能なフォームを使用して電子メールを送信するためのバインディング

例14-22と例14-23は、エンド・ユーザーがデバイスの電子メール・インタフェースを使用して電子メール・メッセージを送信することを可能にするコード例を示しています。

`sendEmail`メソッドについては、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。

例14-22 sendEmailのJavaScriptコード例

    // Populate an email to 'ann.li@example.com',
    // copy 'joe.jones@example.com', with the
    // subject 'Test message', and the body 'This is a test message'
    // No BCC recipients or attachments
    adf.mf.api.sendEmail({to: "ann.li@example.com",
                               cc: "joe.jones@example.com",
                               subject: "Test message",
                               body: "This is a test message"});

    // Populate the same email as before, but this time, also BCC
    // 'john.smith@example.com' & 'jane.smith@example.com' and attach two files.
    // By not specifying a value for the mimeTypes parameter, you are telling
    // ADFMobile to automatically determine the MIME type for each of the attachments.
    adf.mf.api.sendEmail({to: "ann.li@example.com",
                               cc: "joe.jones@example.com",
                               subject: "Test message",
                               body: "This is a test message"});
                               bcc: "john.smith@example.com,jane.smith@example.com",
                               attachments: "path/to/file1.txt,path/to/file2.png"});

    // For iOS only: Same as previous email, but this time, explicitly specify
    // all the MIME types.
    adf.mf.api.sendEmail({to: "ann.li@example.com",
                               cc: "joe.jones@example.com",
                               subject: "Test message",
                               body: "This is a test message"});
                               bcc: "john.smith@example.com,jane.smith@example.com",
                               attachments: "path/to/file1.txt,path/to/file2.png"});
                               mimeTypes: "text/plain,image/png"});

    // For iOS only: Same as previous email, but this time, only specify
    // the MIME type for the second attachment and let the system determine
    // the MIME type for the first one.
    adf.mf.api.sendEmail({to: "ann.li@example.com",
                               cc: "joe.jones@example.com",
                               subject: "Test message",
                               body: "This is a test message"});
                               bcc: "john.smith@example.com,jane.smith@example.com",
                               attachments: "path/to/file1.txt,path/to/file2.png"});
                               mimeTypes: ",image/png"});

    // For Android only: Same as previous e-mail, but this time, explicitly specify
    // the MIME type.
    adf.mf.api.sendEmail({to: "ann.li@example.com",
                               cc: "joe.jones@example.com",
                               subject: "Test message",
                               body: "This is a test message"});
                               bcc: "john.smith@example.com,jane.smith@example.com",
                               attachments: "path/to/file1.txt,path/to/file2.png"});
                               mimeTypes: "image/*"});
    // You can also use "plain/text" as the MIME type as it just determines the type
    // of applications to be filtered in the application chooser dialog.

例14-23 sendEmailのJavaコード例

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

    // Access device features in Java code by acquiring an instance of the
    // DeviceManager from the DeviceManagerFactory.
    // Populate an email to 'ann.li@example.com', copy 'joe.jones@example.com', with the
    // subject 'Test message', and the body 'This is a test message'.
    // No BCC recipients or attachments.
    DeviceManagerFactory.getDeviceManager().sendEmail(
                                            "ann.li@example.com",
                                            "joe.jones@example.com",
                                            "Test message",
                                            "This is a test message",
                                            null,
                                            null,
                                            null);

    // Populate the same email as before, but this time, also BCC
    // 'john.smith@example.com' & 'jane.smith@example.com' and attach two files.
    // By specifying null for the mimeTypes parameter, you are telling
    // ADFMobile to automatically determine the MIME type for each of the attachments.
    DeviceManagerFactory.getDeviceManager().sendEmail(
                                            "ann.li@example.com",
                                            "joe.jones@example.com",
                                            "Test message",
                                            "This is a test message",
                                            "john.smith@example.com,jane.smith@example.com",
                                            "path/to/file1.txt,path/to/file2.png",
                                            null);

    // Same as previous email, but this time, explicitly specify all the MIME types.
    DeviceManagerFactory.getDeviceManager().sendEmail(
                                            "ann.li@example.com",
                                            "joe.jones@example.com",
                                            "Test message",
                                            "This is a test message",
                                            "john.smith@example.com,jane.smith@example.com",
                                            "path/to/file1.txt,path/to/file2.png",
                                            "text/plain,image/png");

    // Same as previous email, but this time, only specify the MIME type for the
    // second attachment and let the system determine the MIME type for the first one.
    DeviceManagerFactory.getDeviceManager().sendEmail(
                                            "ann.li@example.com",
                                            "joe.jones@example.com",
                                            "Test message",
                                            "This is a test message",
                                            "john.smith@example.com,jane.smith@example.com",
                                            "path/to/file1.txt,path/to/file2.png",
                                            ",image/png");

### 14.11.4 createContactメソッドを使用してコンタクト先の作成を可能にする方法

DeviceFeaturesデータ・コントロールに含まれる`createContact`メソッドをMAFアプリケーションで使用すると、コンタクト先を管理するためのデバイスのインタフェースとファイル・システムを活用し、エンド・ユーザーが新しいコンタクト先を作成してデバイスのアドレス帳に保存できるようにすることが可能です。MAFでは、デバイスのインタフェースを表示し、必要に応じて`Contact`のフィールドに事前にデータを移入できます。`例14-25`のように、`createContact`メソッドは`Contact`オブジェクトをパラメータとして使用し、作成されたContactオブジェクトを戻します。

`createContact`メソッドと`Contact`オブジェクトの詳細は、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。Contactのプロパティについては、`第14.11.5項「findContactsメソッドを使用してコンタクト先の検索を可能にする方法」`も参照してください。

注意: `createContact`メソッドのタイムアウト値は、1分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

注意: nullの`Contact`オブジェクトがメソッドに渡された場合、例外がスローされます。

DeviceFeaturesデータ・コントロールを使用してcreateContact操作をカスタマイズするには:

1. JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールから`createContact`操作をドラッグし、リンクまたはボタンとしてページ・デザイナ上にドロップします。

   リンクまたはボタン: 「アクション・バインディングの編集」ダイアログが表示され、`createContact`操作に対する`Contact`オブジェクトのパラメータを入力するよう求められます。このパラメータは、Java Beanクラスから`Contact`を戻すために使用されるマネージドBeanのプロパティを参照するEL式である必要があります。マネージドBeanに`Contact`オブジェクトのゲッターがすでに含まれている場合は、EL式ビルダーを使用して、パラメータの値を設定できます。実行時にページに表示されるボタンまたはリンクをクリックすると、入力した値を使用して`createContact`操作を実行できます。例14-24は、`Contact`オブジェクトを作成するためのマネージドBeanコードの例を示しています。

2. 戻された`Contact`オブジェクトを「データ・コントロール」パネルにある`createContact`操作の下からドラッグし、フォームとしてページ上にドロップすることもできます。その後、「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズできます。`createContact`操作を実行すると、結果がこのフォームに表示されます。

例14-24 Contactオブジェクトを作成するためのマネージドBeanコード

    private Contact contactToBeCreated;

    public void setContactToBeCreated(Contact contactToBeCreated) {
       this.contactToBeCreated = contactToBeCreated;
    }

    public Contact getContactToBeCreated() {
       String givenName = "Mary";
       String familyName = "Jones";
       String note = "Just a Note";
       String phoneNumberType = "mobile";
       String phoneNumberValue = "650-555-0111";
       String phoneNumberNewValue = "650-555-0199";
       String emailType = "home";
       String emailTypeNew = "work";
       String emailValue = "Mary.Jones@example.com";
       String addressType = "home";
       String addressStreet = "500 Barnacle Pkwy";
       String addressLocality = "Redwood Shores";
       String addressCountry = "USA";
       String addressPostalCode = "94065";
       ContactField[] phoneNumbers = null;
       ContactField[] emails = null;
       ContactAddresses[] addresses = null;

       /*
        * Create contact
        */
       this.contactToBeCreated = new Contact();

       ContactName name = new ContactName();
       name.setFamilyName(familyName);
       name.setGivenName(givenName);
       this.contactToBeCreated.setName(name);

       ContactField phoneNumber = new ContactField();
       phoneNumber.setType(phoneNumberType);
       phoneNumber.setValue(phoneNumberValue);

       phoneNumbers = new ContactField[] { phoneNumber };

       ContactField email = new ContactField();
       email.setType(emailType);
       email.setValue(emailValue);

       emails = new ContactField[] { email };

       ContactAddresses address = new ContactAddresses();
       address.setType(addressType);
       address.setStreetAddress(addressStreet);
       address.setLocality(addressLocality);
       address.setCountry(addressCountry);

       addresses = new ContactAddresses[] { address };

       this.contactToBeCreated.setNote(note);
       this.contactToBeCreated.setPhoneNumbers(phoneNumbers);
       this.contactToBeCreated.setEmails(emails);
       this.contactToBeCreated.setAddresses(addresses);

       return this.contactToBeCreated;
    }

例14-25と例14-26は、エンド・ユーザーがデバイスにコンタクト先を作成することを可能にするコード例を示しています。

例14-25 createContactのJavaScriptコード例

    // Contacts, like many other device-specific features,
    // are accessed from the global 'navigator' object in JavaScript
    var contact = navigator.contacts.create();

    var name = new ContactName();
    name.givenName = "Mary";
    name.familyName = "Jones";

    contact.name = name;

    // Store contact phone numbers in ContactField[]
    var phoneNumbers = [1];
    phoneNumbers[0] = new ContactField('home', '650-555-0123', true);

    contact.phoneNumbers = phoneNumbers;

    // Store contact email addresses in ContactField[]
    var emails = [1];
    emails[0] = new ContactField('work', 'Mary.Jones@example.com');

    contact.emails = emails;

    // Save
    contact.save(onSuccess, onFailure);

    function onSuccess()
    {
      alert("Create Contact successful.");
    }

    function onFailure(Error)
    {
      alert("Create Contact failed: " + Error.code);
    }

例14-26 createContactのJavaコード例

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

    import oracle.adf.model.datacontrols.device.ContactAddresses;
    import oracle.adf.model.datacontrols.device.ContactField;
    import oracle.adf.model.datacontrols.device.ContactName;

    String givenName = "Mary";
    String familyName = "Jones";
    String note = "Just a Note";
    String phoneNumberType = "mobile";
    String phoneNumberValue = "650-555-0111";
    String phoneNumberNewValue = "650-555-0199";
    String emailType = "home";
    String emailTypeNew = "work";
    String emailValue = "Mary.Jones@example.com";
    String addressType = "home";
    String addressStreet = "500 Barnacle Pkwy";
    String addressLocality = "Redwood Shores";
    String addressCountry = "USA";
    String addressPostalCode = "91234";
    ContactField[] phoneNumbers = null;
    ContactField[] emails = null;
    ContactAddresses[] addresses = null;
    ContactField[] emails = null;

    /*
    * Create contact
    */
    Contact aContact = new Contact();

    ContactName name = new ContactName();
    name.setFamilyName(familyName);
    name.setGivenName(givenName);
    aContact.setName(name);

    ContactField phoneNumber = new ContactField();
    phoneNumber.setType(phoneNumberType);
    phoneNumber.setValue(phoneNumberValue);

    phoneNumbers = new ContactField[] { phoneNumber };

    ContactField email = new ContactField();
    email.setType(emailType);
    email.setValue(emailValue);

    emails = new ContactField[] { email };

    ContactAddresses address = new ContactAddresses();
    address.setType(addressType);
    address.setStreetAddress(addressStreet);
    address.setLocality(addressLocality);
    address.setCountry(addressCountry);

    addresses = new ContactAddresses[] { address };

    aContact.setNote(note);
    aContact.setPhoneNumbers(phoneNumbers);
    aContact.setEmails(emails);
    aContact.setAddresses(addresses);

    // Access device features in Java code by acquiring an instance of the
    // DeviceManager from the DeviceManagerFactory.
    // Invoking the createContact method, using the DeviceDataControl object.
    Contact createdContact = DeviceManagerFactory.getDeviceManager()
        .findContacts.createContact(aContact);

### 14.11.5 findContactsメソッドを使用してコンタクト先の検索を可能にする方法

DeviceFeaturesデータ・コントロールに含まれる`findContacts`メソッドをMAFアプリケーションで使用すると、コンタクト先を管理するためのデバイスのインタフェースとファイル・システムを活用し、エンド・ユーザーがデバイスのアドレス帳から1つ以上のコンタクト先を検索できるようにすることが可能です。MAFでは、デバイスのインタフェースを表示し、必要に応じて`findContacts`のフィールドに事前にデータを移入できます。`findContacts`メソッドでは、フィルタ文字列と検索する(そして、見つかったコンタクト先の一部として戻す)フィールド名のリストを使用します。フィルタ文字列には、コンタクト先内の検索する任意の項目を指定できます。`findContacts`メソッドの詳細は、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。

`findContacts`操作では、次の引数を使用します。

- `contactFields:` 必須パラメータ。このパラメータは、`findContacts`操作によって生成される`Contact`オブジェクトに含めるフィールドを指定するために使用します。フィールドはカンマで区切ります(スペースの有無は関係ありません)。

- `filter:` コンタクト先をフィルタするために使用する検索文字列。(文字列) (デフォルト: `""`)

- `multiple:` `findContacts`操作から複数のコンタクト先を戻すかどうかを決定します。(ブール) (デフォルト: `false`)

注意: 次のリストに含まれていないフィールド名を渡すと、`findContacts`操作の戻り値が`null`になる場合があります。また、`Contact`のフィールドの引数に指定したフィールドのみが、`Contact`オブジェクトの一部として戻されます。

次のリストに示す`Contact`のプロパティを渡して、検索に使用し、見つかったコンタクト先の一部として戻すことができます。

- `id:` グローバルに一意な識別子

- `displayName:` エンド・ユーザーへの表示に適した、このコンタクト先の名前

- `name:` 人物名のすべてのコンポーネントを含むオブジェクト

- `nickname:` コンタクト先のカジュアルな名前。このフィールドをnullに設定した場合は、空の文字列として保存されます。

- `phoneNumbers:` コンタクト先のすべての電話番号の配列

- `emails:` コンタクト先のすべての電子メール・アドレスの配列

- `addresses:` コンタクト先のすべての住所の配列

- `ims:` すべてのコンタクト先のインスタント・メッセージング(IM)アドレスの配列(`ims`プロパティは、このリリースではサポートされていません。)

  注意: MAFのこのリリースでは、`Contact`プロパティの`ims`はサポートされていません。`ims`プロパティを使用してコンタクト先を作成すると、MAFでは`ims`プロパティなしでコンタクト先が保存されます。そのため、ユーザーが`ims`に基づいて検索を実行しようとした場合は、そのコンタクト先を検索できません。また、ユーザーが検索フィールドに`ims`を入力しようとすると、`ims`は`null`として戻されます。

- `organizations:` コンタクト先のすべての組織の配列

- `birthday:` コンタクト先の誕生日。プログラムでコンタクト先の誕生日フィールドを設定したり、アドレス帳に永続化することはできませんが、オペレーティング・システムのアドレス帳アプリケーションを使用してこのフィールドを手動で設定できます。

- `note:` コンタクト先に関するメモ。このフィールドをnullに設定した場合は、空の文字列として保存されます。

- `photos:` コンタクト先の写真の配列

- `categories:` すべてのコンタクト先のユーザー定義カテゴリの配列

- `urls:` コンタクト先に関連付けられたWebページの配列

注意: `findContacts`メソッドのタイムアウト値は、1分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

DeviceFeaturesデータ・コントロールを使用してfindContacts操作をカスタマイズするには:

1. JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールから`findContacts`操作をドラッグし、リンク、ボタンまたはパラメータ・フォームとしてページ・デザイナ上にドロップします。

   リンクまたはボタン: 「アクション・バインディングの編集」ダイアログが表示され、`findContacts`操作の引数の値を入力するよう求められます。実行時にページに表示されるボタンまたはリンクをクリックすると、入力した値を使用して`findContacts`操作を実行できます。

   パラメータ・フォーム: 「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズします。実行時に、アプリケーション・ユーザーが前述のContactの様々なフィールドの値を入力できる、編集可能なフォームがページに表示されます。このフォームの下にあるボタンをクリックすると、入力した値を使用して`findContacts`操作を実行できます。

2. 戻された`Contact`オブジェクトを「データ・コントロール」パネルにある`findContacts`操作の下からドラッグし、フォームとしてページ上にドロップすることもできます。その後、「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズできます。`findContacts`操作を実行すると、結果がこのフォームに表示されます。

例14-27は、`findContacts`メソッドに指定できる引数値を示しています。例14-28と例14-29は、姓でコンタクト先を検索し、コンタクト先の名前、電話番号、電子メール、住所およびメモを取得する方法を示しています。

例14-27 findContactsに指定できる引数値

    // This will return just one contact with only the ID field:
    Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts("", "", false);

    // This will return all contacts with only ID fields:
    Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts("", "", true);

    // This will return just one contact with all fields:
    Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts("*", "", false);

    // This will return all contacts with all fields:
    Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts("*", "", true);

    // These will throw an exception as contactFields is a required argument and cannot be null:
    DeviceManagerFactory.getDeviceManager().findContacts(null, "", false);
    DeviceManagerFactory.getDeviceManager().findContacts(null, "", true);

    // These will throw an exception as the filter argument cannot be null:
    DeviceManagerFactory.getDeviceManager().findContacts("", null, false);
    DeviceManagerFactory.getDeviceManager().findContacts("", null, true);

注意: 渡される`Contact`のフィールドは、文字列(カンマで区切られたフィールドを含む)です。いずれかの引数が`null`としてメソッドに渡された場合、例外がスローされます。

例14-28 findContactsのJavaScriptコード例

    var filter = ["name", "phoneNumbers", "emails", "addresses", "note"];

    var options = new ContactFindOptions();
    options.filter="FamilyName";

    // Contacts, like many other device-specific features, are accessed from
    // the global 'navigator' object in JavaScript.
    navigator.contacts.find(filter, onSuccess, onFail, options);

    function onSuccess(contacts)
    {
      alert ("Find Contact call succeeded! Number of contacts found = " + contacts.length);
    }

    function onFail(Error)
    {
      alert("Find Contact failed: " + Error.code);
    }

例14-29 findContactsのJavaコード例

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

    /*
     * Find Contact - Find contact by family name.
     *
     * See if we can find the contact that we just created.
     */

    String familyName = "FamilyName"

    // Access device features in Java code by acquiring an instance of the
    // DeviceManager from the DeviceManagerFactory.
    Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts(
        "name,phoneNumbers,emails,addresses,note", familyName, true);

### 14.11.6 updateContactメソッドを使用してコンタクト先の更新を可能にする方法

DeviceFeaturesデータ・コントロールに含まれる`updateContact`メソッドをMAFアプリケーションで使用すると、コンタクト先を管理するためにデバイスのインタフェースとファイル・システムを活用し、エンド・ユーザーがデバイスのアドレス帳に含まれるコンタクト先を更新できるようにすることが可能です。MAFでは、デバイスのインタフェースを表示し、必要に応じて`updateContact`のフィールドに事前にデータを移入できます。`例14-30`のように、`updateContact`メソッドは`Contact`オブジェクトをパラメータとして使用し、更新されたContactオブジェクトを戻します。

`updateContact`メソッドと`Contact`オブジェクトの詳細は、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。Contactのプロパティについては、`第14.11.5項「findContactsメソッドを使用してコンタクト先の検索を可能にする方法」`も参照してください。

注意: 入力パラメータとして必要な`Contact`オブジェクトは、`第14.11.5項「findContactsメソッドを使用してコンタクト先の検索を可能にする方法」`の説明に従ってfindContactsメソッドを使用することによって検索できます。nullの`Contact`オブジェクトがメソッドに渡された場合、例外がスローされます。

DeviceFeaturesデータ・コントロールを使用してupdateContact操作をカスタマイズするには:

1. JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールからupdateContact操作をドラッグし、リンクまたはボタンとしてページ・デザイナ上にドロップします。

   リンクまたはボタン: 「アクション・バインディングの編集」ダイアログが表示され、`updateContact`操作に対する`Contact`オブジェクトのパラメータを入力するよう求められます。このパラメータは、Java Beanクラスから`Contact`を戻すために使用されるマネージドBeanのプロパティを参照するEL式である必要があります。マネージドBeanに`Contact`オブジェクトのゲッターがすでに含まれている場合は、EL式ビルダーを使用して、パラメータの値を設定できます。実行時にページに表示されるボタンまたはリンクをクリックすると、入力した値を使用して`updateContact`操作を実行できます。例14-24は、`Contact`オブジェクトを作成するためのマネージドBeanコードの例を示しています。

2. 戻されたContactオブジェクトを「データ・コントロール」パネルにある`updateContact`操作の下からドラッグし、フォームとしてページ上にドロップすることもできます。その後、「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズできます。`updateContact`操作を実行すると、結果がこのフォームに表示されます。

例14-30と例14-32は、コンタクト先の電話番号を更新する方法を示しています。例14-31と例14-33は、コンタクト先に別の電話番号を追加する方法を示しています。

例14-30 updateContactのJavaScriptコード例

    function updateContact(contact)
    {
      try
      {
        if (null != contact.phoneNumbers)
        {
          alert("Number of phone numbers = " + contact.phoneNumbers.length);
          var numPhoneNumbers = contact.phoneNumbers.length;
          for (var j = 0; j < numPhoneNumbers; j++)
          {
            alert("Type: " + contact.phoneNumbers[j].type + "\n" +
                  "Value: "  + contact.phoneNumbers[j].value + "\n" +
                  "Preferred: "  + contact.phoneNumbers[j].pref);

            contact.phoneNumbers[j].type = "mobile";
            contact.phoneNumbers[j].value = "408-555-0100";
          }

          // save
          contact.save(onSuccess, onFailure);
        }
        else
        {
          //alert ("No phone numbers found in the contact.");
        }
      }
      catch(e)
      {
        alert("updateContact - ERROR: " + e.description);
      }
    }

    function onSuccess()
    {
      alert("Update Contact successful.");
    }

    function onFailure(Error)
    {
      alert("Update Contact failed: " + Error.code);
    }

例14-31は、既存の電話番号に別の電話番号を追加する方法を示しています。

例14-31 updateContactを使用して電話番号を追加するためのJavaScriptコード例

    function updateContact(contact)
    {
      try
      {
        var phoneNumbers = [1];
        phoneNumbers[0] = new ContactField('home', '650-555-0123', true);
        contact.phoneNumbers = phoneNumbers;

        // save
        contact.save(onSuccess, onFailure);
      }
      catch(e)
      {
        alert("updateContact - ERROR: " + e.description);
      }
    }

    function onSuccess()
    {
      alert("Update Contact successful.");
    }

    function onFailure(Error)
    {
      alert("Update Contact failed: " + Error.code);
    }

例14-32は、コンタクト先の電話番号、電子メール・タイプおよび郵便番号を更新する方法を示しています。

例14-32 updateContactのJavaコード例

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

    /*
     * Update Contact - Updating phone number, email type, and adding address postal code
     */
    String familyName = "FamilyName";
    String phoneNumberNewValue = "650-555-0123";
    String emailTypeNew = "work";
    String addressPostalCode = "91234";

    Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts(
        "name,phoneNumbers,emails,addresses,note", familyName, true);

    // Assuming there was only one contact returned, we can use the first contact in the array.
    // If more than one contact is returned then we have to filter more to find the exact contact
    // we need to update.

    foundContacts[0].getPhoneNumbers()[0].setValue(phoneNumberNewValue);
    foundContacts[0].getEmails()[0].setType(emailTypeNew);
    foundContacts[0].getAddresses()[0].setPostalCode(addressPostalCode);

    Contact updatedContact = DeviceManagerFactory.getDeviceManager().updateContact(foundContacts[0]);

例14-33は、既存の電話番号に別の電話番号を追加する方法を示しています。

例14-33 updateContactを使用して電話番号を追加するためのJavaコード例

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

    String additionalPhoneNumberValue = "408-555-0123";
    String additionalPhoneNumberType = "mobile";
    // Create a new phoneNumber that will be appended to the previous one.
    ContactField additionalPhoneNumber = new ContactField();
    additionalPhoneNumber.setType(additionalPhoneNumberType);
    additionalPhoneNumber.setValue(additionalPhoneNumberValue);

    foundContacts[0].setPhoneNumbers(new ContactField[] { additionalPhoneNumber });

    // Access device features in Java code by acquiring an instance of the DeviceManager
    // from the DeviceManagerFactory.
    Contact updatedContact = DeviceManagerFactory.getDeviceManager().updateContact(foundContacts[0]);

注意: `updateContact`メソッドのタイムアウト値は、1分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

### 14.11.7 removeContactメソッドを使用してコンタクト先の削除を可能にする方法

DeviceFeaturesデータ・コントロールに含まれる`removeContact`メソッドをMAFアプリケーションで使用すると、コンタクト先を管理するためのデバイスのインタフェースとファイル・システムを活用し、エンド・ユーザーがデバイスのアドレス帳からコンタクト先を削除できるようにすることが可能です。MAFでは、デバイスのインタフェースを表示し、必要に応じて`removeContact`のフィールドに事前にデータを移入できます。`例14-34`のように、`removeContact`メソッドはContactオブジェクトをパラメータとして使用します。

注意: 入力パラメータとして必要な`Contact`オブジェクトは、`第14.11.5項「findContactsメソッドを使用してコンタクト先の検索を可能にする方法」`の説明に従ってfindContactsメソッドを使用することによって検索できます。

DeviceFeaturesデータ・コントロールを使用してremoveContact操作をカスタマイズするには:

1. JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールからremoveContact操作をドラッグし、リンク、ボタンまたはパラメータ・フォームとしてページ・デザイナ上にドロップします。

   リンクまたはボタン: 「アクション・バインディングの編集」ダイアログが表示され、`removeContact`操作の引数の値を入力するよう求められます。実行時にページに表示されるボタンまたはリンクをクリックすると、入力した値を使用して`removeContact`操作を実行できます。

   パラメータ・フォーム: 「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズします。実行時に、アプリケーション・ユーザーが様々なContactのフィールドの値を入力できる、編集可能なフォームがページに表示されます。このフォームの下にあるボタンをクリックすると、入力した値を使用して`removeContact`操作を実行できます。

2. 戻された`Contact`オブジェクトを「データ・コントロール」パネルにある`removeContact`操作の下からドラッグし、フォームとしてページ上にドロップすることもできます。その後、「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズできます。`removeContact`操作を実行すると、結果がこのフォームに表示されます。

例14-34と例14-35は、`findContacts`を使用して検索したコンタクト先を削除する方法を示しています。`removeContact`メソッドとContactオブジェクトについては、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。

注意: Androidでは、`removeContact`操作を実行しても完全にはコンタクト先が削除されません。`removeContact`メソッドをコールしてコンタクト先を削除すると、「(不明)」の付いた名前がアプリケーション内のコンタクト先リストに表示されます。

例14-34 removeContactのJavaScriptコード例

    // Remove the contact from the device
    contact.remove(onSuccess,onError);

    function onSuccess()
    {
      alert("Removal Success");
    }

    function onError(contactError)'
    {
      alert("Error = " + contactError.code);
    }

例14-35 removeContactのJavaコード例

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

    /*
     * Remove the contact from the device
     */
    Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts(
        "name,phoneNumbers,emails,addresses", familyName, true);

    // Assuming there is only one contact returned, we can use the first contact in the array.
    // If more than one contact is returned we will have to filter more to find the
    // exact contact we want to remove.

    // Access device features in Java code by acquiring an instance of the DeviceManager
    // from the DeviceManagerFactory.
    DeviceManagerFactory.getDeviceManager().removeContact(foundContacts[0]);

注意: `removeContact`メソッドのタイムアウト値は、1分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

### 14.11.8 startLocationMonitorメソッドを使用して地理的位置を有効にする方法

DeviceFeaturesデータ・コントロールに含まれる`startLocationMonitor`メソッドをMAFアプリケーションで使用すると、デバイスの地理的位置サービスを活用して、デバイスの場所を取得および追跡することが可能です。MAFでは、デバイスのインタフェースを表示し、必要に応じて`startLocationMonitor`のフィールドに事前にデータを移入できます。

MAFで公開されているAPIを使用すると、デバイスの現在の位置を取得できるため、特定の時点におけるデバイスの現在の位置を取得したり、定期的にその情報をサブスクライブできます。例14-36と例14-37は、アプリケーションでデバイスの位置の取得を可能にするコード例を示しています。`startLocationMonitor`メソッドについては、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。

注意: Android 2.nシミュレータは、`enableHighAccuracy`オプションが`true`に設定されていなければ、地理的位置の結果を返しません。 `altitudeAccuracy`プロパティは、Androidデバイスでサポートされていません。 Androidプラットフォームでは、iOSほど頻繁に更新が行われません。

DeviceFeaturesデータ・コントロールを使用してデバイスの位置の変更をリスニングするには:

JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールから`startLocationMonitor`操作をページ・デザイナ上にドラッグし、リンクまたはボタンとしてドロップします。アクションの編集ダイアログによって要求されたら、次のようにフィールドに値を移入します。

- `enableHighAccuracy:` `true`の場合、位置の修正を最も正確に取得できるメソッドを使用します。これは単なるヒントであり、オペレーティング・システムでは考慮されない場合があります。多くの場合、デバイスには、携帯電話基地局の三角測量、Wi-Fiホットスポットの参照、実際のGPSなど、位置の修正を取得するためのいくつかの異なるメカニズムがあります。`false`を指定すると、正確性の低い位置を受け入れることになりますが、この場合、レスポンスの速度が向上し、消費電力が低下する可能性があります。

- `updateInterval:` 更新の受信頻度をミリ秒単位で定義します。位置の更新は、指定した頻度では配信されない場合があります。オペレーティング・システムでは、デバイスの位置の大きな変更が検出されるまで待機してから、別の位置の更新をトリガーする場合があります。

- `locationListener:` 次のシグネチャを含むBeanメソッドに解決されるEL式。

      void methodName(Location newLocation)

  このEL式は、位置の更新を受信するたびに評価されます。たとえば、`viewScope.LocationListenerBean.locationUpdated`(`#{}`で囲みません)と入力した後、`LocationListenerBean`というBeanを`viewScope`内に定義し、次のシグネチャを含むメソッドを実装します。

      public void locationUpdated(Location currentLocation) {
        System.out.println(currentLocation);
        // To stop subscribing to location updates, invoke the following:
        // DeviceManagerFactory.getDeviceManager().clearWatchPosition(
        //     currentLocation.getWatchId());
      }

  注意: EL式の評価結果として`locationListener`の名前が必要でないかぎり、`locationListener`を指定するためにEL構文`#{LocationListenerBean.locationUpdate}`を使用しないでください。

例14-36は、デバイスの位置の変更を定期的にサブスクライブする方法を示しています。この例で使用する`DeviceManager.startUpdatingPosition`メソッドのパラメータは、次のとおりです。

- `int updateInterval:` 位置の更新の配信頻度をミリ秒単位で定義します。位置の更新は、指定した頻度では配信されない場合があります。オペレーティング・システムでは、デバイスの位置の大きな変更が検出されるまで待機してから、別の位置の更新をトリガーする場合があります。逆に言えば、位置の更新は指定した頻度で配信されることもありますが、デバイスの位置が大きく変更されるまで、配信される位置が変化しない場合があります。

- `boolean enableHighAccuracy`: `true`に設定された場合、位置の修正を最も正確に取得できるメソッドを使用します。

- `String watchID:` 後から位置の更新のサブスクライブを停止するために使用できる一意のIDを定義します。

- `GeolocationCallback:` GeolocationCallbackインタフェースの実装。`例14-36`のように、この実装のlocationUpdatedメソッドは、位置が更新されるたびに起動されます。

JavaScriptを使用してデバイスの位置の変更をサブスクライブする方法の例は、Cordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。

`locationListener`によって指定されたコールバック関数内に戻されるパラメータは、次のとおりです。

- `double getAccuracy`: m単位の緯度と経度を示す座標の精度レベル。

- `double getAltitude`: m単位の楕円体高度。

- `double getLatitude`: 10進度単位の緯度。

- `double getLongitude`: 10進度単位の経度。

- `double getAltitudeAccuracy`: m単位の高度座標の精度レベル。

- `double getHeading`: 真北から時計回りに計測した度単位で示される進行方向

- `double getSpeed`: m/秒単位で指定される、デバイスの現在の対地速度

- `long getTimestamp`: Unixエポックからのミリ秒単位の経過時間で示されるタイムスタンプの作成

- `String getWatchId`: 定期的な位置の更新をサブスクライブするときのみ使用します。後から位置の更新のサブスクライブを停止するために使用できる一意のID

`startLocationMonitor`メソッドと`startHeadingMonitor`メソッドの詳細は、MAF Javadocの`DeviceDataControl`クラスおよびCordovaのドキュメント(`http://cordova.apache.org/`)を参照してください。

注意: `startLocationMonitor`メソッドと`startHeadingMonitor`メソッドのタイムアウト値は、1分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

例14-36 地理的位置を使用したデバイスの位置の変更のサブスクライブ

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;
    import oracle.adf.model.datacontrols.device.GeolocationCallback;
    import oracle.adf.model.datacontrols.device.Location;

    // Subscribe to location updates that will be delivered every 20 seconds, with high accuracy.
    // As you can have multiple subscribers, let's identify this one as 'MyGPSSubscriptionID'.
    // Notice that this call returns the watchID, which is usually the same as the watchID passed in.
    // However, it may be different if the specified watchID conflicts with an existing watchID,
    // so be sure to always use the returned watchID.
    String watchID = DeviceManagerFactory.getDeviceManager().startUpdatingPosition(20000, true, "
           "MyGPSSubscriptionID", new GeolocationCallback() {
        public void locationUpdated(Location position) {
            System.out.println("Location updated to: " + position);
        }
    });

    // The previous call returns immediately so that you can continue processing.
    // When the device's location changes, the locationUpdated() method specified in
    // the previous call will be invoked in the context of the current feature.

    // When you wish to stop being notified of location changes, call the following method:
    DeviceManagerFactory().getDeviceManager().clearWatchPosition(watchID);

DeviceFeaturesデータ・コントロールを使用してデバイスの位置を取得するには:

JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールからstartLocationMonitor操作をページ・デザイナ上にドラッグし、リンクまたはボタンとしてドロップします。例14-36に従いますが、最初の位置の変更を受信した後、リスニングを停止します。

例14-37は、デバイスの位置を1回取得する方法を示しています。この例で使用する`DeviceManager.getCurrentPosition`メソッドのパラメータは、次のとおりです。

- `int maximumAge:` ミリ秒単位で指定したこの値より古くないキャッシュ値を受け入れます。位置の修正をこの時間内に取得した場合、その情報がすぐに戻されますが、それ以外の場合、コールは新しい位置の修正が確定するまでブロックされます。`maximumAge`パラメータの値は、1000ミリ秒以上である必要があります。これより小さい値は、自動的に1000ミリ秒に設定されます。

- `boolean: enableHighAccuracy` trueに設定された場合、位置の修正を最も正確に取得できるメソッドを使用します。

例14-37 地理的位置を使用したデバイスの現在位置の取得(1回)

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;
    import oracle.adf.model.datacontrols.device.Location;

    // Get the device's current position, with highest accuracy, and accept a cached location that is
    // no older than 60 seconds.
    Location currentPosition = DeviceManagerFactory.getDeviceManager().getCurrentPosition(60000, true);
    System.out.println("The device's current location is: latitude=" + currentPosition.getLatitude() +
        ", longitude=" + currentPosition.getLongitude());

### 14.11.9 displayFileメソッドを使用してファイルの表示を可能にする方法

DeviceFeaturesデータ・コントロールに含まれる`displayFile`メソッドを使用すると、MAFアプリケーションでデバイスのローカル・ファイルを表示できます。プラットフォームに応じて、アプリケーション・ユーザーはPDF、イメージ・ファイル、Microsoft Officeドキュメントおよびその他の様々なファイル・タイプを表示できます。iOSでは、アプリケーション・ユーザーは、サポートされているファイルをMAFアプリケーション内でプレビューできます。またユーザーは、それらのファイルをサードパーティ・アプリケーションで開く、電子メールで送信する、プリンタに送信するなどの操作を行うこともできます。Androidでは、すべてのファイルがサードパーティ・アプリケーション内で開かれます。つまり、アプリケーション・ユーザーは、ファイルを表示しながらMAFアプリケーションから離れることができます。ユーザーは、Androidの戻るボタンを押すことによって、MAFアプリケーションに戻ることができます。所定のファイルを開くことができるアプリケーションがデバイス内に存在しない場合は、エラーが表示されます。`displayFile`メソッドを使用してiOSデバイスとAndroidデバイスの両方でファイルを開く方法の例は、DeviceDemoサンプル・アプリケーションを参照してください。このアプリケーションは、開発コンピュータのJDeveloperインストール・ディレクトリ内で次の場所にある`PublicSamples.zip`ファイルから入手できます。

    jdev_install/jdeveloper/jdev/extensions/oracle.maf/Samples

`displayFile`メソッドの機能は、デバイスのローカル・ファイルを表示することのみです。つまり、リモート・ファイルは最初にダウンロードする必要があります。ダウンロードされたファイルの格納先であるディレクトリ・ルートに戻るには、`AdfmfJavaUtilities.getDirectoryPathRoot`(`AdfmfJavaUtilities.``DownloadDirectory`)をコールします。iOSでは、この場所はアプリケーションによって異なりますが、Androidでは、この場所として外部ストレージ・ディレクトリが参照されます。この外部ストレージ・ディレクトリは公的にアクセス可能であるため、サードパーティ・アプリケーションを使用して、そこに格納されているファイルを読み取ることができます。

表14-7 サポートされるファイル・タイプ

iOS | Android
--- | ---
サポートされるファイル・タイプの詳細は、Apple iOS開発サイト(`http://developer.apple.com/library/ios/navigation/`)にある、Quick Lookプレビュー・コントローラに関するドキュメントを参照してください。 | 特定のMIMEタイプがデバイスにインストールされている場合、このフレームワークによって、そのタイプに関連付けられたビューアが起動されます。特定のファイル・タイプを表示するためのフレームワークは組み込まれていません。そのファイル・タイプを開くことができるアプリケーションがデバイスにインストールされていない場合は、MAFアプリケーションによってエラーが表示されます。
iWorkドキュメント |
Microsoft Officeドキュメント(Office 97以降) |
リッチ・テキスト・フォーマット(RTF)ドキュメント |
PDFファイル |
イメージ |
Uniform Type Identifier (UTI)がpublic.text typeに準拠しているテキスト・ファイル |
カンマ区切り(csv)ファイル |

DeviceFeaturesデータ・コントロールを使用してdisplayFile操作をカスタマイズするには:

1. JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールからdisplayFile操作をドラッグし、リンク、ボタンまたはパラメータ・フォームとしてページ・デザイナ上にドロップします。

   リンクまたはボタン: 「アクション・バインディングの編集」ダイアログが表示され、`displayFile`操作の引数の値を入力するよう求められます。実行時にページに表示されるボタンまたはリンクをクリックすると、入力した値を使用して`displayFile`操作を実行できます。

   パラメータ・フォーム: 「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズします。実行時に、アプリケーション・ユーザーが様々なフィールドの値を入力できる、編集可能なフォームがページに表示されます。このフォームの下にあるボタンをクリックすると、入力した値を使用して`displayFile`操作を実行できます。

例14-38は、`displayFile`メソッドを使用してファイルを表示する方法を示しています。`displayFile`メソッドの詳細は、MAF Javadocの`DeviceDataControl`クラスを参照してください。

例14-38 displayFileのJavaコード例

    import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

     URL remoteFileUrl;
            InputStream is;
            BufferedOutputStream fos;
            try {

                // Open connection to remote file; fileUrl here is a String containing the URL to the remote file.
                remoteFileUrl = new URL(fileUrl);
                URLConnection connection = remoteFileUrl.openConnection();
                is = new BufferedInputStream(connection.getInputStream());
                // Saving the file locally as 'previewTempFile.<extension>'
                String fileExt = fileUrl.substring(fileUrl.lastIndexOf('.'), fileUrl.length());
                String tempFile = "/previewTempFile" + fileExt;
                File localFile = null;
                // Save the file in the DownloadDirectory location
                localFile = new File(AdfmfJavaUtilities.getDirectoryPathRoot(AdfmfJavaUtilities.DownloadDirectory) + tempFile);
                if (localFile.exists()) {
                    localFile.delete();
                }
                // Use buffered streams to download the file.
                fos = new BufferedOutputStream(new FileOutputStream(localFile));
                byte[] data = new byte[1024];
                int read = 0;
                while ((read = is.read(data)) != -1) {
                    fos.write(data, 0, read);
                }
                is.close();
                fos.close();

                // displayFile takes a URL string which has to be encoded on iOS.
                // iOS does not handle "+" as an encoding for space (" ") but
                // expects "%20" instead.  Also, the leading slash MUST NOT be
                // encoded to "%2F".  We will revert it to a slash after the
                // URLEncoder converts it to "%2F".
                StringBuffer buffer = new StringBuffer();
                String path = URLEncoder.encode(localFile.getPath(), "UTF-8");
                // replace "+" with "%20"
                String replacedString = "+";
                String replacement = "%20";
                int index = 0, previousIndex = 0;
                index = path.indexOf(replacedString, index);
                while (index != -1) {
                    buffer.append(path.substring(previousIndex, index)).append(replacement);
                    previousIndex = index + 1;
                    index = path.indexOf(replacedString, index + replacedString.length());
                }
                buffer.append(path.substring(previousIndex, path.length()));
                // Revert the leading encoded slash ("%2F") to a literal slash ("/").
                if (buffer.indexOf("%2F") == 0) {
                    buffer.replace(0, 3, "/");
                }

                // Create URL and invoke displayFile with its String representation.
                URL localURL = null;
                if (Utility.getOSFamily() == Utility.OSFAMILY_ANDROID) {
                    localURL = new URL("file", "localhost", localFile.getAbsolutePath());
                }
                else if (Utility.getOSFamily() == Utility.OSFAMILY_IOS)
                {
                    localURL = new URL("file", "localhost", buffer.toString());
                }
                DeviceManagerFactory.getDeviceManager().displayFile(localURL.toString(), "remote file");
            } catch (Throwable t) {
                System.out.println("Exception caught: " + t.toString());
            }

### 14.11.10 addLocalNotificationおよびcancelLocalNotificationメソッドを使用してローカル通知を管理する方法

DeviceFeaturesデータ・コントロールには、エンド・ユーザーがローカル通知をスケジュールまたは取り消すことができるようにMAFアプリケーションが管理対象通知にデバイスのインタフェースを利用できるようにする`addLocalNotification`および`cancelLocalNotification`メソッドが含まれます。

DeviceFeaturesデータ・コントロールを使用してaddLocalNotificationまたはcancelLocalNotification操作をカスタマイズするには:

1. JDeveloperで、「データ・コントロール」パネルのDeviceFeaturesデータ・コントロールからaddLocalNotificationまたはcancelLocalNotification操作をドラッグし、ボタン、リンク、リスト・アイテムまたはパラメータ・フォームとしてページ・デザイナ上にドロップします。

   ボタン、リンクまたはリスト・アイテム: 「アクション・バインディングの編集」ダイアログが表示され、`addLocalNotification`または`cancelLocalNotification`操作の引数の値を入力するよう求められます。このダイアログの詳細は、Oracle JDeveloperのオンライン・ヘルプを参照してください。実行時にページに表示されるボタン、リンクまたはリスト・アイテムをクリックすると、入力した値を使用して`addLocalNotification`または`cancelLocalNotification`操作を実行できます。

   パラメータ・フォーム: 「フォーム・フィールドの編集」ダイアログでフォームをカスタマイズします。このダイアログの詳細は、Oracle JDeveloperのオンライン・ヘルプを参照してください。実行時に、アプリケーション・ユーザーが様々なフィールドの値を入力できる、編集可能なフォームがページに表示されます。このフォームの下にあるボタンをクリックすると、入力した値を使用して`addLocalNotification`または`cancelLocalNotification`操作を実行できます。

図14-16に、選択した操作のパラメータを構成するために使用する「アクション・バインディングの編集」ダイアログを示します。この例では、`cancelLocalNotification`操作のnotificationIDが`addLocalNotification`操作の結果にバインドされます。

図14-16 ローカル通知のスケジュール用のバインドの設定

図14-17に、式ビルダーを使用して`addLocalNotification`操作の結果を`cancelLocalNotification`操作にバインドする方法を示します。

図14-17 addLocalNotificationの結果へのcancelLocalNotificationのバインド

`addLocalNotification`および`cancelLocalNotification`メソッドの詳細は、MAF Javadocの`DeviceDataControl`クラスを参照してください。コード例を含むローカル通知の管理の詳細は、第24.3項「ローカル通知の管理」を参照してください。通知の全般情報は、第24.1項「通知の概要」を参照してください。

### 14.11.11 デバイス・プロパティに関する必知事項

アプリケーションには、特定のデバイスの特性や機能に依存する機能が含まれる場合があります。たとえば、デバイスの画面の向きによって異なるユーザー・インタフェースを提示したり、デバイスで地理的位置がサポートされる場合のみマッピング機能を有効にすることなどが考えられます。MAFでは、このような動的な動作をサポートするためにJava、JavaScriptおよびELからアクセスできる、様々なプロパティが提供されます。表14-8に、これらのプロパティをリストし、問合せ方法、予期される戻り値およびアプリケーションのライフサイクル中にそのプロパティが変更される可能性があるかどうかに関する情報も提供します。例14-39は、JavaScriptを使用してこれらのプロパティにアクセスする方法の例を示しています。

注意: デバイス・プロパティのタイムアウト値は、1分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

表14-8 デバイス・プロパティと対応するEL式

プロパティ | 静的/ 動的 | EL式 | サンプル値 | Java API
--- | --- | --- | --- | ---
`device.name` | 静的 | `#{deviceScope.device.name}` | `"iPhone Simulator"、"Joe Smith's iPhone"` | `DeviceManager.getName()`
`device.platform` | 静的 | `#{deviceScope.device.platform}` | `"iPhone Simulator"、"iPhone"` | `DeviceManager.getPlatform()`
`device.version` | 静的 | `#{deviceScope.device.version}` | `"4.3.2"、"5.0.1"` | `DeviceManager.getVersion()`
`device.os` | 静的 | `#{deviceScope.device.os}` | `"iOS"` | `DeviceManager.getOs()`
`device.model` | 静的 | `#{deviceScope.device.model}` | `"x86_64"、"i386"、"iPhone3,1"` | `DeviceManager.getModel()`
`device.phonegap` | 静的 | `#{deviceScope.device.phonegap}` | `"1.0.0"` | `DeviceManager.getPhonegap()`
`hardware.hasCamera` | 静的 | `#{deviceScope.hardware.hasCamera}` | `"true"、"false"` | `DeviceManager.hasCamera()`
`hardware.hasContacts` | 静的 | `#{deviceScope.hardware.hasContacts}` | `"true"、"false"` | `DeviceManager.hasContacts()`
`hardware.hasTouchScreen` | 静的 | `#{deviceScope.hardware.hasTouchScreen}` | `"true"、"false"` | `DeviceManager.hasTouchScreen()`
`hardware.hasGeolocation` | 静的 | `#{deviceScope.hardware.hasGeolocation}` | `"true"、"false"` | `DeviceManager.hasGeolocation()`
`hardware.hasAccelerometer` | 静的 | `#{deviceScope.hardware.hasAccelerometer}` | `"true"、"false"` | `DeviceManager.hasAccelerometer()`
`hardware.hasCompass` | 静的 | `#{deviceScope.hardware.hasCompass}` | `"true"、"false"` | `DeviceManager.hasCompass()`
`hardware.hasFileAccess` | 静的 | `#{deviceScope.hardware.hasFileAccess}` | `"true"、"false"` | `DeviceManager.hasFileAccess()`
`hardware.hasLocalStorage` | 静的 | `#{deviceScope.hardware.hasLocalStorage}` | `"true"、"false"` | `DeviceManager.hasLocalStorage()`
`hardware.hasMediaPlayer` | 静的 | `#{deviceScope.hardware.hasMediaPlayer}` | `"true"、"false"` | `DeviceManager.hasMediaPlayer()`
`hardware.hasMediaRecorder` | 静的 | `#{deviceScope.hardware.hasMediaRecorder}` | `"true"、"false"` | `DeviceManager.hasMediaRecorder()`
`hardware.networkStatus` | 動的 | `#{deviceScope.hardware.networkStatus}` | `"wifi"、"2g"、"unknown"、"none"`脚注 1 | `DeviceManager.getNetworkStatus()`
`hardware.screen.width` | 動的 | `#{deviceScope.hardware.screen.width}` | `320、480` | `DeviceManager.getScreenWidth()`
`hardware.screen.height` | 動的 | `#{deviceScope.hardware.screen.height}` | `480、320` | `DeviceManager.getScreenHeight()`
`hardware.availableWidth` | 動的 | `#{deviceScope.hardware.screen.availableWidth}` | `<= 320、<= 480` | `DeviceManager.getAvailableScreenWidth()`
`hardware.availableHeight` | 動的 | `#{deviceScope.hardware.screen.availableHeight}` | `<= 480、<= 320` | `DeviceManager.getAvailableScreenHeight()`
`hardware.screen.dpi` | 静的 | `#{deviceScope.hardware.screen.dpi}` | `160、326` | `DeviceManager.getScreenDpi()`
`hardware.screen.diagonalSize` | 静的 | `#{deviceScope.hardware.screen.diagonalSize}` | `9.7、6.78` | `DeviceManager.getScreenDiagonalSize()`
`hardware.screen.scaleFactor` | 静的 | `#{deviceScope.hardware.screen.scaleFactor}` | `1.0、2.0` | `DeviceManager.getScreenScaleFactor()`

脚注 1 `wifi`と`2G`の両方が有効になっている場合、`wifi`が`2G`よりも優先されるため、ネットワーク・ステータスは`wifi`になります。

例14-39は、JavaScriptを使用してデバイス・プロパティにアクセスする方法を示しています。

例14-39 JavaScriptを使用したデバイス・プロパティへのアクセス

    <!DOCTYPE html>
    <html>
      <head>
        <title>Device Properties Example</title>

        <script type="text/javascript" charset="utf-8" src="cordova-2.2.0.js"></script>
        <script type="text/javascript" charset="utf-8">

        // Wait for Cordova to load
        //
        //document.addEventListener("deviceready", onDeviceReady, false);
        document.addEventListener("showpagecomplete",onDeviceReady,false);

        // Cordova is ready
        //
        function onDeviceReady() {
            adf.mf.api.getDeviceProperties(properties_success, properties_fail);
        }

        function properties_success(response) {
          try {
            var element = document.getElementById('deviceProperties');
            var device = response.device;
            var hardware = response.hardware;
            element.innerHTML = 'Device Name:              ' + device.name            + '<br />' +
                                'Device Platform:          ' + device.platform        + '<br />' +
                                'Device Version:           ' + device.version         + '<br />' +
                                'Device OS:                ' + device.os              + '<br />' +
                                'Device Model:             ' + device.model           + '<br />' +
                                'Hardware Screen Width:    ' + hardware.screen.width  + '<br />' +
                                'Hardware Screen Height:   ' + hardware.screen.height + '<br />' +
          } catch (e) {alert("Exception: " + e);}
        }

        function properties_fail(error) {
            alert("getDeviceProperties failed");
        }

        </script>
      </head>
      <body>
        <p id="deviceProperties">Loading device properties...</p>
      </body>
    </html>

注意: JavaScript関数を`showpagecomplete`イベントに宣言的にバインドするには、次の例のように、`amx:clientListener`タグを`<amx:view>`の直接の子として追加します。
    <amx:clientListener type="showpagecomplete" method="myShowPageCompleteHandler"/>
クライアント・リスナー(`clientListener`)コンポーネントの詳細は、第13.3.24項「クライアント・リスナーの使用方法」を参照してください。

## 14.12 属性の検証

モバイル・アプリケーション・フレームワークでは、バインディング属性に設定された検証ルールを使用してデータ・コントロール・レイヤーで検証が行われます。属性の検証は、バインディングに対する`setValue`操作中に、システム内の単一点で実行されます。

データ・コントロールによって公開される属性に対して、次のバリデータを定義できます。

- 比較バリデータ

- 長さバリデータ

- リスト・バリデータ

- 範囲バリデータ

特定の属性に対してすべてのバリデータが実行され、成功しないすべてのバリデータに対してネストされた例外がスローされます。属性の検証メッセージを定義し、実行時に検証ルールを起動するときに表示できます。詳細は、第13.9項「入力の検証」と第14.12.1項「検証ルールの追加方法」を参照してください。

注意: JSONの制限により、`BigDecimal`が保持できる値は`Double`の範囲内となり、`BigInteger`が保持できる値は`Long`の範囲内となります。許可された値よりも大きい数値を使用するには、`toString`を`BigDecimal`または`BigInteger`でコールし、値を`String`としてシリアライズ(解除)します。

表14-9に、長さバリデータでサポートされる検証の組合せをリストします。

表14-9 長さの検証

比較タイプ | バイト | 文字
--- | --- | ---
`Equals` | サポート | サポート
`Not Equals` | サポート | サポート
`Less Than` | サポート | サポート
`Greater Than` | サポート | サポート
`Less Than Equal To` | サポート | サポート
`Greater Than Equal To` | サポート | サポート
`Between` | サポート | サポート

表14-10と表14-11に、範囲バリデータでサポートされる検証の組合せを示します。

表14-10 範囲検証

比較タイプ | バイト | 文字 | 倍精度 | 浮動小数 | 整数 | 長整数 | 短整数
--- | --- | --- | --- | --- | --- | --- | ---
`Between` | サポート | サポート | サポート | サポート | サポート | サポート | サポート
`Not Between` | サポート | サポート | サポート | サポート | サポート | サポート | サポート

表14-11 範囲検証: math、sqlおよびutilパッケージ

比較タイプ | java.math.BigDecimal | java.math.BigInteger | java.sql.Date | java.sql.Time | java.sql.Timestamp | java.util.Date
--- | --- | --- | --- | --- | --- | ---
`Between` | サポート | サポート | 未サポート | 未サポート | 未サポート | 未サポート
`Not Between` | サポート | サポート | 未サポート | 未サポート | 未サポート | 未サポート

表14-12に、リスト・バリデータでサポートされる検証の組合せをリストします。

表14-12 リスト検証

比較タイプ | 文字列
--- | ---
`In` | サポート
`Not In` | サポート

表14-13と表14-14に、比較バリデータでサポートされる検証の組合せを示します。

表14-13 比較検証

比較タイプ | バイト | 文字 | 倍精度 | 浮動小数 | 整数 | 長整数 | 短整数 | 文字列
--- | --- | --- | --- | --- | --- | --- | --- | ---
`Equals` | サポート | サポート | サポート | サポート | サポート | サポート | サポート | サポート
`Not Equals` | サポート | サポート | サポート | サポート | サポート | サポート | サポート | サポート
`Less Than` | 未サポート | サポート | サポート | サポート | サポート | サポート | サポート | 未サポート
`Greater Than` | 未サポート | サポート | サポート | サポート | サポート | サポート | サポート | 未サポート
`Less Than Equal To` | 未サポート | サポート | サポート | サポート | サポート | サポート | サポート | 未サポート
`Greater Than Equal To` | 未サポート | サポート | サポート | サポート | サポート | サポート | サポート | 未サポート

表14-14 比較検証: java.math、java.sqlおよびjava.utilパッケージ

比較タイプ | java.math.BigDecimal | java.math.BigInteger | java.sql.Date | java.sql.Time | java.sql.Timestamp | java.util.Date
--- | --- | --- | --- | --- | --- | ---
`Equals` | サポート | サポート | 未サポート | 未サポート | 未サポート | 未サポート
`Not Equals` | サポート | サポート | 未サポート | 未サポート | 未サポート | 未サポート
`Less Than` | サポート | サポート | 未サポート | 未サポート | 未サポート | 未サポート
`Greater Than` | サポート | サポート | 未サポート | 未サポート | 未サポート | 未サポート
`Less Than Equal To` | サポート | サポート | 未サポート | 未サポート | 未サポート | 未サポート
`Greater Than Equal To` | サポート | サポート | 未サポート | 未サポート | 未サポート | 未サポート

### 14.12.1 検証ルールの追加方法

検証ルールは、様々なユース・ケースに合わせて定義できます。宣言的検証ルールをエンティティ・オブジェクトに追加するには、データ・コントロール構造ファイルの「概要」エディタにある「属性」ページを使用します。

検証規則を追加するには:

1. 「データ・コントロール」パネルで、データ・コントロール・オブジェクトを右クリックして「定義の編集」を選択します。

2. データ・コントロール構造ファイルの「概要」エディタで、「属性」ページを選択します。

3. ページの下部にある「検証ルール」タブを選択し、「追加」をクリックします。表示された「検証ルールの追加」ダイアログで、検証ルールと失敗処理を定義します。

### 14.12.2 バリデータのメタデータに関する必知事項

バリデータのメタデータは、設計時にデータ・コントロール構造のメタデータXMLファイル内に配置されます。例14-40は、サンプルの長さバリデータを示しています。

例14-40 メタデータ・ファイル内で宣言された長さバリデータ

    <?xml version="1.0" encoding="windows-1252" ?>
    <!DOCTYPE PDefViewObject SYSTEM "jbo_03_01.dtd">
    <PDefViewObject
       xmlns="http://xmlns.oracle.com/bc4j"
       Name="Product"
       Version="12.1.1.61.36"
       xmlns:validation="http://xmlns.oracle.com/adfm/validation">
       <DesignTime>
          <Attr Name="_DCName" Value="DataControls.ProductListBean"/>
          <Attr Name="_SDName" Value="mobile.Product"/>
       </DesignTime>
       <PDefAttribute
          Name="name">
          <validation:LengthValidationBean
             Name="nameRule0"
             OnAttribute="name"
             CompareType="GREATERTHAN"
             DataType="BYTE"
             CompareLength="5"
             Inverse="false"/>
       </PDefAttribute>
    </PDefViewObject>

## 14.13 データ変更イベントについて

JDeveloperでは、データ変更イベントを単純化するために、プロパティ変更リスナー・パターンを使用します。多くの場合、JDeveloperを使用して、Beanのプロパティ・アクセッサから通知をソースとして参照するために必要なコードを生成できます。これを行うには、「アクセッサの生成」ダイアログの「プロパティの変更時にリスナーに通知」チェックボックスを選択します(詳細は第14.3.5.2項「マネージドBeanカテゴリについて」を参照)。`PropertyChangeSupport`オブジェクトが自動的に生成され、新たに生成されたsetterメソッドに`firePropertyChange`へのコールが含まれます。また、`addPropertyChangeListener`および`removePropertyChangeListener`メソッドが追加されるため、プロパティ変更リスナーは、このオブジェクトを使用して自身を登録および登録解除できます。これを使用してフレームワークはクライアント・キャッシュにプッシュする変更を取得し、データが変更されたことをユーザー・インタフェース・レイヤーに通知します。

注意: `PropertyChangeSupport`オブジェクトを手動でクラスに追加する場合は、`addPropertyChangeListener`および`removePropertyChangeListener`メソッドも(これらの明示的なメソッド名を使用して)含める必要があります。

プロパティ変更のみでは、すべてのデータ変更通知は解決されません。たとえば、データ・コントロールによってラップされたBeanがあり、アイテムのコレクションの公開が必要な場合が考えられます。リストの個々のアイテムが変更されたときはプロパティ変更で十分ですが、カーディナリティが変更されたときは十分ではありません。この場合、コレクション全体のプロパティ変更を起動すると、パフォーマンスが低下する可能性があるため、かわりにコレクションのデルタのみを更新します。これを行うには、`ProviderChangeSupport`クラスを使用して、単純なプロパティ変更に必要なデータよりも多くのデータを公開する必要があります。プロバイダ変更イベントはプロパティ変更イベントと似ていますが、個別のプロパティのみを対象とするのではなく、プロバイダ全体に適用されます。

注意: `ProviderChangeSupport`オブジェクトは、自動的には生成されません。このオブジェクトは、手動で`addProviderChangeListener`および`removeProviderChangeListener`メソッドとともに(これらの明示的なメソッド名を使用して)クラスに追加する必要があります。

プロバイダ変更は、データ・コントロールによってラップされたBeanで公開されている動的なコレクションを保持しているときのみ必要になるため、起動するプロバイダ変更イベントは、次のいくつかのタイプのみになります。

- `fireProviderCreate`: 新しい要素をコレクションに追加する場合

- `fireProviderDelete`: 要素をコレクションから削除する場合

- `fireProviderChange`: コレクション内の1つの要素を変更する場合(リスト全体のリフレッシュを回避するのに必要です)

- `fireProviderRefresh`: 同時に複数の変更をコレクションに加え、単純にクライアントにコレクション全体のリフレッシュを要求した方がよいと判断した場合(これは一括操作でのみ使用する必要があります)

`ProviderChangeSupport`クラスは、コレクション要素に関連する通知を送信するために使用され、これによって、Java Beanデータ・コントロール内で変更が発生したときにコンポーネントを適切に更新できます。このクラスが従うパターンは、自動的に生成される`PropertyChangeSupport`クラスと似ていますが、`ProviderChangeSupport`とともに使用されるイベント・オブジェクトでは、操作のタイプや変更された要素のキーと位置などの詳細情報を送信します。`ProviderChangeSupport`では、コレクションに対する要素(プロバイダ)の追加や削除など、コレクションに対する構造的な変更を取得します。`PropertyChangeSupport`では、コレクション内の個々のアイテムに対する変更を取得します。

例14-41は、`ProviderChangeSupport`を使用して、コレクションの要素に対する構造的な変更(子を追加または削除する場合など)に関する通知を送信する方法を示しています。`ProviderChangeListene`インタフェースと`ProviderChangeEvent`クラスおよび`ProviderChangeSupport`クラスの詳細は、MAF Javadocを参照してください。

例14-41 ProviderChangeSupportのコード例

    public class NotePad {
       private static List s_notes = null;

    /* manually adding property change listener as well as provider change listener. */
       protected transient PropertyChangeSupport
                           propertyChangeSupport = new PropertyChangeSupport(this);
       protected transient ProviderChangeSupport
                           providerChangeSupport = new ProviderChangeSupport(this);

        public NotePad() {
            …
        }

        public  mobile.Note[] getNotes() {
            mobile.Note n[] = null;

            synchronized (this) {
                if(s_notes.size() > 0) {
                    n = (mobile.Note[])
                        s_notes.toArray(new mobile.Note[s_notes.size()]);
                }
                else {
                    n = new mobile.Note[0];
                }
            }

            return n;
        }

        public void addNote() {
            System.out.println("Adding a note ....");
            Note n = new Note();
            int s = 0;

            synchronized (this) {
                s_notes.add(n);
                s = s_notes.size();
            }

            System.out.println("firing the events");
            providerChangeSupport.fireProviderCreate("notes", n.getUid(), n);
        }

        public void removeNote() {
            System.out.println("Removng a note ....");
            if(s_notes.size() > 0) {
                int end = -1;
                Note n = null;

                synchronized (this) {
                    end = s_notes.size() - 1;
                    n = (Note)s_notes.remove(end);
                }

                System.out.println("firing the events");
                providerChangeSupport.fireProviderDelete("notes", n.getUid());
            }
        }

        public void RefreshNotes() {
            System.out.println("Refreshing the notes ....");

            providerChangeSupport.fireProviderRefresh("notes");
        }

        public void addProviderChangeListener(ProviderChangeListener l) {
            providerChangeSupport.addProviderChangeListener(l);
        }

        public void removeProviderChangeListener(ProviderChangeListener l) {
            providerChangeSupport.removeProviderChangeListener(l);
        }

        protected String   status;

        /* --- JDeveloper generated accessors --- */

        public void addPropertyChangeListener(PropertyChangeListener l) {
            propertyChangeSupport.addPropertyChangeListener(l);
        }

        public void removePropertyChangeListener(PropertyChangeListener l) {
            propertyChangeSupport.removePropertyChangeListener(l);
        }

        public void setStatus(String status) {
            String oldStatus = this.status;
            this.status = status;
            propertyChangeSupport.firePropertyChange("status", oldStatus, status);
        }

        public String getStatus() {
            return status;
        }
    }

データ変更は、JVMレイヤーからのレスポンス・メッセージや戻り値とともに、クライアントに(キャッシュするために)戻されます。これにより、JDeveloperではユーザー・インタフェースに対してリフレッシュするイベントと更新の数を縮小し、可能なかぎりフレームワークの効率を上げることができます。

ただし、バックグラウンド・スレッドで実行時間の長い処理(Webサービスの相互作用、データベースの相互作用、コストのかかる計算など)に対処し、ユーザー・アクションに依存しないユーザー・インタフェースへの通知を行うことが必要になる場合もあります。AMXページのデータを更新して、値が変更されたデータ・フィールドの現在の値を反映させる場合、`AdfmfJavaUtilities.flushDataChangeEvent`をコールして現在キューに入っているデータ変更をクライアントに強制することによって、AMXページ全体のリロードによるパフォーマンスの低下を回避できます。

注意: `flushDataChangeEvent`メソッドは、バックグラウンド・スレッドからのみ実行できます。

例14-42は、`flushDataChangeEvent`メソッドを使用して保留中のデータ変更をクライアントに強制する方法を示しています。`oracle.adfmf.framework.api.AdfmfJavaUtilities.flushDataChangeEvent`の詳細は、Oracle Fusion Middleware Oracle Mobile Application Framework Java APIリファレンスを参照してください。

例14-42 データ変更イベントの例

    /* Note – Simple POJO used by the NotePad managed bean or data control wrapped bean */

    package mobile;

    import oracle.adfmf.amx.event.ActionEvent;
    import oracle.adfmf.framework.api.AdfmfJavaUtilities;
    import oracle.adfmf.java.beans.PropertyChangeListener;
    import oracle.adfmf.java.beans.PropertyChangeSupport;

    /**
     * Simple note object
     * uid   - unique id - generated and not mutable
     * title - title for the note - mutable
     * note  - note comment - mutable
     */
    public class Note {
        /* standard JDeveloper generated property change support */
        protected transient PropertyChangeSupport
                           propertyChangeSupport = new PropertyChangeSupport(this);

        private static boolean s_backgroundFlushTestRunning = false;

        public Note() {
            this("" + (System.currentTimeMillis() % 10000));
        }

        public Note(String id) {
            this("UID-"+id, "Title-"+id, "");
        }

        public Note(String uid, String title, String note) {
            this.uid     = uid;
            this.title   = title;
            this.note    = note;
        }

        /* update the current note with the values passed in */
        public void updateNote(Note n) {
            if (this.getUid().compareTo(n.getUid()) == 0) {
                this.setTitle(n.getTitle());
                this.setNote(n.getNote());
            }
            else {
                throw new IllegalArgumentException("note");
            }
        }

        /* background thread to simulate some background process that make changes */
        public void startNodeBackgroundThread(ActionEvent actionEvent) {
            Thread backgroundThread   = new Thread() {
                public void run() {
                    System.out.println("startBackgroundThread enter - " +
                                                          s_backgroundFlushTestRunning);

                    s_backgroundFlushTestRunning = true;
                    for(int i = 0; i <= iterations; ++i) {
                        try {
                            System.out.println("executing " + i + " of " + iterations + "
                                    " iterations.");

                            /* update a property value */
                            if(i == 0) {
                                setNote("thread starting");
                            }
                            else if( i == iterations) {
                                setNote("thread complete");
                                s_backgroundFlushTestRunning = false;
                            }
                            else {
                                setNote("executing " + i + " of " + iterations + " iterations.");
                            }
                            setVersion(getVersion() + 1);
                            setTitle("Thread Test v" + getVersion());
                            AdfmfJavaUtilities.flushDataChangeEvent();  /* key line */
                        }
                        catch(Throwable t) {
                            System.err.println("Error in the background thread: " + t);
                        }

                        try {
                            Thread.sleep(delay);  /* sleep for 6 seconds */
                        }
                        catch (InterruptedException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            };

            backgroundThread.start();
        }

        protected String uid;
        protected String title;
        protected String note;
        protected int    version;

        protected int iterations = 10;
        protected int delay = 500;

        /* --- JDeveloper generated accessors --- */

        public void addPropertyChangeListener(PropertyChangeListener l) {
            propertyChangeSupport.addPropertyChangeListener(l);
        }

        public void removePropertyChangeListener(PropertyChangeListener l) {
            propertyChangeSupport.removePropertyChangeListener(l);
        }

        public String getUid() {
            return uid;
        }

        public void setTitle(String title) {
            String oldTitle = this.title;
            this.title = title;
            propertyChangeSupport.firePropertyChange("title", oldTitle, title);
        }

        public String getTitle() {
            return title;
        }

        public void setNote(String note) {
            String oldNote = this.note;
            this.note = note;
            propertyChangeSupport.firePropertyChange("note", oldNote, note);
        }

        public String getNote() {
            return note;
        }

        public void setVersion(int version) {
            int oldVersion = this.version;
            this.version = version;
            propertyChangeSupport.firePropertyChange("version", oldVersion, version);
        }

        public int getVersion() {
            return version;
        }

        public void setIterations(int iterations) {
            int oldIterations = this.iterations;
            this.iterations = iterations;
            propertyChangeSupport.
                     firePropertyChange("iterations", oldIterations, iterations);
        }

        public int getIterations() {
            return iterations;
        }

        public void setDelay(int delay) {
            int oldDelay = this.delay;
            this.delay = delay;
            propertyChangeSupport.
                    firePropertyChange("delay", oldDelay, delay);
        }

        public int getDelay() {
            return delay;
        }
    }

    /* NotePad – Can be used as a managed bean or wrapped as a data control */

    package mobile;

    import java.util.ArrayList;
    import java.util.List;

    import oracle.adfmf.amx.event.ActionEvent;
    import oracle.adfmf.framework.api.AdfmfJavaUtilities;
    import oracle.adfmf.java.beans.PropertyChangeListener;
    import oracle.adfmf.java.beans.PropertyChangeSupport;
    import oracle.adfmf.java.beans.ProviderChangeListener;
    import oracle.adfmf.java.beans.ProviderChangeSupport;

    public class NotePad {
        private static List s_notes = null;
        private static boolean s_backgroundFlushTestRunning = false;

        protected transient PropertyChangeSupport propertyChangeSupport =
                                                       new PropertyChangeSupport(this);

        protected transient ProviderChangeSupport
            providerChangeSupport = new ProviderChangeSupport(this);

        public NotePad() {
            if (s_notes == null) {
                s_notes = new ArrayList();

                for(int i = 1000; i < 1003; ++i) {
                    s_notes.add(new Note(""+i));
                }
            }
        }

        public  mobile.Note[] getNotes() {
            mobile.Note n[] = null;

            synchronized (this) {
                if(s_notes.size() > 0) {
                    n = (mobile.Note[])s_notes.
                         toArray(new mobile.Note[s_notes.size()]);
                }
                else {
                    n = new mobile.Note[0];
                }
            }

            return n;
        }

        public void addNote() {
            System.out.println("Adding a note ....");
            Note  n = new Note();
            int   s = 0;

            synchronized (this) {
                s_notes.add(n);
                s = s_notes.size();
            }

            System.out.println("firing the events");

            /* update the note count property on the screen */
            propertyChangeSupport.
                 firePropertyChange("noteCount", s-1, s);

            /* update the notes collection model with the new note */
            providerChangeSupport.
                 fireProviderCreate("notes", n.getUid(), n);

            /* to update the client side model layer */
            AdfmfJavaUtilities.flushDataChangeEvent();
        }

        public void removeNote() {
            System.out.println("Removing a note ....");
            if(s_notes.size() > 0) {
                int end = -1;
                Note n = null;

                synchronized (this) {
                    end = s_notes.size() - 1;
                    n = (Note)s_notes.remove(end);
                }

                System.out.println("firing the events");

                /* update the client side model layer */
                providerChangeSupport.fireProviderDelete("notes", n.getUid());

               /* update the note count property on the screen */
               propertyChangeSupport.firePropertyChange("noteCount", -1, end);
            }
        }

        public void RefreshNotes() {
            System.out.println("Refreshing the notes ....");

            /* update the entire notes collection on the client */
            providerChangeSupport.fireProviderRefresh("notes");
        }

        public int getNoteCount() {
            int size = 0;

            synchronized (this) {
                size = s_notes.size();
            }
           return size;
        }

        public void addProviderChangeListener(ProviderChangeListener l) {
            providerChangeSupport.addProviderChangeListener(l);
        }

        public void removeProviderChangeListener(ProviderChangeListener l) {
            providerChangeSupport.removeProviderChangeListener(l);
        }

        public void startListBackgroundThread(ActionEvent actionEvent) {
            for(int i = 0; i < 10; ++i) {
                _startListBackgroundThread(actionEvent);
                try {
                    Thread.currentThread().sleep(i * 1234);
                }
                catch (InterruptedException e) {
                }
            }
        }

        public void
        _startListBackgroundThread(ActionEvent actionEvent) {
            Thread backgroundThread = new Thread() {
                public void run() {
                    s_backgroundFlushTestRunning = true;

                    for(int i = 0; i <= iterations; ++i) {
                        System.out.println("executing " + i +
                              " of " + iterations + " iterations.");

                        try {
                            /* update a property value */
                            if(i == 0) {
                                setStatus("thread starting");
                                addNote();  // add a note
                            }
                            else if( i == iterations) {
                                setStatus("thread complete");
                                removeNote();  // remove a note
                                s_backgroundFlushTestRunning = false;
                            }
                            else {
                                setStatus("executing " + i + " of " +
                                        iterations + " iterations.");

                                synchronized (this) {
                                    if(s_notes.size() > 0) {
                                        Note n =(Note)s_notes.get(0);

                                        n.setTitle("Updated-" +
                                              n.getUid() + " v" + i);
                                    }
                                }
                            }
                            AdfmfJavaUtilities.flushDataChangeEvent();
                        }
                        catch(Throwable t) {
                            System.err.
                            println("Error in bg thread - " + t);
                        }

                        try {
                             Thread.sleep(delay);
                        }
                        catch (InterruptedException ex) {
                            setStatus("inturrpted " + ex);
                            ex.printStackTrace();
                        }
                    }
                }
            };

            backgroundThread.start();
        }

        protected int iterations = 100;
        protected int delay      = 750;

        protected String   status;

        /* --- JDeveloper generated accessors --- */

        public void addPropertyChangeListener(PropertyChangeListener l) {
            propertyChangeSupport.addPropertyChangeListener(l);
        }

        public void removePropertyChangeListener(PropertyChangeListener l) {
            propertyChangeSupport.removePropertyChangeListener(l);
        }

        public void setStatus(String status) {
            String oldStatus = this.status;
            this.status = status;
            propertyChangeSupport.firePropertyChange("status", oldStatus, status);
        }

        public String getStatus() {
            return status;
        }

        public void setIterations(int iterations) {
            int oldIterations = this.iterations;
            this.iterations = iterations;
            propertyChangeSupport.firePropertyChange("iterations", oldIterations, iterations);
        }

        public int getIterations() {
            return iterations;
        }

        public void setDelay(int delay) {
            int oldDelay = this.delay;
            this.delay = delay;
            propertyChangeSupport.firePropertyChange("delay", oldDelay, delay);
        }

        public int getDelay() {
            return delay;
        }
    }

StockTrackerサンプル・アプリケーションでは、データ変更イベントでJavaを使用してデータ変更をユーザー・インタフェースに反映する方法の例を示します。このサンプル・アプリケーションは、開発用コンピュータのJDeveloperインストール・ディレクトリ内にある次の場所の`PublicSamples.zip`ファイルに含まれています。

    jdev_install/jdeveloper/jdev/extensions/oracle.maf/Samples

サンプル・アプリケーションの詳細は、付録G「サンプルのMAFアプリケーション」を参照してください。