APLコマンド
Alexa Presentation Language(APL)のコマンドとは、画面上のコンテンツの視覚表現やオーディオ表現を変更するメッセージです。開発者は、発話への応答として、Alexa.Presentation.APL.ExecuteCommands
ディレクティブを使用してコマンドをデバイスに送信します。ボタン押下への応答など、APLドキュメントのイベントハンドラーを使って、コマンドを直接トリガーします。
個別のコマンドについては、APL標準コマンドを参照してください。
コマンドと画面アクション
コマンドがサポートするシーンでのアクションには、次のタイプがあります。
- シーン内をナビゲートする
ScrollView
またはSequence
をスクロールする- スクロールしてコンポーネントを表示する
Pager
で表示されるページを変更する
- 既存シーン内でコンポーネントを変更する
- 入力コントロールを更新して新しい状態を反映する
- 既存コンポーネントの表示を変更する
- 既存のシーン内でビデオクリップを再生/一時停止する
- 音声
- 単一コンポーネントのオーディオコンテンツを読む
- 2つ以上のコンポーネントのオーディオコンテンツを読む
- APL Extensionにコマンドを送信する
コマンド概要
以下の各セクションでは、イベントハンドラーまたは外部で生成されたコマンドによって開始されるコマンドを実行するアルゴリズムについて説明します。
コマンド配列は順に実行されることを想定しています。これらのコマンドは、通常モードまたは高速モードで実行されます。
通常モードの評価
通常モードで実行するコマンドには、名前付きのシーケンサーが必要です。
デフォルトでは、コマンドはMAIN
シーケンサーで実行されます。ただし、以下に該当する場合は除きます。
- コマンドの
sequencer
プロパティに値が含まれている。 - コマンドが、別の
sequencer
を持つコマンドのサブコマンドである。
配列の各コマンドについて、Alexaにより以下の手順が実行されます。
-
コマンドの
when
プロパティを評価します。when
がfalse
に評価された場合は、そのコマンドをスキップし、配列内の次のコマンドに進みます。 -
コマンドの
delay
プロパティの値をチェックします。delay
が0より大きい場合は、指定された時間(ミリ秒単位)だけ一時停止します。 -
コマンドの
sequencer
プロパティの値をチェックします。sequencer
の値が現在のシーケンサーと異なる場合は、このコマンドを指定されたシーケンサーに渡し、配列内の次のコマンドに進みます。 -
コマンドの
type
プロパティを評価します。認識できない値がtype
に含まれている場合は、そのコマンドをスキップし、配列内の次のコマンドに進みます。有効な
type
値には、次のようなものがあります。SendEvent
などのビルトインコマンド。すべての必須コマンドプロパティに値がある場合、コマンドを実行します。一部のコマンドはただちに返されます。このコマンドの実行が終了するまで、ほかのコマンドはシーケンサーを一時停止します。- ユーザー定義コマンド。ユーザー定義コマンドがインフレートしてコマンドの配列になります。通常モードの評価規則に従ってこれらのコマンドを実行し、すべてのコマンドが完了したら続行します。
- Extensionコマンド。コマンドを実行します。一部のextensionコマンドはすぐに返されます。このコマンドの実行が終了するまで、ほかのコマンドはシーケンサーを一時停止します。
高速モードの評価
高速モードで実行されるコマンドには、名前付きのsequencer
はありません。配列の各コマンドについて、以下が順に実行されます。
-
コマンドの
when
プロパティを評価します。when
がfalse
に評価された場合は、そのコマンドをスキップし、配列内の次のコマンドに進みます。 -
コマンドの
sequencer
プロパティの値をチェックします。sequencer
に値が含まれている場合は、このコマンドを適切なシーケンサーに渡し、配列内の次のコマンドに進みます。渡されたコマンドは、高速モードではなく通常モードで実行されます。 -
コマンドの
type
プロパティを評価します。認識できない値がtype
に含まれている場合は、そのコマンドをスキップし、配列内の次のコマンドに進みます。有効な
type
値には、次のようなものがあります。SetValue
など、高速モードをサポートするビルトインコマンド。すべての必須コマンドプロパティに値が含まれる場合、コマンドを実行します。高速モードをサポートするコマンドについては、高速モードのコマンド一覧を参照してください。SendEvent
など、高速モードをサポートしないビルトインコマンド。コマンドをスキップします。- ユーザー定義コマンド。ユーザー定義コマンドがインフレートしてコマンドの配列になります。高速モードの評価規則に従ってこれらのコマンドを実行し、すべてのコマンドが完了したら続行します。
- 高速モードをサポートするextensionコマンド。コマンドを実行します。
- 高速モードをサポートしないextensionコマンド。コマンドをスキップします。
コマンドの評価
コマンドの個々のプロパティはデータバインディングをサポートします。コマンドのデータバインディングコンテキストには、コマンドが定義されているコンテキストが含まれ、さらにevent
プロパティが追加されます。このプロパティには、コマンドがトリガーされた状況に関する情報と、コマンドのターゲットとなったコンポーネントが含まれます。
イベントの内容
コマンドは、そのソースのデータバインディングコンテキストで評価されます。
- ドキュメントのデータバインディングコンテキスト -
viewport
、environment
、ドキュメントのペイロードが含まれます。名前付きリソースとドキュメントのデータバインディングにアクセスできます。 - コンポーネントのデータバインディングコンテキスト - コンポーネント固有のデータバインディングコンテキストです。ドキュメントのデータバインディングコンテキストに加えて、コンポーネント内のすべてのデータバインディングと、コンポーネントのすべての祖先にアクセスできます。
ExecuteCommands
ディレクティブによってデバイスに送信されたコマンド、またはドキュメントのイベントハンドラーによって実行されたコマンドは、ドキュメントのデータバインディングコンテキストで評価されます。APLのイベント(画面タップなど)に応答して発行されたコマンドは、そのコマンドが定義されているコンポーネントのデータバインディングコンテキストで評価されす。
たとえば、次のようなTouchWrapper
があるとします。
{
"type": "TouchWrapper",
"bind": [
"name": "myRandomData",
"value": 24.3
],
"onPress": {
"type": "SendEvent",
"arguments": [ "値は${myRandomData}です" ]
}
}
ユーザーがTouchWrapper
をタップすると、デバイスはスキルにUserEvent
を送信します。このリクエストのarguments
配列には、「値は24.3です」という文字列が含まれます。
eventプロパティ
コマンドが評価されると、ソースのデータバインディングコンテキストにイベントデータが追加されます。このイベントデータには、event
プロパティを通じてアクセスします。
eventプロパティには、event.source
とevent.target
という2つのサブプロパティがあります。
event.source
プロパティには、イベントを発生させたコンポーネントに関してシステムから提供される情報が含まれます。event.target
プロパティには、イベントを受け取るコンポーネントに関してシステムから提供される情報が含まれます(該当する場合)。
すべてのコマンドにevent.target
プロパティがあるとは限りません。たとえば、SendEvent
コマンドにはevent.target
プロパティがありません。event.source
プロパティはすべてのコマンドに存在します。event.source
の詳細はそれぞれ異なる可能性があり、コンポーネントを表すとは限りません。たとえば、ソースがコンポーネントではなく、extensionの場合もあります。
データバインディングコンテキストに追加されるevent
プロパティの形式は次のとおりです。
"event": {
"source": { // 常に存在します
"type": "COMPONENT_TYPE", // コンポーネントの型または"Document"
"handler": "EVENT_HANDLER", // イベントハンドラーの名前(コマンドのメモを参照してください)
"id": "SOURCE_ID", // ソースコンポーネントのID
"uid": "SOURCE_UID", // ソースコンポーネントのUID
... // 追加のソースコンポーネントプロパティ
},
"target": { // コマンドにコンポーネントターゲットがある場合にのみ存在します
"type": "COMPONENT_TYPE", // ターゲットコンポーネントの型
"id": "TARGET_ID", // ターゲットコンポーネントのID
"uid": "TARGET_UID", // ターゲットコンポーネントのUID
... // 追加のターゲットコンポーネントプロパティ
},
... // コマンド固有のプロパティ
}
event.source
プロパティにはhandler
プロパティがありますが、event.target
プロパティにはありません。
以下の表は、さまざまなコンポーネントからレポートされる標準のevent.source
プロパティとevent.target
プロパティの一覧です。特定のコンポーネントのプロパティに関するドキュメント全文については、各コンポーネントのドキュメントを参照してください。
プロパティ | 型 | 説明 | レポート元 |
---|---|---|---|
|
マップ |
データバインディングコンテキスト | |
|
ブール値 |
チェックの状態 | |
|
色 |
現在の色 | |
|
整数 |
現在の再生位置 | |
|
ブール値 |
無効にされた状態 | |
|
整数 |
現在のビデオの長さ(ミリ秒) | |
|
ブール値 |
ビデオが終了している場合はtrue | |
|
ブール値 |
フォーカス状態 | |
|
数値 |
高さ(dp) | |
|
文字列 |
コンポーネントID | |
|
文字列 |
現在の | |
|
ブール値 |
ビデオの現在のミュート状態 | |
|
数値 |
ローカル不透明度(累積値ではない) | |
|
整数 |
現在表示されているページ | |
|
ブール値 |
ビデオが一時停止している場合はtrue | |
|
数値 |
スクロール距離の割合 | |
|
ブール値 |
押された状態 | |
|
文字列 |
表示されるテキスト | |
|
整数 |
トラック数 | |
|
整数 |
現在のトラックのインデックス | |
|
文字列 |
コンポーネントタイプ("Frame"など) | |
|
文字列 |
ランタイムで生成された固有ID | |
|
文字列 |
ソースURL | |
|
数値 |
幅(dp) |
bind
プロパティは、コンポーネントのデータバインディングコンテキストへのアクセスを提供します。次の例は、バインドされた値へのアクセス方法を示しています。IDがMyText
のコンポーネントが、SetValue
コマンドのターゲットです。したがって、event.target.bind
プロパティにはMyText
コンポーネントにバインドされたデータが含まれ、コマンドはその値を使用できます。
[
{
"type": "TouchWrapper",
"onPress": {
"type": "SetValue",
"componentId": "MyText",
"property": "text",
"value": "今日の単語は${event.target.bind.WordOfTheDay}です"
},
"items": [
{
"type": "Text",
"text": "クリックして"
}
]
},
{
"type": "Text",
"bind": [
{
"name": "WordOfTheDay",
"value": "熊"
}
],
"id": "MyText"
}
]
event.source
event
のevent.source
プロパティには、イベントをトリガーした状況に関するメタ情報が含まれます。event.source
はAPLランタイムによって生成され、ドキュメントでこの情報を使用できます。event.source
プロパティには、すべての標準プロパティと以下のプロパティが含まれます。
プロパティ | 型 | 説明 |
---|---|---|
handler | 文字列 | このメッセージを開始したイベントハンドラーの名前です。たとえば、Press やChecked です。 |
value | 任意 | このメッセージを開始したコンポーネントの値です。 |
event.source.value
プロパティは、コンポーネントによって異なります。たとえば、TouchWrapper
の場合、event.source.value
にはコンポーネントのチェック状態が含まれます。ScrollView
の場合、このプロパティにはスクロール位置が含まれます。提供される特定のevent.source.value
プロパティについては、個々のコンポーネントの定義を参照してください。
event.source.value
プロパティは廃止されているため、使用しないでください。代わりに、各コンポーネントに直接ドキュメント化されたプロパティを使用してください。たとえば、TouchWrapper
のチェック状態については、event.source.value
の代わりにevent.source.checked
を使用します。APLの以前のバージョンとの下位互換性のために、event.source
プロパティにはsource
サブプロパティも含まれています。これはtype
プロパティと同じです。つまり、${event.source.source == event.source.type}
となります。event.source.source
プロパティは廃止されました。このプロパティは使用しないでください。
event.target
event.target
プロパティは、イベントを受け取るコンポーネントについての状態情報を提供します。target
の値は、コンポーネントによって異なります。想定される特定のtarget
プロパティについては、個々のコンポーネントの定義を参照してください。
APLの以前のバージョンとの下位互換性のために、イベントのtarget
プロパティはsource
サブプロパティもレポートします。これは、Image
、VectorGraphic
、Video
の各コンポーネントのurl
プロパティと同じです。つまり、${event.target.source == event.target.url}
となります。event.target.source
プロパティは廃止されました。このプロパティは使用しないでください。
評価に関する注意
- ほとんどのコマンドは、ターゲットとして
componentId
を受け取ります。自身をターゲットとするコマンドではcomponentId
を省略できます。 ExecuteCommands
ディレクティブを使用してデバイスにコマンドを送信する場合は、常にcomponentId
を指定する必要があります。- コマンドに
componentId
プロパティが含まれている場合、コマンドはツリー階層のルートからすべてのコンポーネントを深さ優先検索で検索し、値と一致する識別子を持つ最初のコンポーネントをターゲットにします。コマンドが正しいコンポーネントをターゲットにしていることを保証するには、以下のいずれかを実行します。id
プロパティを持つターゲットコンポーネントに一意の値を割り当て、コマンドのcomponentId
をその値に設定します。- 視覚コンテキストで提供される、システムが生成した
uid
プロパティ値を使用します。
- コマンドのデータバインディング式は、コマンドを定義するときではなく、コマンドを実行するときに評価されます。たとえば、
ExecuteCommands
ディレクティブに、グローバルデータバインディングコンテキストを参照するデータバインディング式が含まれていることがあります。これらの式は、デバイスでコマンドが実行されたときに評価されます。コマンドがクラウドで作成されたときではありません。 - イベントハンドラーと
ExecuteCommands
ディレクティブは、実行するコマンドの配列を受け取ります。この配列は、repeatCount
が0のSequential
コマンドのように機能します。
コマンドのシーケンス実行
APLランタイム環境でコマンドが実行されます。ユーザーと環境のアクションが、実行するコマンドをトリガーします。Parallel
コマンドとSequential
コマンドも、実行するコマンドのセットをトリガーします。コマンドのシーケンス実行ルールによって、複数のコマンドが同時に実行される場合の動作が決まります。これらのルールは、コマンドが相互に競合することを防止します。コマンドの定義方法に基づいて、シーケンス実行の動作の一部を制御できます。
以下の用語を使用します。
- リソース – 特定のコマンドによって使用され、ほかのコマンドとは共有できないもの。たとえば、テキストを読み上げるコマンドは"speech"リソースを使用します。特定のコンポーネントに対して一定時間作用するコマンド(
AnimateItem
など)では、そのコンポーネントがリソースと見なされます。 - 通常モード – コマンドを実行する標準の方法です。ほとんどのコマンドは、通常モードで実行されます。通常モードのコマンドは、実行に時間がかかる場合があります。
- 高速モード – コマンドを実行するもう1つの方法です。実行に時間のかかるコマンドが適切でない場合に使用します。高速モードでコマンドを実行する場合、コマンドはすべての遅延を無視し、実行に時間がかかるコマンドをスキップします。
onScroll
などのイベントハンドラーは、高速モードを使用します。これらのイベントハンドラーは、1秒間に複数回起動することがあります。 - シーケンサー – 一度に1つのコマンドを実行できる名前付きのエンティティです。通常モードの各コマンドには、名前付きのシーケンサーがあります。シーケンサーを指定するには、
sequencer
プロパティを設定します。 - サブコマンド – 別のコマンド内に含まれるコマンドです。Sequential、Parallel、OpenURL、Selectなど、サブコマンドを含めることができるコマンドは複数あります。サブコマンドの階層は、「コマンドツリー」と呼ばれることもあります。
コマンドのシーケンス実行ルール
APLランタイムは、コマンドをシーケンス実行する際、次のルールを使用します。
- 通常モードのコマンドはすべて、単一のシーケンサーで実行されます。
そのシーケンサーが既にコマンドを実行している場合、ランタイムは実行中のコマンドを停止し、新しいコマンドを開始します。
- 高速モードのコマンドはシーケンサーを使用しません。
- シーケンサーを明示的に指定するコマンドは、いずれも通常モードで実行されます。
- サブコマンドは、サブコマンドが明示的にシーケンサーを指定しない限り、親コマンドと同じシーケンサー上で実行されます。サブコマンドがシーケンサーを指定している場合、ランタイムはそのコマンドをシーケンサーに引き渡して、親コマンド内でそのコマンドを完了とマークします。
- デフォルトのシーケンサーは
MAIN
です。サブコマンド以外の通常モードコマンドでシーケンサーが明示的に指定されていない場合、そのコマンドはMAIN
シーケンサーで実行されます。通常モードのサブコマンドは、ルール4に従います。 - 画面タッチやキーボード入力など、ユーザーが何らかの物理的なデバイス操作を行うと、
MAIN
sequencer
で実行されているコマンドはすべて停止します。このルールは、音声対話には適用されません。コマンドシーケンスの実行中にユーザーがウェイクワードを発して割り込んでも、MAIN
シーケンサーは続行され、停止しません。 - コマンドの実行が開始されると、既に実行されているコマンドで使用中のリソースがコマンドに必要な場合、ランタイムは実行中のコマンドを停止します。
- 設定変更ハンドラーが実行されると、以前の設定で実行されているコマンドはすべて停止します。
たとえば、ユーザーが画面上のボタンをタッチしたとします。この操作によって、MAIN
シーケンサー上で次のような一連のコマンドが開始されます。
- 画面上の変化をアニメーション化します。
- 特定の位置までリストを下にスクロールします。
- 項目を読み上げます。
ユーザーは、画面をもう一度タッチすることで、この一連のコマンドをいつでも中断できます。ユーザーが画面をタッチすると、MAIN
シーケンサーは現在実行中のコマンドを停止します。
デフォルトのシーケンス実行動作をオーバーライドするには、シーケンサーを指定します。この手法を使うと、コマンドを並行して実行できます。シーケンス実行動作を変更するシナリオには、次のような場合が考えられます。
- 通常は高速モードで実行されるイベントハンドラーを使って、通常モードのコマンドを実行したい場合。たとえば、
ScrollView
コンポーネントのonScroll
イベントハンドラーで、SendEvent
コマンドをトリガーしたいとします。通常、onScroll
ハンドラーは、通常モードのコマンドを無視します。SendEvent
にシーケンサー名を指定すると、onScroll
ハンドラーは通常モードでコマンドを実行するため、SendEvent
が実行されます。 - 通常のユーザー対話動作を上書きして、ユーザーが画面をタッチしてもコマンドの実行を続けたい場合。たとえば、子ども向けのゲームで、
AnimateItem
コマンドを使用して画面上のコンポーネントをアニメーション化しているときに、画面がタッチされてもアニメーションを停止したくない場合があります。 - 複数コンポーネントのアニメーションがトリガーされた場合、新しいコンポーネントを表示して古いコンポーネントを非表示にします。
リソース
各コマンドは、実行の際に1つ以上のシステムリソースを使用できます。シーケンサールールで定義されているように、特定のリソースを使用するコマンドは1回に1つしか実行できません。そのリソースを必要とする新しいコマンドが実行を開始すると、古いコマンドは停止します。
以下は、リソースと、そのリソースを使用するコマンドの一覧です。
- フォアグラウンドオーディオ再生
SpeakItem
SpeakList
PlayMedia
(フォアグラウンドのオーディオトラックを使用する場合)ControlMedia
(フォアグラウンドのオーディオトラックでplay
コマンドを実行する場合)
- バックグラウンドオーディオ再生
PlayMedia
(バックグラウンドのオーディオトラックを使用する場合)ControlMedia
(バックグラウンドのオーディオトラックでplay
コマンドを実行する場合)
- スクロール位置(スクロールコンポーネント、つまり
ScrollView
またはSequence
) - ページャー位置(
Pager
コンポーネント) - コンポーネントアニメーション(コンポーネントプロパティのアニメーション化)
SpeakItem
とSpeakList
は、複数のリソースを使用する可能性があります。
通常モード
APLランタイムは、次のシナリオではコマンドを通常モードで実行します。
- ドキュメントが最初に読み込まれるとき(ドキュメントの
onMount
およびコンポーネントのonMount
)。 - ユーザーが
TouchWrapper
をタッチまたは選択したとき(onPress
)。 - ユーザーが
Pager
上でスワイプし、表示ページを変更したとき(onPageChanged
)。 - Videoコンポーネントがビデオの再生を終了するか、トラックを変更したとき(
onEnd
、onPause
、onPlay
、onTrackUpdate
)。 ExecuteCommands
ディレクティブやExtensionイベントハンドラーなどの外部ソースから新しいコマンドセットを受信したとき。- ユーザーがキーボードのキーを押したとき、または解放したとき(
handleKeyDown
およびhandleKeyUp
)。
デフォルトでは、すべての通常モードコマンドはMAIN
シーケンサーを使用します。
次の例では、onPress
イベントハンドラーにコマンドの配列があります。この配列は、シーケンス実行される配列として扱われます。そのため、SpeakItem
が最初に実行されます。読み上げが完了すると、Scroll
コマンドが実行されます。Scroll
が完了すると、SendEvent
コマンドが実行され、スキルにUserEvent
が送信されます。これらのコマンドはすべて、MAIN
シーケンサーで実行されます。
{
"type": "TouchWrapper",
"items": {
"type": "Text",
"id": "myText",
"speech": "読み上げる値"
},
"onPress": [
{
"type": "SpeakItem",
"componentId": "myText"
},
{
"type": "Scroll",
"componentId": "myScrollRegion",
"distance": 2
},
{
"type": "SendEvent",
"arguments": [
"ボタンが押されたので読み上げました"
]
}
]
}
上の例では、Scroll
コマンドを実行するコンポーネントがスクロールイベントを生成し、コンポーネントのonScroll
イベントハンドラーで定義されたコマンドが呼び出される可能性があります。これらのコマンドは高速モードで実行されるため、現在実行中のコマンドシーケンスに影響しません。
ユーザーがTouchWrapper
を複数回タップすると、コマンドシーケンスは終了し、タップごとに再開します。onPress
ハンドラーを設定してTouchWrapper
を無効にすることもできますが、これは問題の解決にはなりません。ユーザーが画面をタップすると、MAIN
シーケンサー上のコマンドは必ず停止します。
代わりに、コマンドに別のシーケンサーを割り当てることで、コマンドのシーケンス実行が完了することを保証できます。次の例では、TouchWrapper
を無効にして、シーケンスが終了するまでユーザーがシーケンスを再開できないようにするとともに、別のシーケンサーを使用して、画面をタッチしてもコマンドがキャンセルされないようにします。
{
"type": "TouchWrapper",
"items": {
"type": "Text",
"id": "myText",
"speech": "読み上げる値"
},
"onPress": [
{
"type": "Sequential",
"sequencer": "MySequencer",
"commands": [
{
"type": "SetValue",
"property": "disabled",
"value": true
},
{
"type": "SpeakItem",
"componentId": "myText"
},
{
"type": "Scroll",
"componentId": "myScrollRegion",
"distance": 2
},
{
"type": "SendEvent",
"arguments": [
"ボタンが押されたので読み上げました"
]
}
],
"finally": {
"type": "SetValue",
"property": "disabled",
"value": false
}
}
]
}
カスタムの「MySequencer」はMAIN
シーケンサーではないため、画面をもう一度タップしてもコマンドはキャンセルされません。この例では、最後のSetValue
コマンドがfinally
ブロックで実行されることに注意してください。何らかの理由でSequential
が停止しても、finally
で指定されたコマンドは実行されるため、TouchWrapper
が再び有効になります。
上の例は、すべてのコマンドがSequential
のサブコマンドであるため、一連のコマンドとして機能します。シーケンサールールでは、サブコマンドは独自のシーケンサーを指定しない限り、親と同じシーケンサー上で実行されます。
一方、onPress
に割り当てられた以下のコマンド配列は機能しません。ユーザーがこのTouchWrapper
をタップしても、何も起こりません。
{
"type": "TouchWrapper",
"items": {
"type": "Text",
"id": "myText",
"speech": "読み上げる値"
},
"onPress": [
{
"type": "SetValue",
"property": "disabled",
"value": true,
"sequencer": "BadIdea"
},
{
"type": "SpeakItem",
"componentId": "myText",
"sequencer": "BadIdea"
},
{
"type": "Scroll",
"componentId": "myScrollRegion",
"distance": 2,
"sequencer": "BadIdea"
},
{
"type": "SendEvent",
"arguments": [
"ボタンが押されたので読み上げました"
],
"sequencer": "BadIdea"
},
{
"type": "SetValue",
"property": "disabled",
"value": false,
"sequencer": "BadIdea"
}
]
}
onPress
コマンドはデフォルトでMAINシーケンサーで実行されるため、この例は失敗します。別のシーケンサーを指定すると、ハンドラーはコマンドを別のシーケンサーに引き渡して、配列内の「次の」コマンドを実行します。シーケンサーが新しいコマンドを受け取ると、既存のコマンドはすべて停止し、新しいコマンドで置き換えられます。
失敗する例のイベントシーケンスは、次のようになります。
onPress
ハンドラーは、SetValue
をBadIdea
シーケンサーに引き渡します。onPress
ハンドラーはSpeakItem
に進み、すぐにSpeakItem
をBadIdea
に引き渡します。BadIdea
シーケンサーは、SetValue
をキャンセルしてSpeakItem
を開始します。onPress
ハンドラーは、SendEvent
をBadIdea
に引き渡します。BadIdea
シーケンサーは、SpeakItem
をキャンセルしてSendEvent
を開始します。onPress
ハンドラーは、SetValue
をBadIdea
に引き渡します。BadIdea
シーケンサーは、SendEvent
をキャンセルしてSetValue
を開始します。SetValue
コマンドは正常に実行されますが、TouchWrapper
は既に有効になっているため効果はありません。
Sequential
コマンド内に配置し、Sequential
コマンドに新しいシーケンサーを割り当てます。シーケンサーを使って、コマンドを実行するかしないかを切り替えることもできます。次の例では、最初のTouchWrapper
がAnimateItem
コマンドを実行して、ボールの動きをアニメーション化します。2つ目のTouchWrapper
は、AnimateItem
コマンドをキャンセルする新しいコマンドを同じシーケンサーに送信して、アニメーションを強制的に停止させます。
2つ目のTouchWrapper
のonPress
ハンドラーは、BallSequencer
上でIdle
コマンドを実行してAnimateItem
を停止します。Idle
コマンドは何もしませんが、シーケンサー上で現在実行中のコマンドをすべて停止します。ほかのコマンドをBallSequencer
に送信した場合も同じ結果になります。
高速モード
イベントハンドラーの中には、1秒間に何回もトリガーできるものもあります。たとえば、ScrollView
のonScroll
ハンドラーは、スクロール位置が移動するたびに実行されます。同時に、実行に時間がかかるコマンドもあります。たとえば、画面上のリストのスクロールやテキストの読み上げは実行に時間がかかります。
頻繁に起動されるイベントハンドラーから時間のかかるコマンドを実行することによる問題を回避するため、こうしたハンドラーはコマンドを「高速モード」で実行します。
フレームレートで実行されるイベントハンドラーからトリガーされるコマンドのセットはすべて、「高速」モードを使用します。それ以外のコマンドのシーケンスは、すべて通常モードで実行されます。高速モードでは、ハンドラーはコマンドのすべてのdelay
設定を無視し、実行に時間がかかるコマンドをスキップします。イベントハンドラーは次のように動作します。
イベントハンドラー | 動作 |
---|---|
アクション可能なコンポーネントのhandleKeyDown |
通常モード |
アクション可能なコンポーネントのhandleKeyUp |
通常モード |
アクション可能なコンポーネントのonBlur |
高速モード |
アクション可能なコンポーネントのonFocus |
高速モード |
コンポーネントのhandleTick |
高速モード |
コンポーネントのonCursorEnter |
高速モード |
コンポーネントのonCursorExit |
高速モード |
コンポーネントのonMount |
通常モード |
コンポーネントのonSpeechMark |
高速モード |
ドキュメントのhandleTick |
高速モード |
ドキュメントのonConfigChange |
高速モード |
ドキュメントのonDisplayStateChange |
高速モード |
ドキュメントのonMount |
通常モード |
PagerのonPageChanged |
通常モード/高速モード |
ScrollViewのonScroll |
高速モード |
SequenceのonScroll |
高速モード |
タッチ可能なコンポーネントのonDown |
高速モード |
タッチ可能なコンポーネントのonMove |
高速モード |
タッチ可能なコンポーネントのonPress |
通常モード |
タッチ可能なコンポーネントのonUp |
高速モード |
VideoのonEnd |
通常モード/高速モード |
VideoのonPause |
通常モード/高速モード |
VideoのonPlay |
通常モード/高速モード |
VideoのonTimeUpdate |
高速モード |
VideoのonTrackUpdate |
通常モード/高速モード |
ユーザーの操作(TouchWrapper
のタップなど)によって発生する1回限りのイベントは、常に通常モードで実行されます。スクロールなどのイベントは、高速モードで実行されます。いずれのモードでも実行できるイベントは、通常のアクション(ビデオトラックの終了など)、または最終的に高速モードでトリガーされたコマンド(スクロールイベントによるビデオの一時停止など)によってトリガーされます。外部コマンドおよびextensionコマンドは通常モードで実行されます。
各コマンドの説明には、高速モードでの動作が記載されています。次の表は、高速モードの動作をまとめたものです。
コマンド | 高速モードの動作 |
---|---|
AnimateItem |
終了状態にジャンプします。 |
AutoPage |
無視されます。 |
ClearFocus |
実行されます。 |
ControlMedia |
command="play" の場合は無視され、それ以外の場合は実行されます。 |
Finish |
実行されます。 |
Idle |
無視されます。 |
InsertItem |
実行されます。 |
Log |
実行されます。 |
OpenUrl |
無視されます。 |
Parallel |
実行されます。 |
PlayMedia |
無視されます。 |
Reinflate |
実行されます。 |
RemoveItem |
実行されます。 |
Scroll |
無視されます。 |
ScrollToComponent |
無視されます。 |
ScrollToIndex |
無視されます。 |
Select |
実行されます。 |
SendEvent |
無視されます。 |
Sequential |
実行されます。 |
SetFocus |
実行されます。 |
SetPage |
無視されます。 |
SetValue |
実行されます。 |
SpeakItem |
無視されます。 |
SpeakList |
無視されます。 |
通常は高速モードで実行されるコマンドのsequencer
プロパティに値が含まれている場合、そのコマンドは、指定されたシーケンサーで通常モードで実行されます。この機能を使って、高速モードのイベントハンドラーから通常モードのコマンドを実行します。
以下の例は、スクロール表示の位置が移動するたびにその位置をチェックし、スクロール位置が最上部に来たらイベントを送信します。イベントを送信した時刻を記録することでレート制限を適用し、少なくとも1秒経過してから次のイベントが発生するようにします。sequencer
プロパティがない場合、SendEvent
は実行されません。
{
"type": "ScrollView",
"bind": [
{
"name": "LastEventSentTime",
"value": 0
}
],
"onScroll": [
{
"type": "Sequential",
"when": "${event.source.value == 0 && utcTime > LastEventSentTime + 1000}",
"commands": [
{
"type": "SetValue",
"property": "LastEventSentTime",
"value": "${utcTime}"
},
{
"type": "SendEvent",
"arguments": [
"スクロールが最上部に到達しました"
],
"sequencer": "ScrollSender"
}
]
}
]
}
コマンドツリー
コマンドツリーとは、実行される一連のコマンドのことです。コマンドツリーが発生するのは、コマンドが相互にネストできる場合や、あるコマンドによって新しいイベントハンドラーが呼び出される場合です。次のようなプリミティブコマンドに、ネストされたコマンドが含まれます。
OpenURL
Parallel
Select
Sequential
ユーザー定義のコマンドもコマンドのネストをサポートします。
プリミティブコマンドは、イベントハンドラーを呼び出すこともあります。たとえば、Scroll
、ScrollToComponent
、SpeakList
の各コマンドは、いずれもonScroll
イベントハンドラーを呼び出す可能性があります。
コマンドツリーは最後まで実行することも、途中で停止することもできます。ソースがコマンドツリーの実行を開始します。その後、途中で停止されない限り、コマンドツリーは最後まで実行されます。コマンドツリーが停止されると、APLはツリーのすべてのコマンドの実行を即座に停止します。
次の例は、Scroll
、SpeakItem
、PlayVideo
の各コマンドを含むExecuteCommands
ディレクティブのコマンドツリーを示しています。
ExecuteCommand
+ Scroll (distance=-10000) // 上方向にスクロールする
+ onScroll // 一番上までスクロールするたびに呼び出される
+ SetValue (name="opacity", value=event.source.value * 10) // 不透明度を変更する
+ SpeakItem (id) // 項目をスクロールして表示させ、カラオケ状態にする
+ onScroll // スクロールするたびに呼び出される
+ SetValue (name="opacity"....)
+ PlayVideo (synchronously)
+ onStart // 1回呼び出される
+ onTrackUpdate // 新しいトラックが表示されるたびに呼び出される
+ SetValue (name="progress"...) // 進捗バーの表示を更新する
+ onStop // 1回呼び出される
この一連のコマンドは、画面の最上部までスクロールし、項目の1つを読み上げた後、ビデオを再生します。再生中にユーザーが画面をタップすると、実行中の会話、スクロール、ビデオの再生が停止します。
一連のコマンド内の個々のコマンドが、別のsequencer
を指定することもあります。そのコマンドに到達すると、適切なシーケンサーに引き渡され、すぐにシーケンス内の次のコマンドが実行されます。sequencerプロパティが明示的に指定されていないコマンドは、現在のシーケンサー上で実行されます。コマンドが次のシーケンサーで実行されるのは、コマンドの遅延が処理された後に限られます。たとえば、メインのシーケンサー上で実行される次の一連のコマンドを考えてみましょう。
+ Sequential
+ AnimateItem A (delay=100, duration=1000)
+ AnimateItem B (delay=200, sequencer="other", duration=2000)
+ Parallel (delay=200)
+ AnimateItem C (duration=1000)
+ AnimateItem D (sequencer="other", duration=2000)
+ AnimateItem E (delay=100, duration=1000)
次の表は、このコマンドツリーで発生するアクションのタイムラインをまとめたものです。
時間 | アクション |
---|---|
0 |
Sequentialコマンドが開始します |
100 |
AnimateItem Aが開始します |
1100 |
AnimateItem Aが終了します |
1300 |
|
1500 |
Parallelコマンドが開始します |
AnimateItem Cが開始します | |
| |
2500 |
AnimateItem Cが終了します |
Parallelコマンドが終了します | |
2600 |
AnimateItem Eが開始します |
3500 |
|
3600 |
AnimateItem Eが終了します |
Sequentialが終了します |
コマンドツリーが停止すると、APLは次のように想定して、デバイスを一貫した状態にします。
- スクロールを停止します。
- ページめくりをキャンセルして、元のページか次のページのどちらか近い方に移動します。
- 会話を直ちに中止します。
- シーンの変更やレイアウトの構造的な変更の場合は、最終的な位置に「ジャンプ」します。
セレクター
いくつかのコマンドには、コマンドのターゲットコンポーネントを指定するcomponentId
プロパティがあります。コマンドのターゲットとは、コマンドの実行対象となるコンポーネントです。次のコマンドには、それぞれcomponentId
プロパティがあります。
- AnimateItem
- AutoPage
- ControlMedia
- InsertItem
- PlayMedia
- RemoveItem
- Scroll
- ScrollToComponent
- ScrollToIndex
- SendEvent
- SetFocus
- SetPage
- SetValue
- SpeakItem
- SpeakList
componentId
を使用するすべてのAPL Extension
componentId
プロパティは、セレクターの文法に従って解析される文字列セレクターです。セレクターの文法は次のとおりです。
componentId ::= element? modifier*
element ::= uid | id | ":source" | ":root"
modifier ::= modifierType "(" arg? ")"
modifierType ::= ":parent" | ":child" | ":find" | ":next" | ":previous"
arg ::= number | "id=" id | "type=" type
uid ::= ":" [0-9]*
id ::= [_a-zA-Z][_a-zA-Z0-9]*
number ::= "0" | "-"? [1-9][0-9]*
type ::= STRING
セレクター文字列では、
element
と各modifier
の間の空白は無視されます。modifier
には空白を含めないでください。
有効なcomponentId
式の例をいくつか以下に示します。
FOO # id=FOOのコンポーネント
:1003 # 一意のIDが「:1003」のコンポーネント
:source # コマンドを発行したコンポーネント
:root # コンポーネント階層の最上位
:source:child(2) # ソースコンポーネントの3番目の子
:child(2) # ソースコンポーネントの3番目の子(:sourceは暗黙的)
FOO:child(-1) # FOOコンポーネントの最後の子
FOO:parent(1) # FOOの親
FOO:child(id=BAR) # FOOの直接の子のうち、id=BARに最初に一致するもの
FOO:find(id=BAR) # FOOの子孫のうち、id=BARに最初に一致するもの
FOO:child(type=Text) # FOOの直接の子のうち、type=Textに最初に一致するもの
FOO:parent():child(id=BAR) # FOOの兄弟のうち、id=BARに最初に一致するもの
FOO:next(id=BAR) # FOOの後続の兄弟のうち、id=BARに最初に一致するもの
FOO:parent(2) # FOOの親の親
FOO:next(1) # FOOの直後の兄弟
FOO:previous(2) # FOOの2つ前の兄弟
componentId
値は、コマンドを受け取るコンポーネントを定義します。componentId
がコンポーネントに一致しない場合、コマンドは実行されません。componentId
は、省略可能なelement
と、その後に続く0個以上のmodifier
式で構成されます。
APLはセレクター式を順番に評価し、element
から始めて各modifier
を処理します。式に一致する有効なコンポーネントがまったくない場合、式はnullのcomponentId
を返し、そのセレクターを使用するコマンドは実行されません。たとえば、:root:parent():find(id=FOO)
というセレクターは、階層内にid=FOO
のコンポーネントがあったとしても、コンポーネントを返すことはありません。これは、ルートコンポーネントには親が定義されていないためです。
開始要素
element
は、uid
、id
、:source
という文字列、:root
という文字列のいずれかです。コンポーネントの場合、element
のデフォルトは:source
です。このため、コンポーネントのスコープに定義されているイベントハンドラーではelement
を省略できます。ドキュメントレベルのイベントハンドラーでは、element
は省略しないでください。
開始要素:uid
uid
要素は、一致する内部uid
を持つコンポーネントを返します。APLランタイムは、コンポーネントの作成時に、各コンポーネントに固有の文字列IDを割り当てます。この内部IDはドキュメントのスコープ内で一意であり、割り当てられたどのid
とも競合しません。イベントハンドラーでuid
にアクセスして、後で使用するためにバインド変数に保存できます。コンポーネントに割り当てられるuid
値については、特定の値を想定しないでください。ランタイムが異なると、割り当てられる値も異なる可能性があります。
uid
はAPLが割り当てるため、コマンドのターゲットを直接指定するために使用されることはほとんどありません。イベントハンドラーでコンポーネントのuid
値を保存しておくと、後でコンポーネントを変更するときに保存した値を使用できます。以下の例では、TouchWrapper
コンポーネントのリストを表示するSequence
を定義します。これらのコンポーネントのいずれかをタップすると、そのコンポーネントのuid
がLAST_PRESSED
というバインド変数に格納されます。ユーザーがリストをスクロールすると、Sequence
のイベントハンドラーは、LAST_PRESSED
に格納されているuid
を使用してそのコンポーネントをターゲットにし、リストに表示されているテキストを更新します。
開始要素:id
id
要素は、階層内でComponent idプロパティが一致する最初のコンポーネントを返します。id
値は開発者が設定するため、APLはその一意性を保証できません。検索順序は深さ優先ですが、非表示のコンポーネントがどれだけインフレートされているかによって変わります。id
の値が一意であることがわかっている場合は、id
要素を使用してコマンドのターゲットを指定できます。
以下の例では、Container
にTouchWrapper
コンポーネントのリストを表示します。各コンポーネントには、data
配列で指定された色から計算されたid
が設定されています。たとえば、最初のコンポーネントのid
はText_Red
です。ユーザーがコンポーネントをタップすると、SetValue
コマンドによってTouchWrapper
のテキストと色が変更され、隠されていた色が表示されます。
この例が機能するのは、リスト内のそれぞれの色が一意であり、各コンポーネントのid
も一意になるためです。いずれかの色が繰り返されるようにdata
配列を更新すると、SetValue
コマンドはそのid
の最初のインスタンスだけを更新し、後のインスタンスは更新しません。
開始要素::source
:source
要素は、コマンドを発行したコンポーネントを選択します。:source
要素はオプションです。コンポーネントのコンテキストで定義されたイベントハンドラーでは、コンポーネントのターゲットはデフォルトでコンポーネント自体になります。Extensionから発行されるコマンドとドキュメントから発行されるコマンドには、ソースコンポーネントがないことに注意してください。したがって、これらのコマンドでは、ターゲットコンポーネントか:root
のいずれかを指定する必要があります。:source
は、目的のターゲットを明示する場合に使用します。
開始要素::root
:root
要素は、インフレートされたコンポーネント階層の最上位のコンポーネントを選択します。ドキュメントにアタッチされたバインド値を変更するには、:root
を使用します。
以下は、mainTemplate
レベルでbind
が定義されているAPLドキュメントの例です。onMount
イベントハンドラーは、ドキュメントのインフレート時にこの最上位のbind
変数を更新します。ドキュメントを再インフレートするには、ページを更新します。
このコマンドは、有効なcomponentId
がないと失敗することに注意してください。これは、ドキュメントレベルのコマンドハンドラーはすべてターゲットコンポーネントを指定する必要があるためです。
モディファイアー
modifier
式は、element
で識別されるコンポーネントと関係のあるターゲットを選択します。modifier
により、親、兄弟、子を選択したり、コンポーネントの子階層を検索したりできます。モディファイアーを指定することで、一致するコンポーネントを検索する方法が定義されます。
modifier
は、一致条件を指定するオプションのarg
を受け取ります。この引数には、整数またはキーと値のペアを指定できます。有効なキーはid
とtype
です。
モディファイアー::parent
:parent()
モディファイアーは、コンポーネントの親を選択します。:parent()
モディファイアーには、より上位の祖先を選択する数値引数を渡すことができます。この引数は正の整数である必要があります。引数を指定しない場合の結果は:parent(1)
と同じです。次に例を示します。
FOO:parent(1) == FOO:parent() # FOOの親
FOO:parent(2) == FOO:parent():parent() # FOOの親の親
id
とtype
のパターンは、一致する最初の祖先にマッチします。
たとえば、次のようなドキュメント階層があるとします。
TouchWrapper (id=MyButton)
Frame (id=OuterFrame)
Frame (id=InnerFrame)
Text (id=FOO)
この階層に基づいた式の例を以下に示します。
FOO:parent(1) # InnerFrame
FOO:parent(2) # OuterFrame
FOO:parent(id=MyButton) # TouchWrapper
FOO:parent(type=Frame) # InnerFrame
FOO:parent(id=OuterFrame) # OuterFrame
モディファイアー::child
:child()
モディファイアーは、コンポーネントの直接の子のうち、式に一致する最初の子を選択します。
数値引数を指定すると、コンポーネントのN番目の子が返されます。N=0は最初の子、N=1は2番目の子のようになります。負の数値を指定すると、子リストの末尾から選択されます。N=-1は最後の子、N=-2は最後から2番目の子のようになります。引数が空の場合は:child(0)
と同じです。式の例を以下に示します。
FOO:child() # FOOの最初の子
FOO:child(0) # FOOの最初の子
FOO:child(2) # FOOの3番目の子
FOO:child(-1) # FOOの最後の子
名前付き引数(id
またはtype
)は、一致する最初の子を選択します。たとえば、次のようなドキュメント階層があるとします。ここではid
値が一意ではありません。
Sequence (id=FOO)
Container
Text (id=TEXT)
Image (id=IMAGE)
Container
Text (id=TEXT)
Image (id=IMAGE)
この階層に基づいた式の例を以下に示します。
FOO:child(0):child(0) # 最初のContainer内のTextコンポーネント
FOO:child(id=TEXT) # Null(間接的ではない直接の子だけが対象)
FOO:child(0):child(id=TEXT) # 最初のContainer内のTextコンポーネント
FOO:child(1):child(type=Image) # 2番目のContainer内のImageコンポーネント
モディファイアー::find
:find()
モディファイアーは、コンポーネントの子孫のうち、式に一致する最初の子孫を選択します。
数値引数を指定すると、深さ優先検索によってN番目のコンポーネントが返されます。正の数値である必要があります。引数に負の数値または0を指定すると、最初の子が返されます。名前付き引数(id
またはtype
)は、一致する最初の子を選択します。たとえば、次のようなドキュメント階層があるとします。
Sequence (id=FOO)
Container
Text (id=TEXT)
Image (id=IMAGE)
Container
Text (id=TEXT)
Image (id=IMAGE)
この階層に基づいた式の例を以下に示します。
FOO:find(3) # 最初のContainer内のImageコンポーネント
FOO:find(5) # 2番目のContainer内のTextコンポーネント
FOO:find(id=TEXT) # 最初のContainer内のTextコンポーネント
FOO:find(type=Image) # 最初のContainer内のImageコンポーネント
モディファイアー::next
:next()
モディファイアーは、コンポーネントの兄弟を現在のコンポーネントから前方に検索し、式に一致する最初の兄弟を選択します。数値引数を指定すると、コンポーネントのN番目の兄弟が返されます。N=1は最初の兄弟、N=2は2番目の兄弟のようになります。引数が空の場合は:next(1)
と同じです。名前付き引数(id
またはtype
)は、一致する最初の兄弟を選択します。
たとえば、次のような兄弟コンポーネントのリストがあるとします。
TouchWrapper (id=MyButton)
Container (id=FOO)
Frame
Image (id=ImageA)
Video (id=VideoA)
Video (id=VideoB)
このリストに基づいた式の例を以下に示します。
FOO:next() # Frame
FOO:next(2) # Image
FOO:next(9) # Null(兄弟リストの末尾を超過)
FOO:next(id=MyButton) # Null(検索方向が逆)
FOO:next(type=Video) # VideoA
FOO:next(id=VideoB) # VideoB
モディファイアー::previous
:previous()
モディファイアーは、コンポーネントの兄弟を現在のコンポーネントから後方に検索し、式に一致する最初の兄弟を選択します。数値引数を指定すると、コンポーネントのN番目の兄弟が返されます。N=1は最初の兄弟、N=2は2番目の兄弟のようになります。引数が空の場合は:previous(1)
と同じです。名前付き引数(id
またはtype
)は、一致する最初の兄弟を選択します。
たとえば、次のような兄弟コンポーネントのリストがあるとします。
TouchWrapper (id=MyButton)
Frame
Image (id=ImageA)
Video (id=VideoA)
Container (id=FOO)
Video (id=VideoB)
このリストに基づいた式の例を以下に示します。
FOO:previous() # VideoA
FOO:previous(2) # Image
FOO:previous(9) # Null(兄弟リストの末尾を超過)
FOO:previous(id=MyButton) # TouchWrapper
FOO:previous(type=Frame) # Frame
型による一致
コンポーネントがレイアウトの一部である場合、そのコンポーネントは複数の型を持つことがあります。このような状況では、レイアウト名と基盤のコンポーネント型の両方に一致する可能性があります。
以下は、MyText
という名前のレイアウトを含むドキュメントの例です。このレイアウトは、Text
コンポーネントを1つ表示します。mainTemplate
は、MyText
レイアウトのインスタンスを2つ表示します。
onMount
ハンドラーは2つのコマンドを実行します。1つは:root:find(type=MyText)
をターゲットとし、もう1つは:root:find(type=Text)
をターゲットとします。これらのコマンドが実行されると、「Doctor Jane Doe」という値を持つ同じText
コンポーネントが更新されます。 コンポーネントの色がまず青に変わり、次に紫に変わります。ドキュメントを再読み込みして変化を確認するには、ページを更新するか、2つのText
コンポーネントの下にあるTouchWrapper
をクリックしてReinflate
コマンドを実行します。
関連トピック
最終更新日: 2025 年 10 月 09 日