ヘッダーをスキップ

Oracle® Enterprise Pack for Eclipse Oracle Mobile Application Framework (OEPE Edition)でのモバイル・アプリケーションの開発 リリース2.0.1 E57593-01 |
| 目次

前 | 次 |

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

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

この章には次の項が含まれます:

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

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

- 第7.3項「EL式の作成」

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

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

- 第7.6項「データ・コントロール・パレットでのデータバインドUIコンポーネントの作成」

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

- 第7.8項「データ・コントロール属性の使用」

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

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

- 第7.11項「属性の検証」

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

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

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

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

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

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

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

- 第7.6項「データ・コントロール・パレットでのデータバインドUIコンポーネントの作成」

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## 7.3 EL式の作成

MAFアプリケーションのEL式を使用して、属性と実行時に決まるオブジェクト値とをバインドします。たとえば、`#{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の作成と使用の詳細は、第7.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;
        }

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

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

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

    #{bindings.BindingObject.propertyName}

ここで、

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

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

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

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

    #{bindings.ProductName.inputValue}

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

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

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

### 7.3.2 EL式の作成方法

Oracle Enterprise Pack for Eclipseの式ビルダーを使用すると、EL式を宣言的に作成できます。エディタ、ダイアログまたはページ・エディタ・プロパティにが表示される式ビルダーにアクセスできます。

始める前に

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

式ビルダーの使用手順:

1. MAF機能エディタで、変更する機能を展開し、「制約」を右クリックします。「新」→「制約式」を選択します。

2. 「制約式」の下で、をクリックして「式ビルダー」ダイアログを開きます。

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

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

     ツリーを絞り込むには、「変数」に検索条件を入力します。MAFで公開されているELでアクセス可能なオブジェクトは、MAFオブジェクト・ノードの下、「マネージドBean」ノードにあります。

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

     ツリーの項目をダブルクリックすると、項目はEL式内の「式」ボックスに移動します。「式」ボックスに式を直接入力することもできます。

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

   - ツリーのフォルダおよび変数に関する詳細を表示するには、その上にマウスを置きます。

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

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

   ヒント: EL式を作成する正しい構文の使用の詳細は、`http://download.oracle.com/javaee/index.html`の『Java EE tutorial』を参照してください。

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

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

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

プロパティ | 要素
--- | ---
`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`

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

表7-2は、ELに対応していないため「プロパティ」ウィンドウで使用可能なEL「式ビルダー」オプションを持たないプロパティを示しています。

