Alexa APL, 第13回　リソースを使用してスタイルの設定（文字装飾をスタイルで実施）

#はじめに
Alexaを搭載した画面付きデバイスの画面レイアウトを作成します。
今回は、文字サイズや文字色などの文字装飾をリソースとスタイルを設定しながら実施します。
対象とするコンポーネントは、Textです。

スタイルは、HTMLで使用されるものとコンセプトは同じで、文字装飾などの設定をひとつのスタイルに定義して、その作成したスタイルをコンポーネントに設定するように使います。
例えば、「スタイルA」として、「文字は太字で色は赤色」と定義して、あるテキストに「スタイルA」を指定すると、そのテキストは「文字は太字で色は赤色」になるということです。

リソースは、その設定値などを定数化するものです。
例えば、「リソースA」として、「#ff0000」と定義して、あるプロパティで「@リソースA」を指定すると、そのプロパティの値は「#ff0000」になるということです。

#今回実施する内容
Textの文字装飾のスタイルを作成し、Textにスタイルを設定します。スタイルを使うときにリソースも使います。

中型デバイス（Echo Show用）
![中型デバイス4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/d0d4c7e5-8962-36ff-bc8d-6372e4775f66.jpeg)

#環境
OS:Windows 10 JP
Alexaスキル言語：Node.js
Editor：Visual Studio Code
APLバージョン：1.0, 1.1

