Este documento traz detalhes sobre como usar os builders de modelo no Android Jetpack para criar Slices.
Definir seu modelo Slice
Os Slices são criados com um
ListBuilder
. O ListBuilder
permite adicionar diferentes tipos de linhas que são exibidas em uma lista. Esta
seção descreve cada um desses tipos de linha e como eles são criados.
SliceAction
O elemento mais básico de um modelo Slice é uma
SliceAction
. Uma SliceAction
contém uma etiqueta junto com um
PendingIntent
e pode ser um dos
itens a seguir:
- Botão de ícone
- Alternância padrão
- Alternância personalizada (drawable com um estado ativado/desativado)
A SliceAction
é usada pelos builders de modelo descritos no restante desta
seção. Uma SliceAction
pode ter um modo de imagem definido que determina como a
imagem será apresentada para a ação:
ICON_IMAGE
: tamanho muito pequeno e coloridaSMALL_IMAGE
: tamanho pequeno e não coloridaLARGE_IMAGE
: tamanho máximo e não colorida
HeaderBuilder
Na maioria dos casos, é preciso definir um cabeçalho para o modelo usando um
HeaderBuilder
.
Um cabeçalho pode ser compatível com:
- Título
- Subtítulo
- Subtítulo de resumo
- Ação principal
Veja abaixo alguns exemplos de configurações de cabeçalho. As caixas cinza mostram possíveis locais de ícones e padding:
Renderização de cabeçalho em diferentes superfícies
Quando um Slice é necessário, a superfície em exibição determina como renderizá-lo. A renderização pode apresentar algumas diferenças entre as superfícies de hospedagem.
Em formatos menores, apenas o cabeçalho, se existir, costuma ser exibido. Se você especificou um resumo para o cabeçalho, o texto do resumo será exibido em vez do texto do subtítulo.
Se você não especificou um cabeçalho no modelo, a primeira linha adicionada ao
ListBuilder
é geralmente exibida.
Exemplo de HeaderBuilder: Slice de lista simples com cabeçalho
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 nos cabeçalhos
Os cabeçalhos de Slices também podem exibir 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
Você pode criar uma linha de conteúdo por meio de um
RowBuilder
. Uma linha
pode ser compatível com qualquer destes itens:
- Título
- Subtítulo
- Item de início: SliceAction, ícone ou carimbo de data/hora
- Itens de término: SliceAction, ícone ou carimbo de data/hora
- Ação principal
É possível combinar conteúdos de linha de várias formas, de acordo com as seguintes restrições:
- Os itens de início não são exibidos na primeira linha de um Slice.
- Os itens de término não podem ser uma mistura de objetos
SliceAction
eIcon
. - Uma linha pode conter apenas um carimbo de data/hora.
Veja exemplos de linha de conteúdo nas imagens a seguir. As caixas cinza mostram possíveis locais de ícones e padding:
Exemplo de RowBuilder: alternância de Wi-Fi
O exemplo abaixo demonstra uma linha com uma ação principal e uma alternância padrão.
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
Você pode criar uma grade de conteúdo por meio de um
GridBuilder
. Uma grade pode
ser compatível com os seguintes tipos de imagem:
ICON_IMAGE
: tamanho muito pequeno e coloridaSMALL_IMAGE
: tamanho pequeno e não coloridaLARGE_IMAGE
: tamanho máximo e não colorida
Uma célula de grade é criada com um
CellBuilder
. Uma
célula pode ser compatível com até duas linhas de texto e uma imagem. Uma célula não pode ficar vazia.
Veja exemplos de grade nas imagens a seguir:
Exemplo de GridRowBuilder: restaurantes por perto
O exemplo abaixo demonstra uma linha de grade que contém imagens e texto.
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
Com um
RangeBuilder
,
você pode criar uma linha que contenha uma barra de progresso ou um intervalo de entrada, como
um controle deslizante.
Veja exemplos de barra de progresso e controle deslizante nas seguintes imagens:
Exemplo de RangeBuilder: controle deslizante
O exemplo abaixo demonstra como criar um Slice que contém um controle
deslizante de volume por meio de um InputRangeBuilder
. Para criar uma linha de progresso, use
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();
}
Conteúdo atrasado
Retorne um Slice o mais rápido possível a partir de
SliceProvider.onBindSlice()
.
Chamadas demoradas podem causar problemas de exibição, como oscilações e redimensionamento
abrupto.
Se você tiver conteúdo de Slice que não pode ser carregado rapidamente, crie o
Slice com conteúdo de marcador e observe no builder que o
conteúdo está carregando. Quando o conteúdo estiver pronto para exibição, chame
getContentResolver().notifyChange(sliceUri, null)
por meio do URI do Slice. Isso resultará em outra chamada para
SliceProvider.onBindSlice()
, em que você pode criar o Slice novamente com conteúdo
novo.
Exemplo de conteúdo atrasado: deslocamento para o trabalho
Na linha "Ride to work" abaixo, a distância até o trabalho é determinada dinamicamente e pode não estar disponível de imediato. O exemplo de código demonstra o uso de um subtítulo nulo como marcador enquanto o conteúdo é carregado:
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"
);
}
Processar rolagem desativada no Slice
A superfície que apresenta seu modelo Slice pode não ser compatível com a rolagem no modelo. Nesse caso, parte do conteúdo pode não ser exibida.
Por exemplo, considere um Slice que exibe uma lista de redes Wi-Fi:
Se a lista de Wi-Fi for longa e a rolagem estiver desativada, você poderá adicionar um
botão Ver mais para que os usuários possam ver todos os itens
da lista. Para adicionar esse botão, use
addSeeMoreAction()
,
conforme mostrado no exemplo a seguir:
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();
}
O resultado pode ser visto nesta imagem:
O toque em Ver mais envia seeAllNetworksPendingIntent
.
Como alternativa, se você quiser mostrar uma mensagem ou linha personalizada, adicione um 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();
}
A linha ou ação adicionada por esse método é exibida apenas quando uma das seguintes condições é atendida:
- O apresentador do Slice desativou a rolagem na visualização.
- Nem todas as linhas podem ser exibidas no espaço disponível.
Combinar modelos
É possível criar um Slice avançado e dinâmico combinando vários tipos de linha. Por exemplo, um Slice pode conter uma linha de cabeçalho, uma grade com uma única imagem e uma grade com duas células de texto.
Veja um Slice com uma linha de cabeçalho e uma grade com três células.