表7-2 非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`

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

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

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

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

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

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

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

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

表7-3 MAFバインディングのランタイムELプロパティ

ランタイム・プロパティ | 説明 | イテレータ | アクション | attributeValues | ツリー
--- | --- | --- | --- | --- | ---
`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`の子または属性の直接の子として使用できます。バインディングの最初の属性のラベルを戻します(コントロール`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`変数を先頭の語句として追加します。

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

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

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

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

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

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

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

- `authenticated`

- `userGrantedPrivilege`

- `userInRole`

- `userName`

次に例を示します。

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

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

バインディング・タイプ | プロパティ
--- | ---
`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`

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

MAF変数とマネージドBean参照は、変数の存続期間と可視性を決定する様々なオブジェクト・スコープ内で定義されます。これらのスコープには、可視性が高い順に、アプリケーション・スコープ、ページ・フロー・スコープおよびビュー・スコープがあります。様々なオブジェクト・スコープの詳細は、第7.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式を次に示します。

  例7-4 属性名が予約語である場合

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

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

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

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

- `viewControllerBundle`

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

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

  例7-5 loadBundle要素のAMXコード・サンプル

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

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

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

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

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

例7-6 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}"/>

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

例7-7 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}"/>

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

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

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

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

例7-8 マネージドBeanからのEL式の解決

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

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

例7-9 マネージド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);
    }

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

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

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

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

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

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

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

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

### 7.4.1 Oracle Enterprise Pack for EclipseでのマネージドBeanの作成方法

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

始める前に

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

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

1. プロジェクト・エクスプローラで、ビュー・プロジェクトを展開してViewContentを展開し、adfc-mobile-config.xmlをダブルクリックします。

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

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

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

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

4. 図7-4のように、マネージドBeanを選択し、マネージドBeanの追加をクリックします。

   図7-4 マネージドBeanの作成

5. マネージドBeanを定義するには、次の手順を実行します。

   - Beanの名前を入力します。

   - 既存のクラスを参照するか、新しいクラスの名前を入力して、をクリックします。新しいJavaクラスが作成され、エディタで開きます。

   - Beanのスコープを選択します。オプションは次のとおりです。

     - アプリケーション

     - ページ・フロー

     - ビュー

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

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

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

### 7.4.2 Oracle Enterprise Pack for Eclipseを使用してマネージドBeanを作成する場合の処理

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

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

    package view;

    public class MyBean {
        public MyBean() {
        }
    }

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

    #{my_bean.myInfo}

Oracle Enterprise Pack for Eclipseは、`managed-bean`要素を`adfc-mobile-config.xml`ファイルに追加します。例7-12は、`MyBean`クラスに対して作成された`managed-bean`要素を示しています。

例7-12 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>

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

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

「ファイル」→「新」→「その他」を選択することで表示される「新」ダイアログからデータ・コントロールを生成します。データ・コントロールは、1つ以上のXMLメタデータ・ファイルで構成され、これらのファイルでは、実行時にバインディングと連動するサービスの機能が定義されます。データ・コントロールは、基礎となるサービスと連動します。

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

Oracle Enterprise Pack for Eclipseのプロジェクト・エクスプローラ内からアダプタ・ベースのデータ・コントロールを作成します。

始める前に:

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

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

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

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

1. プロジェクト・エクスプローラで、ビュー・プロジェクトのトップレベル・ノードを右クリックし、「新」→「データ・コントロール」を選択します。

2. 「新」ダイアログで、「Oracle」を展開して「モバイル・アプリケーション・フレームワーク」を展開し、「データ・コントロール」を選択します。「次へ」をクリックします。

3. 「データ・コントロール・ソース」ページ:

   - プロジェクトを選択します。

   - データ・コントロールのタイプを選択します。

   - をクリックし、「データ・コントロール・ソース」ダイアログでクラスを選択します。

   「次へ」をクリックします。

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

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

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

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

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

#### 7.5.2.1 データ・コントロール・マネージャ

データ・コントロール・マネージャは、データ・コントロール・オブジェクトの階層のビューと、`DataControls.dcx`ファイルのデータ・モデルの公開メソッドを提供します。プロジェクト・エクスプローラからデータ・コントロール・マネージャを開くには、アプリケーション・オブジェクト・ノードを展開してMAFを展開し、データ・コントロール・マネージャをダブルクリックします。

データ・コントロール・マネージャおよびパレットで使用されているアイコンの説明の詳細は、表7-5を参照してください。

図7-5に示すように、オブジェクトを選択し、構造定義の編集リンクをクリックすることで、データ・コントロール・オブジェクトのラベルおよびツールチップを変更できます。

図7-5 データ・コントロール・マネージャ

#### 7.5.2.2 パレット

デフォルトでは、IDEの左下隅にパレットが表示されます。ノードをパレットからページのデザイン・エディタにドラッグすることで、データバインドUIコンポーネントを作成できます。パレットでダブルクリックすることで、タグおよびタグ・バインディングを作成でき、タグをページにドラッグした場合、式ビルダーを使用してバインドできます。

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

## 7.6 データ・コントロール・パレットからのデータバインドUIコンポーネントの作成

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

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

表7-5 パレット・アイコンおよびオブジェクト階層

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

### 7.6.1 データ・コントロール・パレットの使用方法

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

始める前に:

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

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

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

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

パレットを使用してUIコンポーネントを作成するには、次の手順を実行します。

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

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

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

   図7-6は、パレットのデータ・コレクションをページにドロップしたときに表示されるコンテキスト・メニューを示しています。

   図7-6 パレットのコンポーネントのドロップ

   コンテキスト・メニューから選択したコンポーネントに応じて、そのコンポーネントの外観を定義するためのダイアログがOracle Enterprise Pack for Eclipseによって表示されます。たとえば、コンテキスト・メニューから「フォーム」を選択すると、「フォーム・フィールドの編集」ダイアログが表示されます。コンポーネントを選択したら、Oracle Enterprise Pack for Eclipseによって、ビジュアル・エディタでページにUIコンポーネントが挿入されます。

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

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

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

   - ツールチップ

   - 書式設定

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

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

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

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

### 7.6.2 データ・コントロール・パレットの使用時に行われる処理

パレットを使用してアプリケーションを作成した場合、Oracle Enterprise Pack for Eclipseによって次のことが実行されます。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## 7.8 データ・コントロール属性の使用

ビジネス・サービスのデータ・コントロールを作成する際、個々のデータ・オブジェクトについて作成するデータ・コントロール構造ファイルでデータ・オブジェクトの永続属性のUIヒントを設定できます。

### 7.8.1 属性へのUIヒントの設定

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

UIヒントの1つに対して値が入力された属性はアイコンを使用します。

図7-8 データ・コントロール属性UIヒントの編集

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

1. プロジェクト・エクスプローラで、アセンブリまたはビュー・プロジェクト・ノードを展開し、MAFノードを展開します。

2. 「データ・コントロール・マネージャ」をダブルクリックします。

   > 3. 「アウトライン」の下で、mobile.Departmentを展開します。選択することで属性を編集できます。アイコンに鉛筆が載っている属性は、2つの編集可能なフィールドの1つにすでにエントリがあり、添付のイメージを参照してください。

3. 概要エディタの「アウトライン」の下で、編集する属性を選択します。

4. 必要に応じて、「属性」を展開し、「UIヒント」を展開し、「ラベル」および「ツール・チップ・テキスト」に対応した値を入力します。

## 7.9 Beanデータ・コントロールの作成と使用

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

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

### 7.9.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();

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

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

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

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

- `createContact`

- `findContacts`

- `getPicture`

- `removeContact`

- `sendEmail`

- `sendSMS`

- `startLocationMonitor`

- `updateContact`

- `displayFile`

ページを作成したら、DeviceFeaturesデータ・コントロール・メソッド(またはそれらのメソッド内にネストされたその他のオブジェクト)をパレットからMAF AMXビューにドラッグして、関連する機能にバインドされたコマンド・ボタンやその他のコンポーネントを作成できます。デフォルトのバインディングを受け入れるか、ELを使用してそれらのバインディングを変更できます。また、JavaScriptやJavaを使用して、機能を実装または構成することもできます。

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

データ・コントロールをMAFアプリケーションに含める方法の詳細は、第5.3.2.3項「ビューへのデータ・コントロールの追加」を参照してください。Apache Cordova対応デバイス機能のすべてへのアクセスは、MAFアプリケーションの場合、デフォルトでは有効ではありません。詳細は、第21.6項「デバイス機能へのアクセスの許可」を参照してください。

### 7.10.1 撮影を有効にするためのgetPictureメソッドの使用方法

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

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

注意: 撮影する写真に`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`メソッドによって戻された場合、ファイルのサイズを求めるために使用する前に、問合せパラメータを削除する必要があります。次に例を示します。 `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. 「パレット」→「データ・タブ」→「データ・コントロール」を選択して、ここからgetPicture操作をドラッグし、ボタンとしてページにドロップします(図7-10を参照)。

   図7-10 getPicture操作のカスタマイズ

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

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

