APLスタイルの定義と評価



APLスタイルの定義と評価

スタイルとは、一連の視覚プロパティを定義する名前付きエンティティのことです。スタイルを使用すると、コンポーネントのプロパティを一貫して設定できます。スタイルでは、条件付きロジックを含めたり、コンポーネントの状態を使用したりできます。たとえば、コンポーネントの状態がcheckedに設定されているかどうかに応じて、スタイルでテキストの色を変更できます。

スタイルの定義

スタイルの定義では、スタイルの名前、継承元となる1つまたは複数の親スタイルのリスト、条件付きで適用されるプロパティ定義の順序付きリストを指定します。Textコンポーネントでの使用に適した「title」というスタイル定義を以下に示します。ここでは、フォントのサイズと色が設定されています。色は、Textコンポーネントの状態と現在のテーマに応じて変更されます。

  "styles": {
    "baseText": {
      "values": [
        {
          "fontFamily": "Amazon Ember",
          "color": "${viewport.theme == 'dark' ? 'white' : 'black' }"
        },
        {
          "when": "${state.karaoke}",
          "color": "blue"
        }
      ]
    },
    "title": {
      "extends": "baseText",
      "values": [
        {
          "fontWeight": 700,
          "fontSize": "${viewport.height > 400 ? 30 : 25}"
        }
      ]
    }
  }

たとえば、viewportが大型、ダークテーマ、状態がfocusedに設定されているデバイスの場合、スタイルtitleの評価結果は次のようになります。

{
    "fontFamily": "Amazon Ember Display",
    "color": "blue",
    "fontWeight": 700,
    "fontSize": 30
}
スタイルで設定可能なプロパティ

スタイルでは、すべてのコンポーネントプロパティを設定できるわけではありません。すべてのコンポーネントのスタイル設定可能プロパティについては、スタイル設定可能プロパティを参照してください。個々のコンポーネントについては、利用可能なコンポーネントプロパティの表を参照してください。該当するコンポーネントのプロパティのうち、スタイルで設定可能なものについては、表の「スタイル設定」列に「〇」が記載されています。たとえばTextプロパティの表では、text以外のすべてのプロパティがスタイル設定可能です。

定義のプロパティ

スタイルの定義ごとに、次のプロパティがあります。

プロパティ 必須 説明
description 文字列 このスタイルの説明です。
extend スタイル名の配列 このスタイルの継承元となるスタイルを一覧にしたものです。順序が後の項目が、順序が先の項目を上書きするため、順序が重要です。
values valueオブジェクトの配列 オブジェクトの配列です。 

extend配列には、このスタイルの継承元となるスタイルが順番に並んだ一覧が含まれます。accessプロパティは、定義パッケージ以外でこのスタイルを使用できるかどうかを制御します。

values配列には、適用するvalueオブジェクトが順番に並んだ一覧が含まれます。各valueオブジェクトは、次の形式になります。

{
  "when": EXPRESSION,
  NAME: VALUE,
  NAME: VALUE,
  :
}

whenプロパティはオプションであり、定義されていない場合のデフォルト値はtrueです。定義されたプロパティは、有効なスタイル設定可能プロパティの名前である必要があります(無効な名前は無視されます)。when句のデータバインディングコンテキストには、viewport、環境、リソースの定義のほか、ドキュメント内またはインポートされたパッケージ内の前の方に表示されるスタイルが含まれています。

コンポーネントの状態

各コンポーネントには状態があります。状態とは、定義済みのブール値プロパティのコレクションで、ユーザーがAPLドキュメントを操作すると変更されます。次の表に状態プロパティの一覧を示します。

プロパティ 定義
checked コンポーネントがチェックされているか、オンになっています。
disabled コンポーネントが無効になっています(多くのコンポーネントに「disabled」プロパティがあります)。 
focused コンポーネントがキーボード入力でフォーカスされた状態になっています。
karaoke コンポーネントは音声コマンドによって読み上げられています。
karaokeTarget コンポーネント内の要素が、現在の音声コマンドの特定のターゲットになっています。
pressed マウスまたはタッチパネルによる入力がアクティブになっています。

各コンポーネントの状態は、ブール値のプロパティです。コンポーネントの状態は、すべてのコンポーネントに適用可能です。たとえば、checked状態とdisabled状態がContainerに影響を及ぼす可能性は低いですが、Containerにはこれらの状態があります。

スタイルが評価されると、コンポーネントの状態はstateキーワードの下のブール値のマップとして、データバインディングコンテキストに表示されます。たとえば、checked状態には${state.checked}としてアクセスできます。

コンポーネントの状態はコンポーネントの作成時に設定され、ユーザーが画面に触れたり、キーボードやコマンドを使用したりするなどの操作に応じて変化します。たとえば、TouchWrapperコンポーネントは、pressed状態を設定すると、タッチやキーボードによるイベントに反応します。コンポーネントの状態とスタイルの組み合わせにより、コンポーネントの外観を変更します。

