В этом документе представлена подробная информация о том, как использовать конструкторы шаблонов в Android Jetpack для создания Slices .
Определите свой шаблон Slice
Срезы создаются с помощью ListBuilder
. ListBuilder позволяет добавлять различные типы строк, отображаемых в списке. В этом разделе описывается каждый из этих типов строк и способы их построения.
SliceAction
Самый простой элемент шаблона Slice — это SliceAction
. SliceAction
содержит метку вместе с PendingIntent
и является одним из следующих:
- Кнопка со значком
- Переключение по умолчанию
- Пользовательский переключатель (рисунок с состоянием включения/выключения)
SliceAction
используется конструкторами шаблонов, описанными в оставшейся части этого раздела. Для SliceAction
может быть определен режим изображения, определяющий, как изображение будет представлено для действия:
-
ICON_IMAGE
: крошечный размер и возможность тонирования. -
SMALL_IMAGE
: небольшой размер и не тонируемый. -
LARGE_IMAGE
: самый большой размер и не тонируемый.
Заголовокстроитель
В большинстве случаев вам следует установить заголовок для вашего шаблона с помощью HeaderBuilder
. Заголовок может поддерживать следующее:
- Заголовок
- Субтитры
- Сводный подзаголовок
- Первичное действие
Некоторые примеры конфигураций заголовков показаны ниже. Обратите внимание, что серые прямоугольники показывают потенциальные местоположения значков и полей:
Рендеринг заголовка на разных поверхностях
Когда необходим срез, поверхность отображения определяет, как визуализировать срез. Обратите внимание, что рендеринг может несколько отличаться в зависимости от хостинга.
В меньших форматах обычно отображается только заголовок, если он существует. Если вы указали сводку для заголовка, текст сводки отображается вместо текста подзаголовка.
Если вы не указали заголовок в своем шаблоне, вместо него обычно отображается первая строка, добавленная в ваш ListBuilder
.
Пример HeaderBuilder — простой фрагмент списка с заголовком
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();
}
SliceActions в заголовках
Заголовки фрагментов также могут отображать SliceActions:
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
. Строка может поддерживать любое из следующих действий:
- Заголовок
- Субтитры
- Начальный элемент: SliceAction, Icon или метка времени.
- Конечные элементы: SliceAction, Icon или временная метка.
- Первичное действие
Содержимое строк можно комбинировать разными способами с учетом следующих ограничений:
- Начальные элементы не будут отображаться в первой строке фрагмента.
- Конечные элементы не могут быть смесью объектов
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 — слайдер
В приведенном ниже примере показано, как создать Slice, содержащий ползунок громкости, с помощью InputRangeBuilder
. Чтобы создать строку прогресса, используйте 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();
}
Задержанный контент
Вы должны как можно быстрее вернуть Slice из SliceProvider.onBindSlice()
. Отнимающие много времени вызовы могут привести к проблемам с отображением, таким как мерцание и резкое изменение размера.
Если у вас есть контент Slice, который невозможно загрузить быстро, вы можете создать свой Slice с содержимым-заполнителем, отметив при этом в конструкторе, что контент загружается. Как только контент будет готов к отображению, вызовите getContentResolver().notifyChange(sliceUri, null)
используя URI Slice. Это приводит к еще одному вызову SliceProvider.onBindSlice()
, где вы можете снова создать Slice с новым содержимым.
Пример задержанного контента: поездка на работу
В строке «Поездка на работу» ниже расстояние до работы определяется динамически и может быть доступно не сразу. В примере кода показано использование нулевого подзаголовка в качестве заполнителя во время загрузки контента:
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, может не поддерживать прокрутку внутри шаблона. В этом случае часть вашего контента может не отображаться.
В качестве примера рассмотрим фрагмент, показывающий список сетей Wi-Fi:
Если список 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();
}
Строка или действие, добавленные с помощью этого метода, отображаются только при выполнении одного из следующих условий:
- Ведущий вашего фрагмента отключил прокрутку в представлении.
- Не все ваши строки могут быть отображены на доступном месте.
Объединить шаблоны
Вы можете создать богатый динамический срез, объединив несколько типов строк. Например, фрагмент может содержать строку заголовка, сетку с одним изображением и сетку с двумя ячейками текста.
Вот фрагмент со строкой заголовка и сеткой, содержащей три ячейки.