앱에서 카드를 제공하려면 앱의 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") }
주요 개념
카드는 Android 앱과 동일한 방식으로 빌드되지 않으며 다음과 같은 다른 개념을 사용합니다.
- 레이아웃 템플릿: 디스플레이의 시각적 요소의 전반적인 배치를 정의합니다. 타일은 디스플레이 가장자리에 진행률 표시기가 포함된
EdgeContentLayout
레이아웃 템플릿 또는 이 표시기를 표시하지 않는PrimaryLayout
를 사용합니다. - 레이아웃 요소:
Button
또는Chip
와 같은 개별 그래픽 요소 또는Column
,MultiButtonLayout
,MultiSlotLayout
등을 사용하여 그룹화된 여러 요소를 나타냅니다. 레이아웃 템플릿 내에 삽입됩니다. - 리소스:
ResourceBuilders.Resources
객체는 레이아웃을 렌더링하는 데 필요한 Android 리소스 (이미지)의 키-값 쌍 맵과 버전으로 구성됩니다. - 타임라인:
TimelineBuilders.Timeline
객체는 레이아웃 객체의 인스턴스 1개 이상으로 구성된 목록입니다. 특정 시점에 레이아웃 표시를 중지하는 등 렌더러가 한 레이아웃 객체에서 다른 레이아웃 객체로 전환해야 하는 시점을 나타내는 다양한 메커니즘과 표현식을 제공할 수 있습니다. - 상태: 카드와 앱 간에 전달되어 두 구성요소가 서로 통신할 수 있도록 하는
StateBuilders.State
유형의 데이터 구조입니다. 예를 들어 카드에서 버튼을 탭하면 상태에 버튼의 ID가 유지됩니다. 맵을 사용하여 데이터 유형을 교환할 수도 있습니다. - 카드: 카드를 나타내는
TileBuilders.Tile
객체로, 타임라인, 리소스 버전 ID, 최신성 간격, 상태로 구성됩니다. - Protolayout: 이 용어는 다양한 카드 관련 클래스의 이름에 표시되며 다양한 Wear OS 표시 경로에서 사용되는 그래픽 라이브러리인 Wear OS Protolayout 라이브러리를 나타냅니다.
카드 만들기
앱에서 카드를 제공하려면 TileService
유형의 서비스를 구현하고 매니페스트에 등록합니다. 이를 통해 시스템은 onTileRequest()
호출 중에 필요한 타일을 요청하고 onTileResourcesRequest()
호출 중에 리소스를 요청합니다.
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_BODY1) .setColor(argb(0xFFFFFFFF.toInt())) .build() ) ) .build() ) override fun onTileResourcesRequest(requestParams: ResourcesRequest) = Futures.immediateFuture( Resources.Builder() .setVersion(RESOURCES_VERSION) .build() ) }
다음으로 AndroidManifest.xml
파일의 <application>
태그 내에 서비스를 추가합니다.
<service android:name=".snippets.tile.MyTileService" android:label="@string/tile_label" android:description="@string/tile_description" android:icon="@mipmap/ic_launcher" 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>
권한과 인텐트 필터가 이 서비스를 카드 제공자로 등록합니다.
아이콘, 라벨, 설명, 미리보기 리소스는 사용자가 휴대전화나 시계에서 카드를 구성할 때 표시됩니다.
전체 예시는 GitHub의 코드 샘플 또는 Codelab을 참고하세요.
카드 서비스 수명 주기 개요
앱 매니페스트에서 TileService
를 만들고 선언한 후에는 카드 서비스의 상태 변경에 응답할 수 있습니다.
TileService
는 바인드된 서비스입니다. TileService
는 앱 요청의 결과로 또는 시스템에서 TileService
와 통신해야 하는 경우 바인딩됩니다. 일반적인 바인드된 서비스 수명 주기에는 onCreate()
, onBind()
, onUnbind()
, onDestroy()
라는 4개의 콜백 메서드가 포함됩니다. 시스템은 서비스가 새 수명 주기 단계로 전환될 때마다 이러한 메서드를 호출합니다.
바인드된 서비스 수명 주기를 제어하는 콜백 외에도 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(VERTICAL_ALIGN_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 가이드라인에 따라 정렬된 버튼 집합을 배치합니다.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
하위 요소에는 두 가지 유형이 있습니다.
예를 들어, 다음 코드 샘플과 같이 '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()
메서드 내에 이미지를 제공하는 방법에는 두 가지가 있습니다.
setAndroidResourceByResId()
를 사용하여 드로어블 리소스를 제공합니다.setInlineResource()
를 사용하여 동적 이미지를ByteArray
로 제공합니다.
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() ); }
추천 서비스
- 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- ProtoLayout 네임스페이스로 이전
- Compose의
ConstraintLayout
- 앱의 맞춤 빠른 설정 타일 만들기