タイルのバージョニング

Wear OS デバイスでは、タイルは独立したバージョン管理を行う 2 つの主要なコンポーネントによってレンダリングされます。アプリのタイルがすべてのデバイスで正しく機能するようにするには、この基盤となるアーキテクチャを理解することが重要です。

  • Jetpack タイル関連ライブラリ: これらのライブラリ(Wear Tiles Wear ProtoLayout など)はアプリに埋め込まれており、デベロッパーが バージョンを管理します。アプリはこれらのライブラリを使用して、 TileBuilder.Tileオブジェクト(タイルのデータ構造)を構築します。 これはシステムのonTileRequest()呼び出しに応答します。
  • ProtoLayout レンダラ: このシステム コンポーネントは、ディスプレイに Tile オブジェクトをレンダリングし、ユーザー操作を処理します。レンダラのバージョンはアプリ デベロッパーが管理するものではなく、ハードウェアが同じデバイスでも異なる場合があります。

タイルの外観や動作は、アプリの Jetpack Tiles ライブラリのバージョンと、ユーザーのデバイスの ProtoLayout レンダラのバージョンの両方によって異なります。たとえば、あるデバイスでは回転や心拍数データの表示がサポートされていても、別のデバイスではサポートされていない場合があります。

このドキュメントでは、Tiles ライブラリと ProtoLayout レンダラのさまざまなバージョンとアプリを互換性を持たせる方法について説明します。また、Jetpack ライブラリの新しいバージョンに移行する方法についても説明します。

互換性を考慮する

さまざまなデバイスで正しく機能するタイルを作成するには、機能のサポートの違いを考慮する必要があります。これを行うには、ランタイム時にレンダラの機能を検出する、組み込みのフォールバックを提供するという 2 つの主な方法があります。

レンダラの機能を検出する

特定のデバイスで使用できる機能に基づいて、タイルのレイアウトを動的に変更できます。

レンダラのバージョンを検出する

  • onTileRequest() メソッドに渡される DeviceParameters オブジェクトの getRendererSchemaVersion() メソッドを使用します。 このメソッドは、デバイスの ProtoLayout レンダラのメジャー バージョン番号とマイナー バージョン番号を返します。
  • 検出されたレンダラのバージョンに基づいてタイルのデザインや動作を調整するには、onTileRequest() 実装で条件ロジックを使用します。

@RequiresSchemaVersion アノテーション

  • ProtoLayout メソッドの @RequiresSchemaVersion アノテーションは、そのメソッドがドキュメントに記載されているとおりに動作するために必要な最小レンダラ スキーマ バージョンを示します(例)。
    • デバイスで使用可能なレンダラのバージョンよりも高いレンダラのバージョンを必要とするメソッドを呼び出しても、アプリがクラッシュすることはありませんが、コンテンツが表示されない、または機能が無視される可能性があります。

バージョン検出の例

val rendererVersion = requestParams.deviceConfiguration.rendererSchemaVersion

val arcElement =
    // DashedArcLine has the annotation @RequiresSchemaVersion(major = 1, minor = 500)
    // and so is supported by renderer versions 1.500 and greater
    if (
        rendererVersion.major > 1 ||
        (rendererVersion.major == 1 && rendererVersion.minor >= 500)
    ) {
        // Use DashedArcLine if the renderer supports it …
        DashedArcLine.Builder()
            .setLength(degrees(270f))
            .setThickness(8f)
            .setLinePattern(
                LayoutElementBuilders.DashedLinePattern.Builder()
                    .setGapSize(8f)
                    .setGapInterval(10f)
                    .build()
            )
            .build()
    } else {
        // … otherwise use ArcLine.
        ArcLine.Builder().setLength(degrees(270f)).setThickness(dp(8f)).build()
    }

フォールバックを提供する

一部のリソースでは、ビルダーでフォールバックを直接定義できます。これはレンダラのバージョンを確認するよりも簡単なことが多く、可能な場合はこの方法をおすすめします。

一般的なユースケースは、Lottie アニメーションのフォールバックとして静止画像を提供することです。デバイスが Lottie アニメーションをサポートしていない場合は、代わりに静止画像がレンダリングされます。

val lottieImage =
    ResourceBuilders.ImageResource.Builder()
        .setAndroidLottieResourceByResId(
            ResourceBuilders.AndroidLottieResourceByResId.Builder(R.raw.lottie)
                .setStartTrigger(createOnVisibleTrigger())
                .build()
        )
        // Fallback if lottie is not supported
        .setAndroidResourceByResId(
            ResourceBuilders.AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.lottie_fallback)
                .build()
        )
        .build()

