タイルのスタートガイド

アプリでタイルの提供を開始するには、アプリの build.gradle ファイルに次の依存関係を追加します。

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.4.0"

    // Use to utilize standard components and layouts in your tiles
    implementation "androidx.wear.protolayout:protolayout:1.2.0"

    // Use to utilize components and layouts with Material Design in your tiles
    implementation "androidx.wear.protolayout:protolayout-material:1.2.0"

    // Use to include dynamic expressions in your tiles
    implementation "androidx.wear.protolayout:protolayout-expression:1.2.0"

    // Use to preview wear tiles in your own app
    debugImplementation "androidx.wear.tiles:tiles-renderer:1.4.0"

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

Kotlin

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

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

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

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

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

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

タイルを作成する

アプリでタイルを表示するには、次のコードサンプルに示すように、TileService を拡張するクラスを作成してメソッドを実装します。

Kotlin

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline
import androidx.wear.protolayout.material.Text
import androidx.wear.tiles.TileBuilders.Tile

private val RESOURCES_VERSION = "1"
class MyTileService : TileService() {
    override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
        Futures.immediateFuture(Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    Text.Builder(this, "Hello world!")
                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
                        .setColor(argb(0xFF000000.toInt()))
                        .build()))
            .build())

    override fun onTileResourcesRequest(requestParams: ResourcesRequest) =
        Futures.immediateFuture(Resources.Builder()
            .setVersion(RESOURCES_VERSION)
            .build()
        )
}

Java

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline;
import androidx.wear.protolayout.material.Text;
import androidx.wear.tiles.TileBuilders.Tile;

public class MyTileService extends TileService {
    private static final String RESOURCES_VERSION = "1";

    @NonNull
    @Override
    protected ListenableFuture<Tile> onTileRequest(
        @NonNull TileRequest requestParams
    ) {
        return Futures.immediateFuture(new Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    new Text.Builder(this, "Hello world!")
                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
                        .setColor(ColorBuilders.argb(0xFF000000))
                        .build()))
            .build()
        );
   }

   @NonNull
   @Override
   protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
   ) {
       return Futures.immediateFuture(new Resources.Builder()
               .setVersion(RESOURCES_VERSION)
               .build()
       );
   }
}

次に、AndroidManifest.xml ファイルの <application> タグ内にサービスを追加します。

<service
   android:name=".MyTileService"
   android:label="@string/tile_label"
   android:description="@string/tile_description"
   android:icon="@drawable/tile_icon_round"
   android:roundIcon="@drawable/tile_icon_round"
   android:exported="true"
   android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
   <intent-filter>
       <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
   </intent-filter>

   <meta-data android:name="androidx.wear.tiles.PREVIEW"
       android:resource="@drawable/tile_preview" />
</service>

権限とインテント フィルタにより、このサービスがタイル プロバイダとして登録されます。

ユーザーがスマートフォンまたはスマートウォッチでタイルを設定すると、アイコン、ラベル、説明が表示されます。

プレビューのメタデータタグを使用すると、スマートフォンでタイルを設定するときにプレビューが表示されます。

タイルサービスのライフサイクルの概要

アプリ マニフェストで TileService を作成して宣言したら、次の手順を行います。 タイルサービスの状態変化に応答できます。

TileService は、バインドされたサービスです。TileService が結果としてバインドされます システムが通信する必要がある場合に選択します。典型的な bound-serviceLifecycle には、次の 4 つのコールバック メソッドが含まれています。 onCreate()onBind()onUnbind()onDestroy()サービスが呼び出されるたびに、システムはこれらのメソッドを ライフサイクルの段階に入ります

バインドされたサービスのライフサイクルを制御するコールバックに加えて、 TileService ライフサイクルに固有の他のメソッドを実装します。すべてのタイル サービスで onTileRequest()onTileResourcesRequest() を実装する必要があります。 システムからのアップデートの要求に対応します。

  • onTileAddEvent(): ユーザーが ユーザーが初めてタイルを追加し、 タイルをもう一度表示できます。これが 1 回限りの初期化を行うのに最適なタイミングです。

    onTileAddEvent() は、タイルのセットが再構成された場合にのみ呼び出されます。 タイルが作成されるたびに通知されません。たとえば、デバイスが 再起動または電源がオンになった場合、タイルの onTileAddEvent() は呼び出されません 表示されますgetActiveTilesAsync() を使用できます。 を使用して、アクティブなタイルのスナップショットを取得することもできます。

  • onTileRemoveEvent(): ユーザーが タイルを削除します。

  • onTileEnterEvent(): タイルがフレームのしきい値を超えたときに 画面に表示されます

  • onTileLeaveEvent(): タイルがフレームのしきい値を超えたときに 画面に表示されなくなります。

  • onTileRequest(): システムが実行時にこのメソッドを呼び出します。 このプロバイダに新しいタイムラインをリクエストします。

  • onTileResourcesRequest(): このメソッドが呼び出されると、 システムがこのプロバイダからリソース バンドルをリクエストします。この問題は、 タイルの初回読み込み時、またはリソース バージョンが できます。

