앱에서 카드를 제공하려면 앱의 build.gradle
파일에 다음 종속 항목을 포함합니다.
Groovy
dependencies { // Use to implement support for wear tiles implementation "androidx.wear.tiles:tiles:1.1.0" // Use to utilize components and layouts with Material Design in your tiles implementation "androidx.wear.tiles:tiles-material:1.1.0" // Use to preview wear tiles in your own app debugImplementation "androidx.wear.tiles:tiles-renderer:1.1.0" // Use to fetch tiles from a tile provider in your tests testImplementation "androidx.wear.tiles:tiles-testing:1.1.0" }
Kotlin
dependencies { // Use to implement support for wear tiles implementation("androidx.wear.tiles:tiles:1.1.0") // Use to utilize components and layouts with Material design in your tiles implementation("androidx.wear.tiles:tiles-material:1.1.0") // Use to preview wear tiles in your own app debugImplementation("androidx.wear.tiles:tiles-renderer:1.1.0") // Use to fetch tiles from a tile provider in your tests testImplementation("androidx.wear.tiles:tiles-testing:1.1.0") }
카드 만들기
애플리케이션에서 카드를 제공하려면 다음 코드 샘플과 같이 TileService
를 확장하는 클래스를 만들고 메서드를 구현합니다.
Kotlin
private val RESOURCES_VERSION = "1" class MyTileService : TileService() { override fun onTileRequest(requestParams: RequestBuilders.TileRequest) = Futures.immediateFuture(Tile.Builder() .setResourcesVersion(RESOURCES_VERSION) .setTimeline(Timeline.Builder().addTimelineEntry( TimelineEntry.Builder().setLayout( Layout.Builder().setRoot( Text.Builder().setText("Hello world!").setFontStyle( FontStyle.Builder().setColor(argb(0xFF000000)).build() ).build() ).build() ).build() ).build() ).build()) override fun onResourcesRequest(requestParams: ResourcesRequest) = Futures.immediateFuture(Resources.Builder() .setVersion(RESOURCES_VERSION) .build() ) }
자바
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) .setTimeline(new Timeline.Builder() .addTimelineEntry(new TimelineEntry.Builder() .setLayout(new Layout.Builder() .setRoot(new Text.Builder() .setText("Hello world!") .setFontStyle(new FontStyle.Builder() .setColor(argb(0xFF000000)).build() ).build() ).build() ).build() ).build() ).build() ); } @NonNull @Override protected ListenableFuture<Resources> onResourcesRequest( @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: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>
권한과 인텐트 필터가 이 서비스를 카드 제공자로 등록합니다.
아이콘, 라벨, 설명은 사용자가 휴대전화나 시계에서 카드를 구성할 때 표시됩니다.
미리보기 메타데이터 태그를 사용하면 휴대전화에서 카드를 구성할 때 카드의 미리보기를 표시할 수 있습니다.
카드 UI 만들기
카드의 레이아웃은 빌더 패턴을 사용하여 작성됩니다. 카드의 레이아웃은 레이아웃 컨테이너와 기본 레이아웃 요소로 이루어진 일종의 트리처럼 구성됩니다. 각 레이아웃 요소에는 다양한 setter 메서드를 통해 설정할 수 있는 속성이 있습니다.
기본 레이아웃 요소
다음과 같은 시각적 요소가 지원됩니다.
Text
: 텍스트 문자열을 렌더링합니다(선택적으로 줄바꿈 가능).Image
: 이미지를 렌더링합니다.Spacer
: 요소와 요소 사이에 패딩을 제공하거나 배경 색상을 설정할 때 구분선 역할을 할 수 있습니다.
Material 구성요소
tiles-material
라이브러리는 기본 요소 외에도 Material Design 사용자 인터페이스 권장사항과 일치하는 카드를 디자인할 수 있도록 지원하는 구성요소를 제공합니다.
Button
: 클릭 가능한 원형 구성요소로, 아이콘을 포함할 수 있습니다.Chip
: 클릭 가능한 경기장 모양의 구성요소로, 최대 두 줄의 텍스트와 선택적으로 아이콘을 포함할 수 있습니다.CompactChip
: 클릭 가능한 경기장 모양의 구성요소로, 한 줄의 텍스트를 포함할 수 있습니다.TitleChip
: 클릭 가능한 경기장 모양의 구성요소로,Chip
과 비슷하지만 제목 텍스트를 수용할 수 있도록 높이가 더 큽니다.CircularProgressIndicator
: 원형 진행률 표시기로, 화면 가장자리 주위에 진행률을 표시하도록ProgressIndicatorLayout
내부에 배치할 수 있습니다.
레이아웃 컨테이너
지원되는 컨테이너는 다음과 같습니다.
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()
자바
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 레이아웃
tiles-material
라이브러리는 기본 레이아웃 외에도 요소를 특정 '슬롯'에 배치하는 몇 가지 독자적인 레이아웃을 제공합니다.
PrimaryLayout
: 단일 기본 작업CompactChip
을 하단에 배치하고 그 위 중앙에 콘텐츠를 배치합니다.MultiSlotLayout
: 기본 라벨 및 보조 라벨을 배치하고 그 사이에 선택적인 콘텐츠와 하단에 선택적인CompactChip
을 배치합니다.ProgressIndicatorLayout
: 화면 가장자리 주위에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()
자바
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()
자바
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
레이아웃 요소에 전달해 확인할 수 없다는 의미입니다. 대신 onResourcesRequest()
메서드를 재정의하고 리소스를 직접 제공하세요.
onResourcesRequest()
메서드 내에 이미지를 제공하는 방법에는 두 가지가 있습니다.
setAndroidResourceByResId()
를 사용하여 드로어블 리소스를 제공합니다.setInlineResource()
를 사용하여 동적 이미지를ByteArray
로 제공합니다.
Kotlin
override fun onResourcesRequest( 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() )
자바
@Override protected ListenableFuture<Resources> onResourcesRequest( @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() ); }