앱에서 카드를 제공하려면 앱의 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
는 앱 요청의 결과로 바인딩되거나 시스템과 통신해야 하는 경우 바인딩됩니다. 일반적인 바인드된 서비스 수명 주기에는 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(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 가이드라인에 따라 정렬된 버튼 집합을 배치합니다.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
- 앱의 맞춤 빠른 설정 타일 만들기