開始使用資訊方塊

如果想為應用程式提供資訊方塊,請把下列依附元件加入 應用程式的 build.gradle 檔案。

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.5.0-alpha04"

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

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

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

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

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

Kotlin

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

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

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

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

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

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

建立資訊方塊

如要從應用程式提供資訊方塊,請建立可擴充 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 繫結至結果 或系統需要相互通訊的情況一般 繫結服務生命週期包含下列四種回呼方法: onCreate()onBind()onUnbind()onDestroy()。每次服務都會叫用這些方法 就會進入新的生命週期階段

除了控制繫結服務生命週期的回呼外, 實作 TileService 生命週期特有的其他方法。所有圖塊 服務必須實作 onTileRequest()onTileResourcesRequest() 以 回應來自系統的更新要求。

  • onTileAddEvent():只有在使用者時,系統才會呼叫這個方法 新增您的視訊方格;如果使用者移除並新增您的 。這就是執行任何一次性初始化的最佳時機。

    只有在這組圖塊重新設定時,系統才會呼叫 onTileAddEvent()。 方塊建立時,則不會。例如,裝置在 已重新啟動或開機,但未針對資訊方塊呼叫 onTileAddEvent() 您可以使用 getActiveTilesAsync() 即可取得即時資訊方塊,瞭解與您擁有哪些動態磚。

  • onTileRemoveEvent():只有在使用者情況下,系統才會呼叫這個方法 會移除您的動態磚。

  • onTileEnterEvent():系統會在資訊方塊時呼叫這個方法 螢幕上會顯示由這個供應商提供的內容

  • onTileLeaveEvent():系統會在資訊方塊時呼叫這個方法 畫面上無法顯示這個供應商提供的提示

  • onTileRequest():系統會在系統呼叫這個方法時呼叫這個方法 要求這個供應商提供新的時間軸

  • onTileResourcesRequest():系統會在偵測到事件 系統要求此供應商提供資源組合。 首次載入資訊方塊或資源版本時 並輸入變更內容

,瞭解如何調查及移除這項存取權。

查詢哪些圖塊已啟用

「主動式動態磚」是已經新增至手錶的動態磚。使用 TileService 的靜態方法 getActiveTilesAsync(),可查詢哪些資訊方塊 不屬於您的應用程式

建立資訊方塊 UI

資訊方塊的版面配置是以建構工具模式編寫。資訊方塊的版面配置為 這類建構過程的樹狀結構由版面配置容器和基本版面配置組成 元素。每個版面配置元素都有屬性,您可以透過各種 setter 方法進行設定。

基本的版面配置元素

系統支援 protolayout 程式庫的下列視覺元素和 Material 元件

  • Text:轉譯文字字串,可選擇是否要換行。
  • Image:算繪圖片。
  • Spacer: 提供元素之間的邊框間距,或是在設定元素時充當分隔線 背景顏色。

Material 元件

除了基礎元素之外,protolayout-material 程式庫還有提供其他元件,可確保資訊方塊設計方式符合 Material Design 使用者介面的建議事項。

  • Button:可含有圖示且可供點選的圓形元件。
  • Chip:可含有最多兩行文字、選擇加入圖示且可供點選的體育場形元件。

  • CompactChip:可含有單行文字且可供點選的體育場形元件。

  • TitleChip:和 Chip 用途類似的可點選體育場形元件,但是高度更高,可以容納標題文字。

  • CircularProgressIndicator:可放置在 EdgeContentLayout 內的圓形進度指標,用來在螢幕邊緣顯示進度。

版面配置容器

系統支援下列容器和 Material 版面配置

  • Row:以水平方向依序排列子元素。
  • Column:以垂直方向依序排列子元素。
  • Box:讓子元素互相重疊。
  • Arc:以圓形排列子元素。
  • Spannable:為文字區段套用特定的 FontStyles,並交錯顯示文字和圖片。詳情請參閱「Spannable」。

每個容器都能包含一或多個子項,這些子項也能 容器例如,Column 可包含多個 Row 元素,如下所示: 子項,形成類似格線的版面配置。

例如,資訊方塊有一個容器版面配置和兩個子項版面配置元素 看起來會像這樣:

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();
}

Material 版面配置

除了基礎版面配置之外,protolayout-material 程式庫還有提供幾個項目 清楚明確的版面配置,能將元素固定在特定的「位置」上。

  • PrimaryLayout:把單一主要操作 CompactChip 放置於底部,並在上方置中顯示內容。

  • MultiSlotLayout:放置主要和次要標籤,並在兩個標籤之間放置選用內容,另可視需要在底部放置 CompactChip

  • MultiButtonLayout:放置根據 Material Design 指南排列的一組按鈕。

  • 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 是特殊類型的容器,可以使用和文字類似的方式排列元素。如果您想為大型文字區塊中的單一子字串套用其他樣式,而 Text 元素無法達到這個效果,就適合使用這種容器。

Spannable 容器會由 Span 子項填滿,不得使用其他子項或巢狀 Spannable 例項。

Span 子項分為兩種類型:

  • 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() 方法中提供圖片:

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()
);
}