さまざまなレンダラのバージョンでテストする

さまざまなレンダラのバージョンでタイルをテストするには、さまざまなバージョンの Wear OS エミュレータにデプロイします。(実際のデバイスでは、ProtoLayout レンダラのアップデートは Google Play ストアまたはシステム アップデートによって配信されます。特定のレンダラのバージョンを強制的にインストールすることはできません)。

Android Studio のタイルプレビュー機能は、コードが依存する Jetpack ProtoLayout ライブラリに埋め込まれたレンダラを使用します。そのため、タイルをテストする際に、さまざまなバージョンの Jetpack ライブラリに依存することもできます。

Tiles 1.5 / ProtoLayout 1.3(Material 3 Expressive)に移行する

Jetpack Tile ライブラリを更新して、最新の機能強化(タイルをシステムとシームレスに統合するための UI の変更など)を活用します。

Jetpack Tiles 1.5 と Jetpack ProtoLayout 1.3 では、いくつかの重要な改善と変更が導入されています。次のようなものがあります。

  • UI を記述するための Compose のような API。
  • Material 3 Expressive コンポーネント。ボトムシートのボタンや、Lottie アニメーション、グラデーション タイプ、新しい円弧線のスタイルなど、ビジュアルの強化をサポートしています。注: これらの機能の一部は、新しい API に移行しなくても使用できます。

推奨事項

タイルを移行する際は、次の推奨事項に従ってください。

  • すべてのタイルを同時に移行します。アプリ内でタイルのバージョンを混在させないでください。マテリアル 3 コンポーネントは別のアーティファクト(androidx.wear.protolayout:protolayout-material3)に存在するため、技術的には同じアプリで M2.5 タイルと M3 タイルの両方を使用できますが、どうしても必要な場合(アプリに多数のタイルがあり、一度に移行できない場合など)を除き、この方法を使用しないことを強くおすすめします。
  • Tiles UX ガイダンスを採用します。タイルは構造化され、テンプレート化されている ため、既存のサンプルのデザインを独自のデザインの出発点 として使用してください。
  • さまざまな画面サイズとフォントサイズでテストします。タイルは情報密度が高いため、テキスト(特にボタンに配置されている場合)がオーバーフローしたり、クリップされたりする可能性があります。これを最小限に抑えるには、組み込みのコンポーネントを使用し、大幅なカスタマイズは避けてください。Android Studio のタイルプレビュー機能と、複数の実際のデバイスを使用してテストします。

移行プロセス

タイルを移行する手順は次のとおりです。

依存関係を更新する

まず、build.gradle.kts ファイルを更新します。バージョンを更新し、 protolayout-material 依存関係を protolayout-material3 に変更します。

// In build.gradle.kts

//val tilesVersion = "1.4.1"
//val protoLayoutVersion = "1.2.1"

// Use these versions for M3.
val tilesVersion = "1.5.0"
val protoLayoutVersion = "1.3.0"

 dependencies {
     // Use to implement support for wear tiles
     implementation("androidx.wear.tiles:tiles:$tilesVersion")

     // Use to utilize standard components and layouts in your tiles
     implementation("androidx.wear.protolayout:protolayout:$protoLayoutVersion")

     // Use to utilize components and layouts with Material Design in your tiles
     // implementation("androidx.wear.protolayout:protolayout-material:$protoLayoutVersion")
     implementation("androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion")

     // Use to include dynamic expressions in your tiles
     implementation("androidx.wear.protolayout:protolayout-expression:$protoLayoutVersion")

     // Use to preview wear tiles in your own app
     debugImplementation("androidx.wear.tiles:tiles-renderer:$tilesVersion")

     // Use to fetch tiles from a tile provider in your tests
     testImplementation("androidx.wear.tiles:tiles-testing:$tilesVersion")
 }

TileService はほとんど変更されない

この移行の主な変更は、UI コンポーネントに影響します。そのため、リソース読み込みメカニズムを含む TileService 実装は、ほとんど変更する必要がありません。

主な例外は、タイルのアクティビティ トラッキングです。アプリで onTileEnterEvent() または onTileLeaveEvent() を使用している場合は、 onRecentInteractionEventsAsync() に移行することをおすすめします。API 36 以降では、これらのイベントはバッチ処理されます。

レイアウト生成コードを調整する

ProtoLayout 1.2(M2.5)では、onTileRequest() メソッドは TileBuilders.Tile を返します。このオブジェクトには、 TimelineBuilders.Timeline など、さまざまな要素が含まれていました。このオブジェクトには、タイルの UI を記述する LayoutElement が含まれていました。

