В этом документе представлена подробная информация о том, как использовать конструкторы шаблонов в 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(); }
Строка или действие, добавленные с помощью этого метода, отображаются только при выполнении одного из следующих условий:
- Ведущий вашего фрагмента отключил прокрутку в представлении.
- Не все ваши строки могут быть отображены на доступном месте.
Объединить шаблоны
Вы можете создать богатый динамический срез, объединив несколько типов строк. Например, фрагмент может содержать строку заголовка, сетку с одним изображением и сетку с двумя ячейками текста.
Вот фрагмент со строкой заголовка и сеткой, содержащей три ячейки.