コンポジションは、アプリの UI を記述するもので、コンポーザブルを実行することで生成されます。コンポジションは、UI を記述するコンポーザブルで構成されるツリー構造です。
Composition の隣には、セマンティクス ツリーと呼ばれる並列ツリーがあります。このツリーでは、ユーザー補助サービスとテスト フレームワークからわかりやすい別の方法で UI を記述します。ユーザー補助サービスはこのツリーを使用して、特定のニーズを持つユーザーにアプリを説明します。テスト フレームワークは、このツリーを使用してアプリを操作し、アプリに関するアサーションを作成します。セマンティクス ツリーには、コンポーザブルを描画する方法に関する情報は含まれていませんが、コンポーザブルのセマンティックな意味に関する情報が含まれています。
アプリが Compose foundation とマテリアル ライブラリのコンポーザブルと修飾子で構成されている場合は、セマンティクス ツリーが自動的に入力、生成されます。ただし、カスタムの低レベルのコンポーザブルを追加する場合は、そのセマンティクスを手動で指定する必要があります。また、ツリーが画面上の要素の意味を正しく表現していないか、完全に表現していない場合もあります。その場合は、ツリーを調整できます。
たとえば、このカスタム カレンダーのコンポーザブルを考えてみましょう。
この例では、カレンダー全体が 1 つの下位レベルのコンポーザブルとして実装され、Layout
コンポーザブルを使用して Canvas
に直接描画しています。何もしなければ、コンポーザブルのコンテンツとカレンダー内でのユーザーの選択に関する十分な情報がユーザー補助サービスに送信されません。たとえば、ユーザーが 17 の日にちをクリックしても、ユーザー補助機能のフレームワークが受け取るのは、カレンダー全体のコントロールを説明する情報だけです。この場合、TalkBack ユーザー補助サービスは「カレンダー」、あるいは少しだけ優れた「4 月のカレンダー」と通知します。ユーザーは、どの日が選択されたか迷うことになります。このコンポーザブルにアクセスしやすくするには、セマンティック情報を手動で追加する必要があります。
セマンティクス プロパティ
意味論的意味を持つ UI ツリーのすべてのノードには、セマンティクス ツリーの並列ノードがあります。セマンティクス ツリーのノードには、対応するコンポーザブルの意味を伝えるプロパティが含まれています。たとえば、Text
コンポーザブルにはセマンティック プロパティ text
が含まれています。これは、そのコンポーザブルの意味だからです。Icon
には、Icon
の意味をテキストで伝える contentDescription
プロパティが含まれています(デベロッパーが設定している場合)。Compose 基盤ライブラリ上に構築されたコンポーザブルと修飾子では、関連するプロパティがすでに設定されています。必要に応じて、semantics
修飾子と clearAndSetSemantics
修飾子を使用して、プロパティを自分で設定またはオーバーライドします。たとえば、カスタムのユーザー補助アクションをノードに追加する、切り替え可能な要素に代替の状態の説明を提供する、特定のテキスト コンポーザブルを見出しとみなすよう指示する、といったことが可能です。
セマンティクス ツリーを可視化するには、Layout Inspector ツールを使用するか、テスト内で printToLog()
メソッドを使用します。これにより、Logcat 内に現在のセマンティクス ツリーが出力されます。
class MyComposeTest { @get:Rule val composeTestRule = createComposeRule() @Test fun MyTest() { // Start the app composeTestRule.setContent { MyTheme { Text("Hello world!") } } // Log the full semantics tree composeTestRule.onRoot().printToLog("MY TAG") } }
このテストの出力は次のようになります。
Printing with useUnmergedTree = 'false'
Node #1 at (l=0.0, t=63.0, r=221.0, b=120.0)px
|-Node #2 at (l=0.0, t=63.0, r=221.0, b=120.0)px
Text = '[Hello world!]'
Actions = [GetTextLayoutResult]
セマンティクス プロパティがコンポーザブルの意味をどのように伝えるかについて考えてみましょう。Switch
について考えてみましょう。これは次のように表示されます。
この要素の意味を説明するには、次のように言ってみてください。「これはスイッチであり、「オン」状態の切り替え可能な要素です。クリックすると操作できます」
これこそがセマンティクス プロパティが使用される目的です。この Switch 要素のセマンティクス ノードには、Layout Inspector で可視化されたように、次のプロパティが含まれています。
Role
は要素のタイプを示します。StateDescription
は、「オン」状態の参照方法について説明します。デフォルトでは、「On」という言葉のローカライズ版ですが、コンテキストに応じてさらに具体的に(「Enabled」など)できます。ToggleableState
は、Switch の現在の状態です。OnClick
プロパティは、この要素の操作に使用されるメソッドを参照します。セマンティクス プロパティの完全なリストについては、SemanticsProperties
オブジェクトをご覧ください。利用可能なユーザー補助アクションの一覧については、SemanticsActions
オブジェクトをご覧ください。
アプリで各コンポーザブルのセマンティクス プロパティを追跡することで、アプリの可能性が大きく広がります。次のような例が挙げられます。
- TalkBack はプロパティを使用して画面に表示されている内容を読み上げ、ユーザーがスムーズに操作できるようにします。Switch コンポーザブルの場合、TalkBack は「ON: 切り替え、ダブルタップで切り替え」と言います。ユーザーは画面をダブルタップして、スイッチをオフに切り替えられます。
-
テスト フレームワークは、このプロパティを使用してノードを検出し、それを操作して、アサーションを行います。Switch のテストの例を次に示します。
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
マージされたセマンティクス ツリーとマージされていないセマンティクス ツリー
前述のように、UI ツリーの各コンポーザブルには、0 個以上のセマンティクス プロパティが設定されます。コンポーザブルにセマンティクス プロパティが設定されていない場合、コンポーザブルはセマンティクス ツリーの一部として含まれません。このように、セマンティクス ツリーには実際に意味論的意味を含んでいるノードのみが含まれます。ただし、画面に表示される内容の正しい意味を伝えるには、ノードの特定のサブツリーをマージして 1 つのノードとして扱うと便利な場合があります。これにより、子孫の各ノードを個別に扱うのではなく、ノードのセット全体について推論できます。おおまかには、このツリーの各ノードが、ユーザー補助サービスを使用する場合のフォーカス可能な要素を表しています。
このようなコンポーザブルの例は、Button
です。1 つのボタンに複数の子ノードが含まれていても、1 つの要素と見なすことができます。
Button(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = null ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Like") }
セマンティクス ツリーでは、ボタンの子孫のプロパティがマージされ、ボタンがツリー内の単一のリーフノードとして表示されます。
コンポーザブルと修飾子は、Modifier.semantics
(mergeDescendants = true) {}
を呼び出すことで、子孫のセマンティクス プロパティをマージしたいことを示すことができます。このプロパティを true
に設定することで、セマンティクス プロパティのマージが必要であることが示されます。Button
の例では、Button
コンポーザブルは、この semantics
修飾子を含む clickable
修飾子を内部的に使用しています。したがって、ボタンの子孫ノードはマージされます。コンポーザブルのマージ動作を変更するタイミングについて詳しくは、ユーザー補助に関するドキュメントをご覧ください。
Foundation ライブラリと Material Compose ライブラリの修飾子とコンポーザブルには、このプロパティ セットが用意されています。たとえば clickable
修飾子と toggleable
修飾子を使用すると、その子孫が自動的にマージされます。また、ListItem
コンポーザブルも、その子孫をマージします。
ツリーを検査する
セマンティクス ツリーは、実際には 2 つの異なるツリーです。マージ セマンティクス ツリーがあります。これは、mergeDescendants
が true
に設定されている場合に子孫ノードをマージします。「マージされていない」セマンティクス ツリーもあります。このツリーはマージを適用しませんが、すべてのノードを維持します。ユーザー補助サービスでは、マージされていないツリーを使用し、mergeDescendants
プロパティを考慮した独自のマージ アルゴリズムを適用します。テスト フレームワークでは、デフォルトでマージツリーが使用されます。
両方のツリーを printToLog()
メソッドで調べることができます。デフォルトでは、前の例と同様に、マージされたツリーがログに記録されます。マージされていないツリーを出力するには、onRoot()
マッチャーの useUnmergedTree
パラメータを true
に設定します。
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
Layout Inspector では、ビューフィルタで優先するセマンティクス ツリーを選択することで、マージされたセマンティクス ツリーとマージされていないセマンティクス ツリーの両方を表示できます。
Layout Inspector では、ツリー内のノードごとに、マージされたセマンティクスとそのノードに設定されているセマンティクスの両方が、プロパティ パネルに表示されます。
テスト フレームワークのマッチャーでは、デフォルトで、マージされたセマンティクス ツリーが使用されます。
そのため、Button
内のテキストを照合することで、Button
を操作できます。
composeTestRule.onNodeWithText("Like").performClick()
onRoot
マッチャーの場合と同様に、マッチャーの useUnmergedTree
パラメータを true
に設定して、この動作をオーバーライドします。
結合の動作
コンポーザブルで子孫のマージが指示された場合、このマージは正確にはどのように行われるのでしょうか。
各セマンティクス プロパティには、明確なマージ戦略があります。たとえば、ContentDescription
プロパティは、子孫のすべての ContentDescription 値をリストに追加します。SemanticsProperties.kt
で mergePolicy
の実装をチェックし、セマンティクス プロパティのマージ戦略を確認します。プロパティは、親または子の値を受け取る、値をリストまたは文字列にマージする、マージを一切許可せず、代わりに例外をスローする、その他のカスタムマージ戦略を実行できます。
重要な点は、自身で mergeDescendants
= true
を設定した子孫はマージに含まれないことです。次の例をご覧ください。
こちらがクリック可能なリストアイテムです。ユーザーが行をタップすると、記事の詳細ページに移動し、そこで記事を読むことができます。リストアイテム内には、記事をブックマークするボタンがあります。このボタンはネストされたクリック可能な要素を形成するため、マージツリー内で個別にボタンが表示されます。行の残りのコンテンツはマージされます。
セマンティクス ツリーを適応させる
前述のように、特定のセマンティクス プロパティをオーバーライドまたはクリアしたり、ツリーのマージ動作を変更したりできます。これは、独自のカスタム コンポーネントを作成する場合に特に当てはまります。適切なプロパティとマージ動作を設定しないと、アプリにアクセスできなくなることがあります。また、テストが想定とは異なる動作になる可能性もあります。セマンティクス ツリーの調整が必要な一般的なユースケースについては、ユーザー補助に関するドキュメントをご覧ください。テストの詳細については、テストガイドをご覧ください。
参考情報
- ユーザー補助: すべての Android アプリ開発に共通する基本的なコンセプトと手法
- 誰にとっても使いやすいアプリを作成する: アプリのユーザー補助機能を向上させるための重要なステップ
- アプリのユーザー補助機能を改善するための原則: アプリのユーザー補助機能を強化する際に留意すべき主な原則
- ユーザー補助機能のテスト: Android のユーザー補助機能に関するテストの原則とツール
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- Compose のユーザー補助機能
- Compose のマテリアル デザイン 2
- Compose レイアウトのテスト