ProtoLayout 1.3(M3)では、データ構造とフロー全体は変更されていませんが、LayoutElement は Compose にインスパイアされたアプローチで構築されるようになりました。レイアウトは定義されたスロット に基づいており、上から順に titleSlot(省略可。通常はメインタイトルまたはヘッダー用)、mainSlot(必須。コア コンテンツ用)、bottomSlot(省略可。多くの場合、ボトムシートのボタンなどのアクションや、短いテキストなどの補足情報用)です。このレイアウトは primaryLayout() 関数によって構築されます。

mainSlot、titleSlot、bottomSlot を表示するタイルのレイアウト
図 1.: タイルのスロット。
M2.5 と M3 のレイアウト関数の比較

M2.5

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters
) =
    PrimaryLayout.Builder(deviceConfiguration)
        .setResponsiveContentInsetEnabled(true)
        .setContent(
            Text.Builder(context, "Hello World!")
                .setTypography(Typography.TYPOGRAPHY_BODY1)
                .build()
        )
        .build()

M3

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
) =
    materialScope(context, deviceConfiguration) {
        primaryLayout(mainSlot = { text("Hello, World!".layoutString) })
    }

主な違いを以下に示します。

  1. ビルダーの廃止 。マテリアル UI コンポーネントの以前のビルダー パターンは、より宣言的な Compose にインスパイアされた構文に置き換えられました。(String、Color、Modifiers などの UI 以外のコンポーネントにも、新しい Kotlin ラッパーが用意されています)。
  2. 初期化関数とレイアウト関数の標準化 。M3 レイアウトは、 標準化された初期化関数と構造関数(materialScope() と primaryLayout())に依存しています。これらの必須関数は、M3 環境(テーマ設定、materialScope を使用したコンポーネント スコープ)を初期化し、プライマリ スロットベースのレイアウト(primaryLayout を使用)を定義します。どちらもレイアウトごとに 1 回だけ呼び出す必要があります。

テーマ設定

マテリアル 3 では、ダイナミック カラーや、タイポグラフィとシェイプのオプションの拡張など、テーマ設定にいくつかの変更が加えられています。

色

Material 3 Expressive の際立った機能は「ダイナミック テーマ設定」です。この機能を有効にしたタイル(デフォルトでオン)は、システム提供のテーマで表示されます(利用可能かどうかはユーザーのデバイスと構成によって異なります)。

M3 のもう 1 つの変更点は、カラートークンの数が 4 から 29 に増えたことです。新しいカラートークンは ColorScheme クラスにあります。

タイポグラフィ

M2.5 と同様に、M3 は事前定義されたフォントサイズ定数に大きく依存しています。フォントサイズを直接指定することはおすすめしません。これらの定数は Typography クラスにあり、より表現力豊かな オプションが用意されています。

詳しくは、タイポグラフィのドキュメントをご覧ください。

シェイプ

ほとんどの M3 コンポーネントは、色だけでなくシェイプのディメンションも変更できます。

シェイプが full の textButton(mainSlot 内):

「full」シェイプのタイル(角がより丸い)
図 2.: シェイプが「full」のタイル

シェイプが small の同じ textButton:

「小」の形状のタイル(角の丸みが少ない)
図 3.: シェイプが「small」のタイル

コンポーネント

M3 コンポーネントは、M2.5 コンポーネントよりも柔軟で構成可能です。 M2.5 では、さまざまなビジュアル処理に個別のコンポーネントが必要になることがよくありましたが、M3 では、デフォルト値が適切に設定された、汎用性の高い構成可能なベース コンポーネントがよく使用されます。

この原則は、ルートレイアウトにも適用されます。M2.5 では、これは PrimaryLayout または EdgeContentLayoutでした。M3 では、単一のトップレベル MaterialScope を確立した後、 primaryLayout() 関数を呼び出します。この関数はルートレイアウトを直接返します。ビルダーは必要ありません。また、titleSlot、mainSlot、bottomSlot など、複数のスロットの LayoutElements を受け入れます。これらのスロットには、 text()、 button()、card() によって返される具体的な UI コンポーネントや、Row や Column などのレイアウト構造をLayoutElementBuilders から設定できます。

テーマは、M3 のもう 1 つの重要な機能強化です。デフォルトでは、UI 要素は M3 スタイリング仕様に自動的に準拠し、ダイナミック テーマ設定をサポートします。

M2.5 M3
インタラクティブ要素
Button または Chip
テキスト
Text text()
進行状況インジケーター
CircularProgressIndicator circularProgressIndicator() または segmentedCircularProgressIndicator()
レイアウト
PrimaryLayout または EdgeContentLayout primaryLayout()
— buttonGroup()
画像検索
— icon()、avatarImage()、backgroundImage()