3. getPictureの戻り値をドラッグして、「イメージ」としてページにドロップします。

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

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

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

このアプリケーションを実行すると、イメージ・チューザが自動的に表示され、エンド・ユーザーは表示するイメージを選択できます。イメージ・チューザが自動的に表示される理由は、イメージ・コントロールが`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`を設定してイメージ・サイズを制限します。

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

例7-13 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);
    }

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

例7-14 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);

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

例7-15 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);

### 7.10.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が表示されます。

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

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

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

詳細は、`sendSMS`メソッド、MAF Javadocの`DeviceDataControl`クラス、Cordovaドキュメント(`http://cordova.apache.org/docs/en/2.2.0/index.html`)を参照してください。

例7-16 sendSMSのJavaScriptコード例

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

例7-17 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");

### 7.10.3 電子メールを有効にするためのsendEmailメソッドの使用方法

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

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

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

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

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

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

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

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

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

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

注意: Androidでは、エンド・ユーザーが電子メールの編集中にアプリケーションから離れ、その後戻ってきたときには、もはや電子メール編集画面にはなっていません。かわりに、メッセージを下書きとして保存すると、手動で選択して編集を続行できます。

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

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

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

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

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

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

例7-18 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.

例7-19 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");

### 7.10.4 コンタクト先の作成を有効にするためのcreateContactメソッドの使用方法

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

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

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

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

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

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

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

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

例7-20 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;
    }

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