#参考
・[Alexa ハローAPL、Alexaスキルの画面への対応](https://qiita.com/Mount/items/72d9928ff2c0ae5de737)
第1回のAlexa APLの記事です。タイトル通り、ハローAPLを表示させるだけのAPLです。

・[Alexa APL, 第12回　文字サイズ、文字色](https://qiita.com/drafts/e9be488124d6098e19c9/edit
)
第12回のAlexa APLの記事です。TextにcolorやfontFamilyなどを直接設定して動作を確認する記事です。

・[APLドキュメント　リソース](https://developer.amazon.com/ja/docs/alexa-presentation-language/apl-resources.html)
AmazonのAPLドキュメントにあるリソースの説明です。

・[APLドキュメント　スタイル](https://developer.amazon.com/ja/docs/alexa-presentation-language/apl-style-definition-and-evaluation.html)
AmazonのAPLドキュメントにあるスタイルの説明です。

・[APLトレーニングシリーズ第2回: APLドキュメントの概要](https://developer.amazon.com/ja/blogs/alexa/post/0aa0bfac-044f-4c02-879c-92d8cb20768a/jp-alexa-presentation-language-part2)
AmazonのAlexa BlogsにAPLのレイアウト方法の記載がありました。
`when`の使い方についてしれっと重要なことが書かれていました。

#用語
##APL
Alexa Presentation Language
amazonの画面つきのAlexaの画面表示用の言語。
JSONを使用した記載方法です。
インターネットのホームページはHTMLとCSSで作成しますが、AlexaはAPLで作成するということです。

##APLオーサリングツール
APL作成を視覚的に見ながらAPLのJSONファイルを作成するツール。
サンプルテンプレートも準備されており、その中から選択していくだけで、だいたいの画面は作成できる。

##スタイル
文字色、文字サイズ、フォントや背景色などのプロパティを集めた集合のこと。CSSなどで使うスタイルと意味は同じです。

#前提条件
前提条件はとくにないといえばないですが、本まとめを読むにあたり、以下がわかっていることが前提です。
・alexa developer consoleのアカウントがある
・Alexaスキルを開発したことがある
・JSONの記載方法を知っている
・「Alexa ハローAPL、Alexaスキルの画面への対応」、および「Alexa APL, 第12回　文字サイズ、文字色」の記事をみている

#今回使用するレイアウト
今回は、文字サイズや文字色などを変更して、その動きを確認します。
そのために基本となるレイアウトを以下の通り作成します。
「レイアウト画面」はこんな感じです。
![レイアウト.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/f5cc6ac0-a829-a44a-fe2a-0957e13a1d89.jpeg)

1. APLオーサリングツールを起動して「最初から作成」を選択する。
2. 「レイアウト画面」の「mainTemplate」を選択し、「Container」を追加する。
3. 「レイアウト画面」の「Container」を選択し、「Text」を追加する。
4. 「レイアウト画面」の上で作成した「Text」を選択し、「詳細設定画面」で、以下を設定する。

・ text：${payload.sample.sampleArray[0].text}
5. 「レイアウト画面」の「Container」を選択し、「Text」を追加する。
6. 「レイアウト画面」の上で作成した「Text」を選択し、「詳細設定画面」で、以下を設定する。

・ text：${payload.sample.sampleArray[0].detail}
・ paddingLeft：10vw
7. 5, 6を3回繰り返す。sampleArrayの配列部分は1~3に設定する。
※今回は文字サイズや文字色を試すために、あえて配列をSequenceではなく、Textの繰り返しで設定しました。

##JSONデータの作成
以下の通り、JSONデータを作成する。

```json:JSONデータ
{
    "sample": {
        "sampleArray": [
            {
                "text": "コーヒー(coffee)",
                "detail": "コーヒー(coffee)です。",
                "imageURL": "https://coffee_480x480.jpg"
            },
            {
                "text": "カプチーノ(cappuccino)",
                "detail": "カプチーノ(cappuccino)です。<br>ミルク入りのコーヒーです。スチームミルク入りが一般的？",
                "imageURL": "https://cappuccino_480x480.jpg"
            },
            {
                "text": "カフェラテ(latte)",
                "detail": "カフェラテ(latte)です。<br>ミルク入りのコーヒーです。スチームミルク入りが一般的？<br>カプチーノとはミルクの量が違う？",
                "imageURL": "https://latte_480x480.jpg"
            },
            {
                "text": "エスプレッソ(espresso)",
                "detail": "エスプレッソ(espresso)です。<br>加圧して抽出したもので、コーヒーよりもかなり濃いです。",
                "imageURL": "https://espresso_480x480.jpg"
            }
        ]
    }
}
```

##上記で作成した画面の確認
上記で作成した画面をみると、以下のようになります。
中型デバイス（Echo Show）
![中型デバイス1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/71ba0ff8-6630-3eb8-bd5f-3ca406b433a9.jpeg)

#スタイルを使う
##スタイルの作成（赤色＋Bold）
まずは単純なモデルで、「コーヒー」や「カプチーノ」などを赤色の文字にしてBoldにするスタイルを作成します（配色は最悪ですが機能確認のためです）。
APLオーサリングツール上で、APLを開いて、"styles"に直接編集します。
以下のように記載します。

```json:APL
...
    "styles": {
        "menuNameStyle": {
            "values": [
                {
                    "color": "#ff0000",
                    "fontWeight": "bold"
                }
            ]
        }
    },
...
```

スタイルはこれだけです。
簡単に説明します。

- `menuNameStyle`はスタイル名であり、自分で好きなものをつけることができます。
- `values`は配列で、複数記載でき、`when`と組み合わせて状態によって、配列に記載したマッチするスタイルを変更できます。今回は一つだけ設定しました。
- `color`や`fontWeight`でそのスタイルで使用する文字装飾を設定する。

##スタイルの適用
APLオーサリングツール上で、以下のようにします。
1. 「レイアウト画面」の上で作成した1番目の「Text」を選択し、「詳細設定画面」で、以下を設定する。

・ style：menuNameStyle
2. 1の内容を3、5、7番目の「Text」で同様に実施する。

以下のようになります。
中型デバイス（Echo Show）
![中型デバイス2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/f977d0ec-b631-2946-4dcb-64c95f15bcf9.jpeg)

スタイルは、Textに対して、ひとつを設定するだけで複数の文字装飾を一気に適用できるため、個別に設定するよりも手間が省けますし、統一感をもって設定できるため有用です。

##whenをスタイルで使ってみる（要注意）
valuesが配列で設定し、`when`を使って動作をわけられるということで動作を試してみます。
[APLトレーニングシリーズ第2回: APLドキュメントの概要](https://developer.amazon.com/ja/blogs/alexa/post/0aa0bfac-044f-4c02-879c-92d8cb20768a/jp-alexa-presentation-language-part2)に、`when`の使い方についてしれっと重要なことが書かれていました。

>  when プロパティは、リソースやスタイル、レイアウトなどで幅広く利用できます。 いずれも when プロパティが true のとき、そのオブジェクトが採択されますが、リソースやスタイルの配列では true になった後者の定義で上書きされるのに対し、レイアウトのコンポーネント配列では最初に true になったコンポーネントが採択されます。

|要素|説明|
|:--|:--|
|リソース、スタイル|後者の定義で上書き。どんどん継承。|
|レイアウト|trueになった時点のコンポーネントが採択。|

ということで、whenは使う場所によって、適用され方が違うのです。そんなに細かいレベルではなく大きな分類のためなれれば問題ないかと思いますが、最初勘違いしてスタイルが期待通り動かなかったため、記載しました。
![whenの使い方.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/64dc02b7-846d-fb5d-3488-49ff01887efe.jpeg)

では、小型デバイス用（Echo Spot）に、タイトルの色を緑色に変えます。

1. `import`に`alexa-viewport-profiles`を追加する。
2. `menuNameStyle`の`values`内を配列の2番目に`when`を使って、小型デバイス（Echo Spot）用の識別をいれて、色を以下の通り緑色にする。

```json:APL
    ...
    "import": [
        {
            "name": "alexa-viewport-profiles",
            "version": "1.1.0"
        }
    ],
    ...
        "menuNameStyle": {
            "values": [
                {
                    "color": "#ff0000",
                    "fontWeight": "bold"
                },
                {
                    "when": "${viewport.shape == 'round'}",
                    "color": "#00ff00",
                    "fontWeight": "bold"
                }
            ]
        },
```
試した結果は以下です。小型デバイスだけ変更されます。
小型デバイス（Echo Spot）
![小型デバイス1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/42efd39a-ebfb-781a-4728-65e34b174193.jpeg)

##whenを文字装飾内で使ってみる
スタイルを作っていて、Amazonの例を見ていて気付きましたが、whenはcolorなどの設定でも記載ができます。
三項演算子のような記載もできるようです。
では、小型デバイス用（Echo Spot）に、タイトルの色を緑色に変えます。
1. `import`に`alexa-viewport-profiles`を追加する。
2. `menuNameStyle`の`values`内を配列の2番目に`when`を使って、小型デバイス（Echo Spot）用の識別をいれて、色を以下の通り緑色にする。

```json:APL
    ...
    "import": [
        {
            "name": "alexa-viewport-profiles",
            "version": "1.1.0"
        }
    ],
    ...
        "menuNameStyle": {
            "values": [
                {
                    "color": "${viewport.shape == 'round' ? '#00ff00' : '#ff0000'}",
                    "fontWeight": "bold"
                }
            ]
        },
```

上の「whenをスタイルで使ってみる（要注意）」に載せたものと同じ結果が得られます。
今回のように`color`を一か所だけ変更したいといった場合には、三項演算子のような記載もよいですが、感触的にはひとつだけではすまなさそうなため、上位で`when`を使う前項の使い方のほうが良いかなと思いました。
もしくは、スタイル名を完全にデバイスごとに変えて作成するというのもありますが、スタイル名が長くなりそうですし、やはり一つのスタイル内で`when`でわけるのがよさそうかなと思いました。

##スタイルのプロパティ
スタイルのプロパティには、以下があります。

|プロパティ|型 |説明|
|:-:|:-:|:-:|
|description|文字列|スタイルの説明を記載します。自分への備忘。|
|extend|スタイル名の配列|継承元のスタイルの一覧。後のものが上書き。|
|values|valueオブジェクトの配列|スタイルを設定するオブジェクトの配列。|

##スタイルの作成（extend）
`extend`を使って、継承動作を確認します。
APLオーサリングツール上で、APLを開いて、"styles"に直接編集します。
以下のように記載します。

```json:APL
...
    "styles": {
        "menuNameStyle": {
            "values": [
                {
                    "color": "#ff0000",
                    "fontWeight": "bold"
                }
            ]
        },
        "detailBase": {
            "values": [
                {
                    "color": "#00ff00",
                    "fontSize": "24dp"
                }
            ]
        },
        "detailStyle": {
            "extend": [
                "detailBase"
            ],
            "values": [
                {
                    "color": "#0000ff",
                    "fontWeight": "bold"
                }
            ]
        }
    },
...
```

図で説明します。
![extend説明.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/275607c2-8dd1-d870-b748-ae3087ab0e02.jpeg)
図の通りであり、extendは配列を設定できますが、複数設定するとどんどん上書きされるようです。

#リソースを使う
##リソースの作成（薄い黄色）
まずは単純なモデルで、薄い黄色のリソースを作成します。
APLオーサリングツール上で、APLを開いて、"resources"に直接編集します。
以下のように記載します。

```json:APL
...
    "resources": [
        {
            "colors": {
                "lghtYlw": "#ffff99",
                "pnk": "#ff99ff"
            }
        }
    ],
...
```

リソースはこれだけです。
簡単に説明します。

- `colors`はリソースのプロパティ名であり、文字色に関する色を設定するためのものです。
- `lghtYlw`や`pnk`はリソース名であり、自分で好きなものをつけることができます。
- `colors`はオブジェクトであり複数個設定できます。

##リソースの適用
APLオーサリングツール上で、以下のようにします。
1. APLを開いて、`menuNameStyle`の`color`に`@lghtYlw`を設定する。
2. `detailStyle`の`color`に`@pnk`を設定する。
以下のようになります。
中型デバイス（Echo Show）
![中型デバイス4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/d0d4c7e5-8962-36ff-bc8d-6372e4775f66.jpeg)

##リソースのプロパティ
リソースのプロパティには、以下があります。

|プロパティ|型 |説明|
|:-:|:-:|:-:|
|description|文字列|スタイルの説明を記載します。自分への備忘。|
|boolean|ブール値のマップ|ブール値の名前をマッピング。|
|colors|色のマップ|色の名前のマッピング。|
|dimensions|ディメンションのマップ|ディメンションの名前のマッピング。|
|strings|文字列の配列|名前を文字列にマッピング。|
|when|ブール値|trueの場合にこのリソースを適用。|

要するに`boolean`、`colors`、`dimensions`、`strings`の型名を指定して、その型の定数をその配下で定義するということです。
![resourcesの使い方.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/add1fa8f-3270-3240-c45c-5298f6eeb980.jpeg)

descriptionも説明を記載するだけのため、説明を割愛します。

##whenを使ってみる
リソースに`when`をつけてみます。
これをつけることで、同じ名前のリソースを`when`でわけることができます。
では、小型デバイス用（Echo Spot）に、タイトルの色を赤色に変えます。
スタイルのところで作成したAPLに変更を加えます。
1. `resources`の`colors`の下にもうひとつ`colors`を追加する。そのとき、`when`もつける。

```json:APL
...
    "resources": [
        {
            "colors": {
                "lghtYlw": "#ffff99",
                "pnk": "#ff99ff"    <=ピンク色を設定
            }
        },
        {
            "when": "${viewport.shape == 'round'}",
            "colors": {
                "pnk": "#ff0000"   <=赤色を設定
            }
        }
    ],
...
```

これによって、以下のようになります。
小型デバイス（Echo Spot）
![小型デバイス2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/dc17b06b-722c-d642-abc0-fd3535274022.jpeg)

デバイスによるイメージの差分は、スタイルの`when`を用いるか、リソースの`when`を用いるか、どちらでも可能です。
どちらを用いるかは悩ましいですが、感覚的にはスタイルかなと思います。
リソースは値に名前を付けているものですが、名前はその値の特徴を示すものだと思います。
その特徴がデバイスの差分で違うというのは違和感があるため、スタイルでわけたほうが望ましいのかなと思いました。
といいつつも、dimensionsのような数値を相対的に設定する場合（例えば、中型デバイスは32dpだけど小型デバイスは24dpを使うなど）は、リソースでもいいなと思うので、悩ましいですね。

##APLソースコード
出来上がったAPLのフルソースコードとデータJSONを載せます。実際にスキルで表示を試す場合は、Alexa ハローAPL、Alexaスキルの画面への対応を参考しに実施します。

```json:APL
{
    "document": {
        "type": "APL",
        "version": "1.1",
        "settings": {},
        "theme": "dark",
        "import": [
            {
                "name": "alexa-viewport-profiles",
                "version": "1.1.0"
            }
        ],
        "resources": [
            {
                "colors": {
                    "lghtYlw": "#ffff99",
                    "pnk": "#ff99ff"
                }
            },
            {
                "when": "${viewport.shape == 'round'}",
                "colors": {
                    "pnk": "#ff0000"
                }
            }
        ],
        "styles": {
            "menuNameStyle": {
                "values": [
                    {
                        "color": "@lghtYlw",
                        "fontWeight": "bold"
                    },
                    {
                        "when": "${viewport.shape == 'round'}",
                        "color": "#00ff00",
                        "fontWeight": "bold"
                    }
                ]
            },
            "detailBase": {
                "values": [
                    {
                        "color": "#00ff00",
                        "fontSize": "24dp"
                    }
                ]
            },
            "detailStyle": {
                "extend": [
                    "detailBase"
                ],
                "values": [
                    {
                        "color": "@pnk",
                        "fontWeight": "bold"
                    }
                ]
            }
        },
        "onMount": [],
        "graphics": {},
        "commands": {},
        "layouts": {},
        "mainTemplate": {
            "parameters": [
                "payload"
            ],
            "items": [
                {
                    "type": "Container",
                    "items": [
                        {
                            "type": "Text",
                            "style": "menuNameStyle",
                            "text": "${payload.sample.sampleArray[0].text}"
                        },
                        {
                            "type": "Text",
                            "style": "detailStyle",
                            "paddingLeft": "10vw",
                            "text": "${payload.sample.sampleArray[0].detail}"
                        },
                        {
                            "type": "Text",
                            "style": "menuNameStyle",
                            "text": "${payload.sample.sampleArray[1].text}"
                        },
                        {
                            "type": "Text",
                            "style": "detailStyle",
                            "paddingLeft": "10vw",
                            "text": "${payload.sample.sampleArray[1].detail}"
                        },
                        {
                            "type": "Text",
                            "style": "menuNameStyle",
                            "text": "${payload.sample.sampleArray[2].text}"
                        },
                        {
                            "type": "Text",
                            "style": "detailStyle",
                            "paddingLeft": "10vw",
                            "text": "${payload.sample.sampleArray[2].detail}"
                        },
                        {
                            "type": "Text",
                            "style": "menuNameStyle",
                            "text": "${payload.sample.sampleArray[3].text}"
                        },
                        {
                            "type": "Text",
                            "style": "detailStyle",
                            "paddingLeft": "10vw",
                            "text": "${payload.sample.sampleArray[2].detail}"
                        }
                    ]
                }
            ]
        }
    },
    "datasources": {
        "sample": {
            "sampleArray": [
                {
                    "text": "コーヒー(coffee)",
                    "detail": "コーヒー(coffee)です。",
                    "imageURL": "https://coffee_480x480.jpg"
                },
                {
                    "text": "カプチーノ(cappuccino)",
                    "detail": "カプチーノ(cappuccino)です。<br>ミルク入りのコーヒーです。スチームミルク入りが一般的？",
                    "imageURL": "https://cappuccino_480x480.jpg"
                },
                {
                    "text": "カフェラテ(latte)",
                    "detail": "カフェラテ(latte)です。<br>ミルク入りのコーヒーです。スチームミルク入りが一般的？<br>カプチーノとはミルクの量が違う？",
                    "imageURL": "https://latte_480x480.jpg"
                },
                {
                    "text": "エスプレッソ(espresso)",
                    "detail": "エスプレッソ(espresso)です。<br>加圧して抽出したもので、コーヒーよりもかなり濃いです。",
                    "imageURL": "https://espresso_480x480.jpg"
                }
            ]
        }
    }
}
```

#おわりに
リソースとスタイルを使った文字装飾について記載しました。