이 문서에서는 Android Jetpack의 템플릿 빌더를 사용하여 Slice를 구성하는 방법을 자세히 설명합니다.
슬라이스 템플릿 정의
Slice는 ListBuilder
를 사용하여 구성됩니다. ListBuilder를 사용하면 목록에 표시되는 여러 유형의 행을 추가할 수 있습니다. 이 섹션에서는 각 행 유형 및 행이 구성되는 방법을 설명합니다.
SliceAction
Slice 템플릿의 가장 기본적인 요소는 SliceAction
입니다. SliceAction
은 PendingIntent
와 함께 라벨을 포함하며 다음 중 하나입니다.
- 아이콘 버튼
- 기본 전환
- 맞춤 전환(켜기/끄기 상태가 있는 드로어블)
SliceAction
은 이 섹션의 나머지 부분에서 설명하는 템플릿 빌더에 의해 사용됩니다. SliceAction
에서는 작업의 이미지가 표시되는 방식을 결정하는 이미지 모드를 정의할 수 있습니다.
ICON_IMAGE
: 크기가 아주 작고 색조를 조정할 수 있음SMALL_IMAGE
: 크기가 작고 색조를 조정할 수 없음LARGE_IMAGE
: 크기가 가장 크고 색조를 조정할 수 없음
HeaderBuilder
대부분의 경우 HeaderBuilder
를 사용하여 템플릿의 헤더를 설정해야 합니다.
헤더에는 다음을 포함할 수 있습니다.
- 제목
- 자막
- 요약 자막
- 기본 작업
다음은 몇 가지 헤더 구성 예입니다. 회색 상자는 잠재적 아이콘 및 패딩 위치를 나타냅니다.
다른 표면의 헤더 렌더링
Slice가 필요한 경우 표시 표면에 따라 Slice를 렌더링하는 방법이 결정됩니다. 호스팅하는 표면 간에 렌더링이 약간 다를 수도 있습니다.
더 작은 형식에서는 일반적으로 헤더만 표시됩니다(헤더가 있는 경우). 헤더 요약을 지정한 경우 자막 텍스트 대신 요약 텍스트가 표시됩니다.
템플릿에 헤더를 지정하지 않았다면 일반적으로 ListBuilder
에 추가된 첫 번째 행이 대신 표시됩니다.
HeaderBuilder 예 - 헤더가 있는 간단한 목록 Slice
fun createSliceWithHeader(sliceUri: Uri) =
list(context, sliceUri, ListBuilder.INFINITY) {
setAccentColor(0xff0F9D) // Specify color for tinting icons
header {
title = "Get a ride"
subtitle = "Ride in 4 min"
summary = "Work in 1 hour 45 min | Home in 12 min"
}
row {
title = "Home"
subtitle = "12 miles | 12 min | $9.00"
addEndItem(
IconCompat.createWithResource(context, R.drawable.ic_home),
ListBuilder.ICON_IMAGE
)
}
}
public Slice createSliceWithHeader(Uri sliceUri) {
if (getContext() == null) {
return null;
}
// Construct the parent.
ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
.setAccentColor(0xff0F9D58) // Specify color for tinting icons.
.setHeader( // Create the header and add to slice.
new HeaderBuilder()
.setTitle("Get a ride")
.setSubtitle("Ride in 4 min.")
.setSummary("Work in 1 hour 45 min | Home in 12 min.")
).addRow(new RowBuilder() // Add a row.
.setPrimaryAction(
createActivityAction()) // A slice always needs a SliceAction.
.setTitle("Home")
.setSubtitle("12 miles | 12 min | $9.00")
.addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_home),
SliceHints.ICON_IMAGE)
); // Add more rows if needed...
return listBuilder.build();
}
헤더의 SliceAction
Slice 헤더에 SliceAction을 표시할 수도 있습니다.
fun createSliceWithActionInHeader(sliceUri: Uri): Slice {
// Construct our slice actions.
val noteAction = SliceAction.create(
takeNoteIntent,
IconCompat.createWithResource(context, R.drawable.ic_pencil),
ICON_IMAGE,
"Take note"
)
val voiceNoteAction = SliceAction.create(
voiceNoteIntent,
IconCompat.createWithResource(context, R.drawable.ic_mic),
ICON_IMAGE,
"Take voice note"
)
val cameraNoteAction = SliceAction.create(
cameraNoteIntent,
IconCompat.createWithResource(context, R.drawable.ic_camera),
ICON_IMAGE,
"Create photo note"
)
// Construct the list.
return list(context, sliceUri, ListBuilder.INFINITY) {
setAccentColor(0xfff4b4) // Specify color for tinting icons
header {
title = "Create new note"
subtitle = "Easily done with this note taking app"
}
addAction(noteAction)
addAction(voiceNoteAction)
addAction(cameraNoteAction)
}
}
public Slice createSliceWithActionInHeader(Uri sliceUri) {
if (getContext() == null) {
return null;
}
// Construct our slice actions.
SliceAction noteAction = SliceAction.create(takeNoteIntent,
IconCompat.createWithResource(getContext(), R.drawable.ic_pencil),
ListBuilder.ICON_IMAGE, "Take note");
SliceAction voiceNoteAction = SliceAction.create(voiceNoteIntent,
IconCompat.createWithResource(getContext(), R.drawable.ic_mic),
ListBuilder.ICON_IMAGE,
"Take voice note");
SliceAction cameraNoteAction = SliceAction.create(cameraNoteIntent,
IconCompat.createWithResource(getContext(), R.drawable.ic_camera),
ListBuilder.ICON_IMAGE,
"Create photo note");
// Construct the list.
ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
.setAccentColor(0xfff4b400) // Specify color for tinting icons
.setHeader(new HeaderBuilder() // Construct the header.
.setTitle("Create new note")
.setSubtitle("Easily done with this note taking app")
)
.addRow(new RowBuilder()
.setTitle("Enter app")
.setPrimaryAction(createActivityAction())
)
// Add the actions to the ListBuilder.
.addAction(noteAction)
.addAction(voiceNoteAction)
.addAction(cameraNoteAction);
return listBuilder.build();
}
RowBuilder
RowBuilder
를 사용하여 콘텐츠 행을 구성할 수 있습니다. 행에는 다음을 포함할 수 있습니다.
- 제목
- 자막
- 시작 항목: SliceAction, 아이콘 또는 타임스탬프
- 종료 항목: SliceAction, 아이콘 또는 타임스탬프
- 기본 작업
다음 제한사항에 따라 여러 가지 방법으로 행 콘텐츠를 조합할 수 있습니다.
- 시작 항목은 Slice의 첫 번째 행에 표시되지 않습니다.
- 종료 항목은
SliceAction
개체와Icon
개체를 혼합하여 사용할 수 없습니다. - 한 행에 하나의 타임스탬프만 포함할 수 있습니다.
다음 이미지에 행 콘텐츠의 예가 표시되어 있습니다. 회색 상자는 잠재적 아이콘 및 패딩 위치를 나타냅니다.
RowBuilder 예 - Wi-Fi 전환
아래의 예는 기본 작업과 기본 전환이 있는 행을 보여줍니다.
fun createActionWithActionInRow(sliceUri: Uri): Slice {
// Primary action - open wifi settings.
val wifiAction = SliceAction.create(
wifiSettingsPendingIntent,
IconCompat.createWithResource(context, R.drawable.ic_wifi),
ICON_IMAGE,
"Wi-Fi Settings"
)
// Toggle action - toggle wifi.
val toggleAction = SliceAction.createToggle(
wifiTogglePendingIntent,
"Toggle Wi-Fi",
isConnected /* isChecked */
)
// Create the parent builder.
return list(context, wifiUri, ListBuilder.INFINITY) {
setAccentColor(0xff4285) // Specify color for tinting icons / controls.
row {
title = "Wi-Fi"
primaryAction = wifiAction
addEndItem(toggleAction)
}
}
}
public Slice createActionWithActionInRow(Uri sliceUri) {
if (getContext() == null) {
return null;
}
// Primary action - open wifi settings.
SliceAction primaryAction = SliceAction.create(wifiSettingsPendingIntent,
IconCompat.createWithResource(getContext(), R.drawable.ic_wifi),
ListBuilder.ICON_IMAGE,
"Wi-Fi Settings"
);
// Toggle action - toggle wifi.
SliceAction toggleAction = SliceAction.createToggle(wifiTogglePendingIntent,
"Toggle Wi-Fi", isConnected /* isChecked */);
// Create the parent builder.
ListBuilder listBuilder = new ListBuilder(getContext(), wifiUri, ListBuilder.INFINITY)
// Specify color for tinting icons / controls.
.setAccentColor(0xff4285f4)
// Create and add a row.
.addRow(new RowBuilder()
.setTitle("Wi-Fi")
.setPrimaryAction(primaryAction)
.addEndItem(toggleAction));
// Build the slice.
return listBuilder.build();
}
GridBuilder
GridBuilder
를 사용하여 콘텐츠의 그리드를 구성할 수 있습니다. 그리드에는 다음과 같은 이미지 유형을 포함할 수 있습니다.
ICON_IMAGE
: 크기가 아주 작고 색조를 조정할 수 있음SMALL_IMAGE
: 크기가 작고 색조를 조정할 수 없음LARGE_IMAGE
: 크기가 가장 크고 색조를 조정할 수 없음
그리드 셀은 CellBuilder
를 사용하여 구성됩니다. 셀에는 최대 두 줄의 텍스트와 이미지 하나를 포함할 수 있습니다. 셀은 비워둘 수 없습니다.
다음 이미지에 그리드 예가 표시되어 있습니다.
GridRowBuilder 예 - 근처 음식점
아래 예에서는 이미지와 텍스트가 포함된 그리드 행을 보여줍니다.
fun createSliceWithGridRow(sliceUri: Uri): Slice {
// Create the parent builder.
return list(context, sliceUri, ListBuilder.INFINITY) {
header {
title = "Famous restaurants"
primaryAction = SliceAction.create(
pendingIntent, icon, ListBuilder.ICON_IMAGE, "Famous restaurants"
)
}
gridRow {
cell {
addImage(image1, LARGE_IMAGE)
addTitleText("Top Restaurant")
addText("0.3 mil")
contentIntent = intent1
}
cell {
addImage(image2, LARGE_IMAGE)
addTitleText("Fast and Casual")
addText("0.5 mil")
contentIntent = intent2
}
cell {
addImage(image3, LARGE_IMAGE)
addTitleText("Casual Diner")
addText("0.9 mi")
contentIntent = intent3
}
cell {
addImage(image4, LARGE_IMAGE)
addTitleText("Ramen Spot")
addText("1.2 mi")
contentIntent = intent4
}
}
}
}
public Slice createSliceWithGridRow(Uri sliceUri) {
if (getContext() == null) {
return null;
}
// Create the parent builder.
ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
.setHeader(
// Create the header.
new HeaderBuilder()
.setTitle("Famous restaurants")
.setPrimaryAction(SliceAction
.create(pendingIntent, icon, ListBuilder.ICON_IMAGE,
"Famous restaurants"))
)
// Add a grid row to the list.
.addGridRow(new GridRowBuilder()
// Add cells to the grid row.
.addCell(new CellBuilder()
.addImage(image1, ListBuilder.LARGE_IMAGE)
.addTitleText("Top Restaurant")
.addText("0.3 mil")
.setContentIntent(intent1)
).addCell(new CellBuilder()
.addImage(image2, ListBuilder.LARGE_IMAGE)
.addTitleText("Fast and Casual")
.addText("0.5 mil")
.setContentIntent(intent2)
)
.addCell(new CellBuilder()
.addImage(image3, ListBuilder.LARGE_IMAGE)
.addTitleText("Casual Diner")
.addText("0.9 mi")
.setContentIntent(intent3))
.addCell(new CellBuilder()
.addImage(image4, ListBuilder.LARGE_IMAGE)
.addTitleText("Ramen Spot")
.addText("1.2 mi")
.setContentIntent(intent4))
// Every slice needs a primary action.
.setPrimaryAction(createActivityAction())
);
return listBuilder.build();
}
RangeBuilder
RangeBuilder
를 사용하면 진행률 표시줄 또는 입력 범위(예: 슬라이더)가 포함된 행을 만들 수 있습니다.
다음 이미지에 진행률 표시줄 및 슬라이더 예가 표시되어 있습니다.
RangeBuilder 예 - 슬라이더
아래의 예는 InputRangeBuilder
를 사용하여 볼륨 슬라이더가 포함된 Slice를 만드는 방법을 보여줍니다. 진행률 표시줄 행을 구성하려면 addRange()
를 사용하세요.
fun createSliceWithRange(sliceUri: Uri): Slice {
return list(context, sliceUri, ListBuilder.INFINITY) {
inputRange {
title = "Ring Volume"
inputAction = volumeChangedPendingIntent
max = 100
value = 30
}
}
}
public Slice createSliceWithRange(Uri sliceUri) {
if (getContext() == null) {
return null;
}
// Construct the parent.
ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
.addRow(new RowBuilder() // Every slice needs a row.
.setTitle("Enter app")
// Every slice needs a primary action.
.setPrimaryAction(createActivityAction())
)
.addInputRange(new InputRangeBuilder() // Create the input row.
.setTitle("Ring Volume")
.setInputAction(volumeChangedPendingIntent)
.setMax(100)
.setValue(30)
);
return listBuilder.build();
}
지연된 콘텐츠
SliceProvider.onBindSlice()
에서 가능한 한 빠르게 Slice를 반환해야 합니다.
호출 시간이 오래 걸리면 깜박임 및 갑작스러운 크기 조절과 같은 디스플레이 문제가 발생할 수 있습니다.
빠르게 로드할 수 없는 Slice 콘텐츠가 있다면 빌더에서 콘텐츠가 로드 중임에 유의하면서 자리표시자 콘텐츠를 사용하여 Slice를 구성할 수 있습니다. 콘텐츠를 표시할 준비가 되면 Slice URI를 사용하여 getContentResolver().notifyChange(sliceUri, null)
을 호출합니다. 이렇게 하면 또 다른 SliceProvider.onBindSlice()
가 호출되어 새로운 콘텐츠로 Slice를 다시 구성할 수 있습니다.
지연 콘텐츠 예 - 직장으로 이동
아래의 직장으로 이동 예에서 직장까지의 거리는 동적으로 결정되며 즉시 사용하지 못할 수도 있습니다. 코드 예는 콘텐츠가 로드되는 동안 null 자막을 자리표시자로 사용하는 방법을 보여줍니다.
fun createSliceShowingLoading(sliceUri: Uri): Slice {
// We’re waiting to load the time to work so indicate that on the slice by
// setting the subtitle with the overloaded method and indicate true.
return list(context, sliceUri, ListBuilder.INFINITY) {
row {
title = "Ride to work"
setSubtitle(null, true)
addEndItem(IconCompat.createWithResource(context, R.drawable.ic_work), ICON_IMAGE)
}
}
}
public Slice createSliceShowingLoading(Uri sliceUri) {
if (getContext() == null) {
return null;
}
// Construct the parent.
ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
// Construct the row.
.addRow(new RowBuilder()
.setPrimaryAction(createActivityAction())
.setTitle("Ride to work")
// We’re waiting to load the time to work so indicate that on the slice by
// setting the subtitle with the overloaded method and indicate true.
.setSubtitle(null, true)
.addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_work),
ListBuilder.ICON_IMAGE)
);
return listBuilder.build();
}
private SliceAction createActivityAction() {
return SliceAction.create(
PendingIntent.getActivity(
getContext(),
0,
new Intent(getContext(), MainActivity.class),
0
),
IconCompat.createWithResource(getContext(), R.drawable.ic_home),
ListBuilder.ICON_IMAGE,
"Enter app"
);
}
Slice 내 사용 중지된 스크롤 처리
Slice 템플릿을 표시하는 표면은 템플릿 내에서 스크롤을 지원하지 않을 수 있습니다. 이 경우 일부 콘텐츠가 표시되지 않을 수도 있습니다.
예를 들어 Wi-Fi 네트워크 목록을 보여주는 Slice를 생각해 보세요.
Wi-Fi 목록이 길고 스크롤이 사용 중지된 경우 사용자가 목록의 모든 항목을 볼 수 있도록 더보기 버튼을 추가할 수 있습니다. 다음 예와 같이 addSeeMoreAction()
을 사용하여 이 버튼을 추가할 수 있습니다.
fun seeMoreActionSlice(sliceUri: Uri) =
list(context, sliceUri, ListBuilder.INFINITY) {
// [START_EXCLUDE]
// [END_EXCLUDE]
setSeeMoreAction(seeAllNetworksPendingIntent)
// [START_EXCLUDE]
// [END_EXCLUDE]
}
public Slice seeMoreActionSlice(Uri sliceUri) {
if (getContext() == null) {
return null;
}
ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY);
// [START_EXCLUDE]
listBuilder.addRow(new RowBuilder()
.setTitle("Hello")
.setPrimaryAction(createActivityAction())
);
// [END_EXCLUDE]
listBuilder.setSeeMoreAction(seeAllNetworksPendingIntent);
// [START_EXCLUDE]
// [END_EXCLUDE]
return listBuilder.build();
}
다음 이미지와 같이 표시됩니다.
더보기를 탭하면 seeAllNetworksPendingIntent
가 전송됩니다.
또는 맞춤 메시지 또는 행을 제공하려면 RowBuilder를 추가해 보세요.
fun seeMoreRowSlice(sliceUri: Uri) =
list(context, sliceUri, ListBuilder.INFINITY) {
// [START_EXCLUDE]
// [END_EXCLUDE]
seeMoreRow {
title = "See all available networks"
addEndItem(
IconCompat.createWithResource(context, R.drawable.ic_right_caret), ICON_IMAGE
)
primaryAction = SliceAction.create(
seeAllNetworksPendingIntent,
IconCompat.createWithResource(context, R.drawable.ic_wifi),
ListBuilder.ICON_IMAGE,
"Wi-Fi Networks"
)
}
}
public Slice seeMoreRowSlice(Uri sliceUri) {
if (getContext() == null) {
return null;
}
ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
// [START_EXCLUDE]
.addRow(new RowBuilder()
.setTitle("Hello")
.setPrimaryAction(createActivityAction())
)
// [END_EXCLUDE]
.setSeeMoreRow(new RowBuilder()
.setTitle("See all available networks")
.addEndItem(IconCompat
.createWithResource(getContext(), R.drawable
.ic_right_caret),
ListBuilder.ICON_IMAGE)
.setPrimaryAction(SliceAction.create(seeAllNetworksPendingIntent,
IconCompat.createWithResource(getContext(), R.drawable.ic_wifi),
ListBuilder.ICON_IMAGE,
"Wi-Fi Networks"))
);
// [START_EXCLUDE]
// [END_EXCLUDE]
return listBuilder.build();
}
이 메서드를 통해 추가된 행 또는 작업은 다음 조건 중 하나가 충족되는 때만 표시됩니다.
- Slice의 프레젠터가 뷰에서 스크롤을 사용 중지했습니다.
- 사용 가능한 공간에 일부 행을 표시할 수 없습니다.
템플릿 결합
여러 행 유형을 결합하여 서식 있는 동적 Slice를 만들 수 있습니다. 예를 들어 Slice에 헤더 행, 단일 이미지가 있는 그리드 및 텍스트 셀이 두 개 있는 그리드를 포함할 수 있습니다.
다음은 세 개의 셀이 포함된 그리드와 함께 헤더 행이 있는 Slice입니다.