例7-21 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);
    }

例7-22 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);

### 7.10.5 コンタクト先の検索を有効にするためのfindContactsメソッドの使用方法

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

`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. Oracle Enterprise Pack for Eclipseで、`findContacts`操作をパレットのDeviceFeaturesデータ・コントロールからドラッグし、リンク、ボタンまたはパラメータ・フォームとしてページ・デザイナにドロップします。

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

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

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

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

例7-23 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`としてメソッドに渡された場合、例外がスローされます。

例7-24 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);
    }

例7-25 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);

### 7.10.6 コンタクト先の更新を有効にするためのupdateContactメソッドの使用方法

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

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

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

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

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

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

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

例7-26と例7-28は、コンタクト先の電話番号を更新する方法を示しています。例7-27と例7-29は、コンタクト先に別の電話番号を追加する方法を示しています。

例7-26 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);

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

例7-27 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);
    }

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

例7-28 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]);

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

例7-29 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分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

### 7.10.7 コンタクト先の削除を有効にするためのremoveContactメソッドの使用方法

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

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

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

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

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

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

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

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

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

例7-30 removeContactのJavaScriptコード例

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

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

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

例7-31 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分に設定されます。デバイス操作に、許可されたタイムアウトよりも長い時間がかかった場合は、タイムアウト・エラーが表示されます。

### 7.10.8 地理的位置を有効にするためのstartLocationMonitorメソッドの使用方法

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

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

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

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

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

- `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}`を使用しないでください。

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

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

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

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

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

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

`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/docs/en/2.2.0/index.html`)を参照してください。

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

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

    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データ・コントロールを使用してデバイスの位置を取得するには、次の手順を実行します。

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

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

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

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

例7-33 地理的位置を使用したデバイスの現在位置の取得(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());

### 7.10.9 ファイルの表示を有効にするためのdisplayFileメソッドの使用方法

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

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

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

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. Oracle Enterprise Pack for Eclipseで、displayFile操作をパレットのDeviceFeaturesデータ・コントロールからドラッグし、リンク、ボタンまたはパラメータ・フォームとしてページ・デザイナにドロップします。

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

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

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

例7-34 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());
            }

### 7.10.10 デバイス・プロパティ

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

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

表7-7 デバイス・プロパティと対応する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`になります。

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

例7-35 デバイス・プロパティにアクセスするための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>

## 7.11 属性の検証

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

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

- 比較バリデータ

- 長さバリデータ

- リスト・バリデータ

- 範囲バリデータ

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

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

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

表7-8 長さの検証

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

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

表7-9 範囲検証

比較タイプ | バイト | Char | Double | Float | Integer | Long | Short
--- | --- | --- | --- | --- | --- | --- | ---
`Between` | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象
`Not Between` | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象

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

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

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

表7-11 リスト検証

比較タイプ | String
--- | ---
`In` | サポート対象
`Not In` | サポート対象

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

表7-12 比較検証

比較タイプ | バイト | Char | Double | Float | Integer | Long | Short | String
--- | --- | --- | --- | --- | --- | --- | --- | ---
`Equals` | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象
`Not Equals` | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象
`Less Than` | サポート対象外 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象外
`Greater Than` | サポート対象外 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象外
`Less Than Equal To` | サポート対象外 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象外
`Greater Than Equal To` | サポート対象外 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象 | サポート対象外

表7-13 比較検証: 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` | サポート対象 | サポート対象 | サポート対象外 | サポート対象外 | サポート対象外 | サポート対象外

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

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

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

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

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

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

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

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

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

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

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

例7-36 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;

        /* --- OEPE 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レイヤーからのレスポンス・メッセージや戻り値とともに、クライアントに(キャッシュするために)戻されます。これにより、Oracle Enterprise Pack for Eclipseではユーザー・インタフェースに対してリフレッシュするイベントと更新の数を減らし、可能なかぎりフレームワークの効率を上げることができます。

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

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

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

例7-37 データ変更イベントの例

    /* 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 OEPE 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;

        /* --- OEPE 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;

        /* --- OEPE 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を使用してデータ変更をユーザー・インタフェースに反映する方法の例を示します。このサンプル・アプリケーションは、「ファイル」→「新」→「MAFサンプル」にあります。

アプリケーション例の詳細は、付録F「モバイル・アプリケーション・フレームワークのアプリケーション例」を参照してください。