コンポーネントの状態を変更しても、inheritParentStateプロパティを設定しない限り、子コンポーネントの状態は変更されません。以下は、押されると色が変わるTextコンポーネントのサンプルです。TextコンポーネントはTouchWrapperでラップされており、TouchWrapperの状態を継承するように設定されています。

スタイルの定義:

{
  "textStylePressable": {
    "values": [
      {
        "color": "white"
      },
      {
        "when": "${state.pressed}",
        "color": "green"
      }
    ]
  }
}

レイアウトでのスタイルの使用:

{
   "type": "TouchWrapper",
   "item": {
     "type": "Text",
     "inheritParentState": true,
     "style": "textStylePressable",
     "text": "Push Me!"
   }
}

コンポーネントの状態に関する以下のルールに注意してください。

  1. checked状態とdisabled状態を変更するには、SetValueコマンドを使用してください。これらの状態の変更にはこの方法をお勧めします。
  2. SetStateコマンドでもchecked状態とdisabled状態を変更できますが、SetValueを使用することをお勧めします。
  3. disabledコンポーネントにはpressed状態とfocused状態を設定できません。コンポーネントにdisabled状態を設定すると、pressed状態とfocused状態が解除されます。

コンポーネントの状態の管理

次の表は、状態の値と変更についてまとめたものです。

概念 checked disabled focused karaoke karaokeTarget pressed
プロパティによる初期化 -- -- -- --
SetValueによる制御 -- -- -- --
SetStateによる制御 -- -- -- --
SetFocusによる制御 -- -- -- -- --
ClearFocusによる制御 -- -- -- -- --
タッチによる変更 -- -- -- -- --
キーボードイベントによる変更 -- -- -- --
SpeakItem/Listによる制御 -- -- -- --
event.source.valueでの表示 -- -- -- -- --
視覚コンテキストでの表示 -- -- --

次のセクションでは、状態の動作について詳しく説明します。

checked

checked状態はコンポーネントによって使用され、チェックボックスやラジオボタンのように動作します。コンポーネントのchecked状態は制御できます。次のような特徴があります。

  • コンポーネントの定義でchecked状態を初期化できます。
  • SetValueコマンドまたはSetStateコマンドを使用して、動的にcheckedを変更できます。SetValueコマンドの使用をお勧めします。
  • 視覚コンテキストはchecked状態をレポートします。

TouchWrapperコンポーネントは、押された場合に、event.source.valueプロパティでコンポーネントのchecked状態をレポートします。通常、チェックボックスを切り替えるために使用されます。以下に例を示します。

{
  "onPress": {
    "type": "SetVaue",
    "state": "checked",
    "value": "${!event.source.value}"
  }
}

画面上のコンポーネントにタッチしても、checked状態の設定や解除は行われません。チェック状態を変更するには、SetValueコマンドを使用します。

disabled

disabled状態は、通常、タッチまたはキーボードのイベントに応答するコンポーネントが利用できないことを示します。

  • コンポーネントの定義でdisabled状態を初期化できます。
  • SetValueコマンドまたはSetStateコマンドを使用して、disabledを動的に変更できます。SetValueコマンドの使用をお勧めします。
  • 視覚コンテキストはdisabled状態をレポートします。

disabled状態のコンポーネントをフォーカスしたり、押したりすることはできません。コンポーネントにdisabled状態を設定すると、pressed状態とfocused状態が解除されます。

子を含むコンポーネントの状態をdisabledにしても、親の状態を継承しない限り、子コンポーネントは影響を受けません。たとえば、有効なTouchWrapperがdisabled状態のContainer内に配置されていても、通常どおりに表示されてタッチイベントに応答します。

focused

focused状態は、コンポーネントがランタイムで有効なフォーカスを当てられているかどうかを反映します。focused状態は、キーボードで制御できる、アクション可能なコンポーネントにのみ適用されます。アクション可能なコンポーネントは、 TouchWrapperPagerScrollViewSequenceです。

  • コンポーネントが初期化されると、focused状態はデフォルトでfalseになります。
  • SetFocusコマンドとClearFocusコマンドを使用して、focusedを動的に変更できます。
  • 視覚コンテキストはfocused状態をレポートします。

focused状態は、キーボードイベントによるユーザーの操作、またはSetFocusコマンドおよびClearFocusコマンドによってのみ制御できます。SetStateコマンドとSetValueコマンドを使用してfocused状態を変更することはできません。

focused状態をtrueに設定できるのは、一度に1つの親コンポーネントだけです。inheritParentStateプロパティが設定された子コンポーネントには、視覚的に表示するためにfocused状態が割り当てられますが、キーボードイベントを受け取ることはできません。

karaoke/karaokeTarget