修飾子

M3 では、Modifiers(コンポーネントの装飾や拡張に使用)が Compose に似ています。この変更により、適切な内部型が自動的に構築されるため、ボイラープレートを削減できます。(この変更は、M3 UI コンポーネントの使用とは無関係です。必要に応じて、ProtoLayout 1.2 のビルダー スタイルの修飾子を M3 UI コンポーネントで使用することも、その逆も可能です)。

M2.5

// Uses Builder-style modifier to set opacity
fun myModifier(): ModifiersBuilders.Modifiers =
    ModifiersBuilders.Modifiers.Builder()
        .setOpacity(TypeBuilders.FloatProp.Builder(0.5F).build())
        .build()

M3

// Uses Compose-like modifiers to set opacity
fun myModifier(): LayoutModifier = LayoutModifier.opacity(0.5F)

修飾子はどちらの API スタイルでも構築できます。また、 toProtoLayoutModifiers() 拡張関数を使用して、 LayoutModifier を ModifiersBuilders.Modifier に変換することもできます。

ヘルパー関数

ProtoLayout 1.3 では、多くの UI コンポーネントを Compose にインスパイアされた API を使用して表現できますが、行や列などの基本的なレイアウト要素は、LayoutElementBuilders引き続きビルダー パターンを使用します。このスタイルのギャップを埋め、新しい M3 コンポーネント API との一貫性を高めるには、ヘルパー関数を使用することを検討してください。

ヘルパーなし

primaryLayout(
    mainSlot = {
        Column.Builder()
            .setWidth(expand())
            .setHeight(expand())
            .addContent(text("A".layoutString))
            .addContent(text("B".layoutString))
            .addContent(text("C".layoutString))
            .build()
    }
)

ヘルパーあり

// Function literal with receiver helper function
fun column(builder: Column.Builder.() -> Unit) =
    Column.Builder().apply(builder).build()

primaryLayout(
    mainSlot = {
        column {
            setWidth(expand())
            setHeight(expand())
            addContent(text("A".layoutString))
            addContent(text("B".layoutString))
            addContent(text("C".layoutString))
        }
    }
)

Tiles 1.2 / ProtoLayout 1.0 に移行する

バージョン 1.2 ではタイル レイアウト API のほとんどは androidx.wear.protolayout 名前空間にあります。最新の API を使用するには、コードで以下の移行手順を実行してください。

依存関係を更新する

アプリ モジュールのビルドファイルを次のように変更します。

Groovy

  // Remove
  implementation 'androidx.wear.tiles:tiles-material:version'

  // Include additional dependencies
  implementation "androidx.wear.protolayout:protolayout:1.4.2"
  implementation "androidx.wear.protolayout:protolayout-material:1.4.2"
  implementation "androidx.wear.protolayout:protolayout-expression:1.4.2"

  // Update
  implementation "androidx.wear.tiles:tiles:1.6.2"

Kotlin

  // Remove
  implementation("androidx.wear.tiles:tiles-material:version")

  // Include additional dependencies
  implementation("androidx.wear.protolayout:protolayout:1.4.2")
  implementation("androidx.wear.protolayout:protolayout-material:1.4.2")
  implementation("androidx.wear.protolayout:protolayout-expression:1.4.2")

  // Update
  implementation("androidx.wear.tiles:tiles:1.6.2")

名前空間を更新する

アプリの Kotlin ベースと Java ベースのコードファイルで、次の更新を行います。 または、この 名前空間の変更 スクリプトを実行することもできます。

  1. すべての androidx.wear.tiles.material.* インポートを androidx.wear.protolayout.material.* に置き換えます。androidx.wear.tiles.material.layouts ライブラリでもこのステップを実行します。
  2. 他の androidx.wear.tiles.* インポートのほとんどを androidx.wear.protolayout.* に置き換えます。

    androidx.wear.tiles.EventBuilders、androidx.wear.tiles.RequestBuilders、androidx.wear.tiles.TileBuilders、androidx.wear.tiles.TileService のインポートはそのままにします。

  3. TileService クラスと TileBuilder クラスの非推奨のメソッドの名前を変更します。

    1. TileBuilders: getTimeline() から getTileTimeline()、setTimeline() から setTileTimeline() に変更
    2. TileService: onResourcesRequest() から onTileResourcesRequest() に変更
    3. RequestBuilders.TileRequest: getDeviceParameters() から getDeviceConfiguration()、setDeviceParameters() から setDeviceConfiguration()、getState() から getCurrentState()、setState() から setCurrentState() に変更