で確認できます。

アクティブなタイルのクエリ

アクティブなタイルとは、スマートウォッチで表示するために追加されたタイルのことです。使用 TileService の静的メソッド getActiveTilesAsync() で、どのタイルをクエリするかを指定します。 アプリに属しているものがアクティブです。

タイルの UI を作成する

タイルのレイアウトは、ビルダー パターンを使用して記述し、レイアウト コンテナと基本的なレイアウト要素からなるツリーのように作成します。各レイアウト要素にはプロパティがあり、さまざまなセッター メソッドで設定できます。

基本的なレイアウト要素

protolayout ライブラリの次の視覚的要素とマテリアル コンポーネントがサポートされています。

  • Text: テキスト文字列をレンダリングし、必要に応じて折り返します。
  • Image: 画像をレンダリングします。
  • Spacer: 要素間にパディングを設定します。背景色を設定するときは区切りとして機能します。

マテリアル コンポーネント

基本要素に加えて、protolayout-material ライブラリにはマテリアル デザイン ユーザー インターフェースの推奨事項に沿ったタイルデザインを実現するためのコンポーネントが用意されています。

  • Button: アイコンを含むようにデザインされた、クリック可能な円形のコンポーネント。
  • Chip: 最大 2 行のテキストと任意のアイコンを含むようにデザインされた、クリック可能なスタジアム形のコンポーネント。

  • CompactChip: 1 行のテキストを含むようにデザインされた、クリック可能なスタジアム形のコンポーネント。

  • TitleChip: Chip に似ているが、タイトル テキストを表示できるように高さが大きくなっている、クリック可能なスタジアム形のコンポーネント。

  • CircularProgressIndicator: EdgeContentLayout 内に配置して画面の端に進行状況を表示できる、円形の進行状況インジケーター。

レイアウト コンテナ

次のコンテナとマテリアル レイアウトがサポートされています。

  • Row: 子要素を水平方向に並べます。
  • Column: 子要素を垂直方向に並べます。
  • Box: 子要素を重ね合わせます。
  • Arc: 子要素を環状に並べます。
  • Spannable: テキストのセクションに特定の FontStyles を適用し、テキストや画像の間に入れます。詳細については、Spannable をご覧ください。

どのコンテナにも、子を 1 つ以上含めることができます。子はコンテナにすることもできます。たとえば、Column に子として複数の Row 要素を含めることができます。この場合、格子状のレイアウトになります。

一例として、コンテナ レイアウトと 2 つの子レイアウト要素を持つタイルは次のようになります。

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build()

Java