karaoke状態は、Alexaによって読み上げられている、または記述されているコンポーネントをハイライトします。karaokeTarget状態は、Alexaによって読み上げられている、または記述されているコンポーネントのサブセクションをハイライトします。

karaoke状態は、SpeakListコマンドとSpeakItemコマンドで制御されます。SetStateコマンドとSetValueコマンドを使用してkaraoke状態を変更することはできません。

karaokeスタイル設定の定義方法について詳しくは、Karaokeスタイルの計算を参照してください。

Pressed

pressed状態は、ユーザーによって直接タッチされているか、キーボードの「Enter」キーを押して間接的にタッチされているコンポーネントをハイライトします。pressed状態はTouchWrapperにのみ適用されます。

TouchWrapperコンポーネントは、ユーザーがコンポーネントにタッチするとpressed状態を設定します。ユーザーがタッチを離すか、コンポーネントの範囲外にドラッグすると、pressed状態を解除します。ユーザーがドラッグアウトしてから再びドラッグインすると、pressed状態に戻ります。

TouchWrapperonPressイベントハンドラーは、ユーザーが同じコンポーネントを押して離した場合にのみ起動します。onPressコマンドは、pressed状態が解除された後に起動します。

スタイルの評価

各コンポーネントは、名前付きのスタイルを明示的または非明示的に参照します(デバイスのランタイムには、コンポーネントごとにデフォルトのスタイルがあります)。スタイルの評価は、限定されたデータバインディングのコンテキストで発生し、viewportstate、およびresourceの定義のみを使用します。

スタイルの計算結果は、viewportresourcestateの相関関係で表されます。計算アルゴリズムは、おおよそ次の擬似コードに相当します。

function _calcInternal(style, context):
  result = {};

  // extend配列のチェック
  foreach style.extend as name:
    result += _calcInternal( getStyleByName(name), context)

  // values配列のチェック
  foreach values as value:
    if evaluate(value.when):
      result += evaluateEach(value)

  return result

function calculateStyle(style, context, state):
  workingContext = extendContext(context, { state: state })
  return _calcInternal(style, workingContext)

最初にextend配列、次にvaluesのブロックを(順番に)計算します。

Karaokeスタイルの計算

karaoke状態のプロパティは、SpeakItemコマンドとSpeakListコマンドで設定されます。通常、karaoke状態は他の状態と同じように動作し、すべてのスタイル設定可能プロパティを自由に適用できます。karaokeモードでは、以下の2点を考慮する必要があります。

まず、SpeakItemコマンドとSpeakListコマンドを実行すると、karaokeスタイル設定が適用される前に、コンテンツがスクロール表示されます。karaokeスタイル設定によって画面表示が大幅に変更される場合、スクロールの終了後に予期せずコンテンツが移動することがあります。そのため、画面上のコンポーネントの位置が変わるような変更は最小限にとどめてください。これには、コンポーネント、テキスト、フォント、境界線のサイズ変更も含みます。

次に、SpeakItemプロパティは、karaoke状態でテキスト行を個別に「ハイライト」するように設定できます。このハイライトモードが選択されると、テキストのブロックはkaraoke状態に割り当てられます。読み上げられているテキスト行には、karaoke状態とkaraokeTarget状態の両方が割り当てられます。karaokeTarget状態では、ハイライトされたテキストの色のみを変更できます。

次に例を示します。

{
  "styles": {
    "karaokeText": {
      "values": [
        {
          "color": "#fafafa",
          "fontWeight": 300
        },
        {
          "when": "${state.karaoke}",
          "color": "blue",
          "fontWeight": 700
        },
        {
          "when": "${state.karaokeTarget}",
          "color": "white",
          "fontWeight": 100
        }
      ]
    }
  }
}

この例では、通常のテキストの色は#FAFAFA(薄いグレー)で、fontWeightは300です。1行ずつハイライトするように設定したSpeakItemコマンドの実行中には、テキストブロック全体のfontWeightが700になります。ハイライトされる行は白(karaokeTarget=Trueを指定)、他の行は青で表示されます。現在のAPLではハイライトされた行の色しか変更できないため、karaokeのターゲット行のfontWeight設定は無視されました。

1行ずつハイライトするように設定しても、常に1行以上のテキスト行がハイライト表示されるとは限りません。SpeakListコマンドプロパティのminimumDwellTimeが読み上げ時間よりも長く設定されている場合、行がハイライトされない時間が発生します。ただし、コンポーネントには引き続きkaraoke状態が設定されています。上記の例に従うと、この「余分」な時間の間、すべてのテキスト行は青で表示されてfontWeightは700になります。

最後に、1行ずつのハイライトではなく、ブロックをハイライトするモードが選択されている場合、すべてのテキストは青で表示されます。ブロックをハイライトするモードでは、karaokeTarget状態は有効になりません。