private LayoutElement myLayout() {
    return new Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(new Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(new Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build();
}

マテリアル レイアウト

基本的なレイアウトに加えて、protolayout-material ライブラリには、特定の「スロット」内に要素を保持するための独自のレイアウトがいくつか用意されています。

  • PrimaryLayout: 1 つのプライマリ アクション CompactChip を下部に配置し、その中央にコンテンツを重ねます。

  • MultiSlotLayout: プライマリ ラベルとセカンダリ ラベルを配置し、その間にオプションのコンテンツを配置して、下部にオプションの CompactChip を配置します。

  • MultiButtonLayout: マテリアル ガイドラインに沿って並んだボタンのセットを配置します。

  • EdgeContentLayout: CircularProgressIndicator など、コンテンツを画面の端に配置します。このレイアウトを使用すると、その中のコンテンツには適切な余白とパディングが自動的に適用されます。

円弧

サポートされている Arc コンテナの子は次のとおりです。

  • ArcLine: 円弧の周りに曲線をレンダリングします。
  • ArcText: 円弧の中に曲線テキストをレンダリングします。
  • ArcAdapter: 円弧の中に基本的なレイアウト要素をレンダリングし、円弧の接線上に描画します。

詳細については、それぞれの要素のリファレンス ドキュメントをご覧ください。

修飾子

利用可能なすべてのレイアウト要素に、必要に応じて修飾子を適用できます。修飾子は以下の目的で使用します。

  • レイアウトの外観を変更する: たとえば、レイアウト要素に背景、枠線、パディングを追加します。
  • レイアウトに関するメタデータを追加する: たとえば、スクリーン リーダーで使用するセマンティクス修飾子をレイアウト要素に追加します。
  • 機能を追加する: たとえば、レイアウト要素にクリック可能な修飾子を追加して、タイルをインタラクティブにします。詳しくは、タイルを操作するをご覧ください。

たとえば、次のコードサンプルで示すとおり、Image のデフォルトの外観とメタデータをカスタマイズできます。

Kotlin

private fun myImage(): LayoutElement =
    Image.Builder()
        .setWidth(dp(24f))
        .setHeight(dp(24f))
        .setResourceId("image_id")
        .setModifiers(Modifiers.Builder()
            .setBackground(Background.Builder().setColor(argb(0xFFFF0000)).build())
            .setPadding(Padding.Builder().setStart(dp(12f)).build())
            .setSemantics(Semantics.builder()
                .setContentDescription("Image description")
                .build()
            ).build()
        ).build()

Java

private LayoutElement myImage() {
   return new Image.Builder()
           .setWidth(dp(24f))
           .setHeight(dp(24f))
           .setResourceId("image_id")
           .setModifiers(new Modifiers.Builder()
                   .setBackground(new Background.Builder().setColor(argb(0xFFFF0000)).build())
                   .setPadding(new Padding.Builder().setStart(dp(12f)).build())
                   .setSemantics(new Semantics.Builder()
                           .setContentDescription("Image description")
                           .build()
                   ).build()
           ).build();
}

Spannable

Spannable は、テキストと同様に要素をレイアウトする特別なタイプのコンテナです。大きなテキスト ブロック内の 1 つの部分文字列だけに別のスタイルを適用する場合に便利です。これは Text 要素では不可能です。

Spannable コンテナは Span の子で満たされます。その他の子やネストされた Spannable インスタンスは使用できません。

Span の子には、次の 2 種類があります。

  • SpanText: テキストを特定のスタイルでレンダリングします。
  • SpanImage: テキストとともに画像をインラインでレンダリングします。

たとえば、次のコードサンプルに示すように、「Hello world」タイルの「world」を斜体にし、単語の間に画像を挿入できます。

Kotlin

private fun mySpannable(): LayoutElement =
    Spannable.Builder()
        .addSpan(SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(SpanText.Builder()
            .setText("world")
            .setFontStyle(FontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build()

Java

private LayoutElement mySpannable() {
   return new Spannable.Builder()
        .addSpan(new SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(new SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(new SpanText.Builder()
            .setText("world")
            .setFontStyle(newFontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build();
}

リソースを扱う

タイルはアプリのリソースにアクセスできません。これは、Android 画像 ID を Image レイアウト要素に渡して解決することができないことを意味します。代わりに、onTileResourcesRequest() メソッドをオーバーライドし、リソースを手動で指定します。

onTileResourcesRequest() メソッド内で画像を指定する方法には次の 2 つがあります。

Kotlin

override fun onTileResourcesRequest(
    requestParams: ResourcesRequest
) = Futures.immediateFuture(
Resources.Builder()
    .setVersion("1")
    .addIdToImageMapping("image_from_resource", ImageResource.Builder()
        .setAndroidResourceByResId(AndroidImageResourceByResId.Builder()
            .setResourceId(R.drawable.image_id)
            .build()
        ).build()
    )
    .addIdToImageMapping("image_inline", ImageResource.Builder()
        .setInlineResource(InlineImageResource.Builder()
            .setData(imageAsByteArray)
            .setWidthPx(48)
            .setHeightPx(48)
            .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
            .build()
        ).build()
    ).build()
)

Java

@Override
protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
) {
return Futures.immediateFuture(
    new Resources.Builder()
        .setVersion("1")
        .addIdToImageMapping("image_from_resource", new ImageResource.Builder()
            .setAndroidResourceByResId(new AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.image_id)
                .build()
            ).build()
        )
        .addIdToImageMapping("image_inline", new ImageResource.Builder()
            .setInlineResource(new InlineImageResource.Builder()
                .setData(imageAsByteArray)
                .setWidthPx(48)
                .setHeightPx(48)
                .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
                .build()
            ).build()
        ).build()
);
}