1. Antes de começar
O que você aprenderá
- As experiências de usuário exclusivas possibilitadas pelo Android XR.
- Como otimizar um app para um headset Android XR usando a biblioteca Jetpack Compose XR.
- Como usar elementos da interface da biblioteca Jetpack Compose XR.
- Onde saber como criar aplicativos para Android XR.
O que este codelab não é
- Um guia para criar aplicativos Android XR sem usar o Compose. Consulte Desenvolver UI para aplicativos baseados em visualizações do Android.
- Um guia para criar aplicativos Unity ou OpenXR para Android XR. Consulte Desenvolver com Unity para Android XR e Desenvolver com OpenXR.
O que é necessário
- A versão de pré-lançamento mais recente do Android Studio.
- Um computador capaz de executar o emulador Android XR.
- Experiência com Kotlin e Jetpack Compose, por exemplo, concluindo o curso Noções básicas do Android com o Compose.
- Experiência na criação de dispositivos virtuais Android e execução de aplicativos neles.
- Teste os modos e painéis espaciais do Android XR, por exemplo, concluindo o codelab Aprender os fundamentos do Android XR (Parte 1): modos e painéis espaciais.
O que você vai criar
Neste codelab, você vai otimizar ainda mais um app com alguns recursos atuais do XR, adicionando elementos da interface flutuantes e personalizando o ambiente virtual que envolve o usuário enquanto ele usa o app.
Ponto de partida | Resultado final |
2. Começar a configuração
Acessar o código
- O código deste codelab pode ser encontrado no diretório
xr-fundamentals
do repositórioxr-codelabs
do GitHub. Para clonar o repositório, execute o seguinte comando:
git clone https://github.com/android/xr-codelabs.git
- Se preferir, baixe o repositório como um arquivo ZIP:
Abrir o projeto
- Depois de iniciar o Android Studio, importe o diretório
xr-fundamentals/part1
. O diretórioxr-fundamentals/part2
contém o código da solução, que você pode consultar a qualquer momento se tiver dificuldades ou apenas quiser conferir o projeto completo.
Conhecer o código
- Depois de abrir o projeto no Android Studio, analise o código inicial.
- Se você ainda não fez o primeiro codelab ou usou o emulador do Android XR, siga as etapas em Executar o app no emulador do Android XR.
3. Aprender os conceitos do XR: orbitadores
Os orbitadores são elementos da interface flutuantes disponíveis no Modo de Espaço Ampliado que geralmente são usados para controlar o conteúdo em painéis espaciais ou outras entidades em que a órbita está ancorada. O uso de orbitadores para controles de conteúdo dá mais espaço ao conteúdo, o que significa que os usuários podem acessar rapidamente os recursos contidos nos orbitadores enquanto o conteúdo principal permanece visível. Os orbitadores oferecem a versatilidade necessária para integrar componentes de interface atuais (como barras de navegação) ou criar novos.
Além disso, a API Orbiter permite renderizar o conteúdo de um orbitador onde ele normalmente seria executado no Modo de Espaço Compacto ou em um dispositivo não XR e o divide automaticamente em um orbitador quando executado no Modo de Espaço Ampliado.
No modo de Espaço Compacto, essa coluna de navegação é renderizada no painel principal do app. | No modo de Espaço Ampliado, a coluna de navegação é dividida em um orbitador anexado ao painel principal. |
Nesse ponto, o app tem um botão na barra de apps na parte de cima para mudar entre o Modo de Espaço Compacto e o Modo de Espaço Ampliado. Esse botão é um exemplo perfeito de um controle que pode ser contido em um orbitador quando executado no modo de Espaço Ampliado. Mover o controle para orbitar o painel principal ajuda a destacar o controle e indica visualmente que ele vai reduzir o conteúdo do app para esse painel quando clicado.
O estado atual | O que você vai implementar |
Para saber mais sobre as considerações de design para orbitadores, consulte Interface espacial.
4. Adicionar um orbitador
Unir o botão ativar/desativar do modo de espaço
Para transformar o botão do modo de espaço em um orbitador, una o elemento combinável ToggleSpaceModeButton
em um elemento combinável Orbiter
.
ui/component/XRFundamentalsTopAppBar .kt
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import androidx.xr.compose.spatial.EdgeOffset
import androidx.xr.compose.spatial.Orbiter
import androidx.xr.compose.spatial.OrbiterEdge
import androidx.xr.compose.subspace.layout.SpatialRoundedCornerShape
...
Orbiter(
position = OrbiterEdge.Top,
alignment = Alignment.End,
offset = EdgeOffset.inner(16.dp),
shape = SpatialRoundedCornerShape(
CornerSize(percent = 100)
)
) {
ToggleSpaceModeButton()
}
Agora, execute o app: ao executar no modo de Espaço Compacto, você vai notar que nada mudou. No entanto, quando você clica no botão e o app entra no modo de Espaço Ampliado, o botão não está mais localizado na barra de apps superior, mas na borda superior direita do painel espacial principal.
O orbitador é ancorado ao painel espacial principal porque é a entidade espacial pai mais próxima na árvore da UI. A posição exata do orbitador em relação ao painel espacial principal é determinada pelos parâmetros position
, alignment
e offset
. Tente modificar esses parâmetros para conferir os comportamentos compatíveis.
Modo de Espaço Compacto | Modo de Espaço Ampliado |
5. Aprender os conceitos do XR: ambientes espaciais
Usar um Orbiter
para personalizar a posição no espaço 3D dos elementos da interface é uma ótima maneira de melhorar a experiência do usuário em dispositivos XR. Para melhorar ainda mais a experiência, personalize o ambiente espacial em que os usuários ficam quando usam o app.
Os ambientes espaciais podem incorporar recursos de profundidade, textura e geometria 3D para criar uma experiência visual rica e imersiva. Isso é feito usando uma imagem esférica skybox (no formato EXR) para fornecer um plano de fundo panorâmico distante e/ou um recurso de geometria (no formato glTF) para fornecer elementos em primeiro e segundo plano que podem ser mesclados em uma skybox. Por exemplo, um app de streaming de vídeo pode usar uma skybox noturna com um glTF de um cinema drive-in com uma tela de projeção e carros. Ao criar recursos para definir o ambiente espacial dos usuários, garanta que eles tenham resolução de alta qualidade e um tamanho de arquivo razoável. Consulte Otimizar recursos do ambiente para mais informações.
Além disso, a opacidade do ambiente espacial pode ser controlada. Isso permite transmitir vídeo do mundo real e o misturar ao ambiente virtual, o que pode ajudar os usuários a se orientarem
Na próxima etapa, você vai adicionar um recurso de geometria ao app e criar um menu para permitir que o usuário escolha o ambiente.
Para saber todos os detalhes sobre o design e a implementação de ambientes espaciais, consulte Ambientes espaciais e Adicionar ambientes espaciais ao app.
6. Permitir que os usuários mudem o ambiente espacial
Como os apps controlam o ambiente espacial
Antes de começar, é bom entender exatamente como os apps podem controlar o ambiente espacial.
Ao contrário do conteúdo nos painéis, os apps não controlam diretamente o ambiente. Em vez disso, eles podem interagir com a sessão do SceneCore para fornecer uma preferência para o ambiente que querem que o sistema use. Essa preferência é representada por uma SpatialEnvironmentPreference
, que consiste em uma imagem EXR de skybox e/ou um gLTF de geometria. O que acontece quando o app fornece uma preferência depende dos recursos dele ao definir a preferência. Se o app tiver capacidade de mudar o ambiente, o sistema vai fazer isso imediatamente. Caso contrário, a preferência será aplicada quando o app ganhar esse recurso.
Por exemplo, os apps geralmente não conseguem mudar o ambiente enquanto são executados no Modo de Espaço Compacto, mas geralmente têm quando são executados no Modo de Espaço Ampliado. Se você permitir que um usuário defina uma preferência de ambiente no Modo de Espaço Compacto, essa preferência geralmente não vai entrar em vigor até que o app esteja em execução no Modo de Espaço Ampliado.
Adicionar uma dependência à biblioteca XR SceneCore
Para começar a modificar o ambiente espacial, adicione uma dependência à biblioteca XR SceneCore, que você vai usar para carregar os recursos do ambiente e definir as preferências dele. Também é necessário adicionar uma dependência ao artefato kotlinx-coroutines-guava
, já que algumas APIs para carregar recursos usam o tipo de dados ListenableFuture
.
libs.version.toml
[versions]
...
xrSceneCore = "1.0.0-alpha04"
kotlinxCoroutinesGuava = "1.10.2"
[libraries]
...
androidx-xr-scenecore = { group = "androidx.xr.scenecore", name = "scenecore", version.ref = "xrSceneCore"}
jetbrains-kotlinx-coroutines-guava = {group = "org.jetbrains.kotlinx", name="kotlinx-coroutines-guava", version.ref = "kotlinxCoroutinesGuava"}
app/build.gradle.kts
dependencies {
...
implementation(libs.androidx.xr.scenecore)
implementation(libs.jetbrains.kotlinx.coroutines.guava)
...
}
Adicionar um recurso de ambiente ao projeto
Para especificar uma preferência de ambiente exclusiva, você vai precisar de um recurso de céu e/ou de geometria. Neste codelab, você vai usar apenas o recurso de geometria green_hills_ktx2_mipmap.glb
, que pode ser encontrado na pasta part2
que contém o código da solução ou no GitHub (link em inglês).
- Clique com o botão direito do mouse no módulo do app na janela "Project" do Android Studio. Em seguida, selecione New > Folder > Assets Folder e clique em Finish para criar a pasta.
- Adicione o arquivo GLB à pasta
app/src/main/assets
que você acabou de criar.
Modelar as opções de ambiente
Para simplificar a interação entre o código da UI e as APIs do sistema, crie uma classe de dados do Kotlin para modelar cada opção de ambiente.
- Clique com o botão direito do mouse no pacote
com.example.android.xrfundamentals
na janela do projeto e selecione New > Package. Digitecom.example.android.xrfundamentals.environment
como o nome do pacote. - Clique com o botão direito do mouse no pacote e selecione New > Kotlin Class/File. Insira
EnvironmentOption
como o nome e clique no tipo Data class. - Adicione o código abaixo no arquivo que você acabou de criar:
EnvironmentOption.kt
data class EnvironmentOption(val name: String, val skyboxPath: String?, val geometryPath: String?)
val DEFAULT_ENVIRONMENT = EnvironmentOption("Default", null, null)
val ENVIRONMENT_OPTIONS = listOf(
DEFAULT_ENVIRONMENT,
EnvironmentOption("Green Hills", null, "green_hills_ktx2_mipmap.glb")
)
Adicionar um auxiliar para criar recursos de carregamento e retornar a SpatialEnvironmentPreference
Em seguida, adicione um método auxiliar à classe de dados para facilitar a conversão de uma EnvironmentOption
na SpatialEnvrionmentPreference
correspondente.
EnvironmentOption.kt
import androidx.xr.runtime.Session
import androidx.xr.scenecore.ExrImage
import androidx.xr.scenecore.GltfModel
import androidx.xr.scenecore.SpatialEnvironment
import kotlinx.coroutines.guava.await
...
data class EnvironmentOption(val name: String, val skyboxPath: String?, val geometryPath: String?) {
suspend fun toSpatialEnvironmentPreference(session: Session): SpatialEnvironmentPreference? {
if (skyboxPath == null && geometryPath == null) {
return null
} else {
val skybox = skyboxPath?.let {
ExrImage.create(session, it).await()
}
val geometry = geometryPath?.let {
GltfModel.create(session, it).await()
}
return SpatialEnvironmentPreference(skybox, geometry)
}
}
}
Há algumas observações importantes:
- Se a skybox e a geometria forem nulas, o valor "null" será retornado para indicar que a preferência de ambiente padrão do sistema deve ser usada. Consulte
setSpatialEnvironmentPreference
para mais informações. - Os recursos
skybox
egeometry
são criados de forma assíncrona porque esses recursos geralmente são muito grandes e demoram para ser lidos na memória. Em um app de produção, convém armazenar esses recursos em cache na memória se você mudar os ambientes com frequência.
Implementar a UI de seleção do ambiente
Para implementar a UI, adicione um segundo orbitador que gire pelas opções de ambiente quando clicado.
Adicionar o orbitador
- Clique com o botão direito do mouse no módulo
app
na janela "Project" e selecione New > Vector Asset. Clique no campo Clip art, pesquise e selecione o recursolandscape
(da família de ícones Filled), clique em OK e em Next para criar o recurso. - Clique com o botão direito do mouse no pacote
com.example.android.xrfundamentals.ui.component
e selecione New > Kotlin Class/File. InsiraEnvironmentSelectionOrbiter
como o nome e clique no tipo File. - No arquivo que você acabou de criar, adicione a implementação do elemento combinável
EnvironmentSelectionOrbiter
abaixo.
EnvironmentSelectionOrbiter.kt
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.xr.compose.spatial.EdgeOffset
import androidx.xr.compose.spatial.Orbiter
import androidx.xr.compose.spatial.OrbiterEdge
import androidx.xr.compose.subspace.layout.SpatialRoundedCornerShape
import com.example.android.xrfundamentals.R
@Composable
fun EnvironmentSelectionOrbiter(
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
) {
Orbiter(
position = OrbiterEdge.Top,
alignment = Alignment.Start,
offset = EdgeOffset.inner(16.dp),
shape = SpatialRoundedCornerShape(
CornerSize(100)
)
) {
FilledTonalIconButton(
modifier = modifier,
onClick = onClick,
) {
Icon(painterResource(R.drawable.baseline_landscape_24), "Show environment selection dialog")
}
}
}
- Por fim, adicione o
EnvironmentSelectionOrbiter
no painel espacial principal.
XRFundamentalsApp.kt
import androidx.xr.compose.platform.LocalSpatialCapabilities
import com.example.android.xrfundamentals.ui.component.EnvironmentSelectionOrbiter
...
SpatialPanel(...) {
// Only show the environment selection orbiter if the app is actually able to
// change the environment
if (LocalSpatialCapabilities.current.isAppEnvironmentEnabled) {
EnvironmentSelectionOrbiter(
onClick = { TODO() }
)
}
...
}
Mudar o ambiente quando o orbitador for clicado
Para que tudo funcione, há uma última etapa, que é chamar setSpatialEnvironmentPreference
no gerenciador de cliques do EnvironmentSelectionOrbiter
.
- Configure uma variável para rastrear a opção de ambiente atual (fora do
Subspace
, para que o estado seja mantido ao alternar entre os modos de Espaço Compacto e Espaço Ampliado). Além disso, crie variáveis para a sessão atual de XR e um escopo de corrotina para chamar o auxiliartoSpatialEnvironmentPreference
.
XRFundamentalsApp.kt
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.xr.compose.platform.LocalSession
...
var currentEnvironmentOptionIndex by remember { mutableStateOf(0) }
Subspace {
val session = checkNotNull(LocalSession.current)
val scope = rememberCoroutineScope()
...
}
- Implemente o callback
onClick
para mudar entre as opções de ambiente.
XRFundamentalsApp.kt
EnvironmentSelectionOrbiter(
onClick = {
scope.launch {
currentEnvironmentOptionIndex =
(currentEnvironmentOptionIndex + 1) % ENVIRONMENT_OPTIONS.size
session.scene.spatialEnvironment.setSpatialEnvironmentPreference(
ENVIRONMENT_OPTIONS[currentEnvironmentOptionIndex].toSpatialEnvironmentPreference(
session
)
)
}
}
)
Execute o app mais uma vez e confira se você pode mudar entre "Green Hills" e os ambientes padrão.
7. Parabéns
Para continuar aprendendo sobre como aproveitar ao máximo o XR, confira os seguintes recursos e exercícios:
Leia mais
- O Design para XR aborda os princípios de design e as práticas recomendadas para criar aplicativos Android XR.
- Desenvolver com o SDK do Jetpack para XR contém orientações técnicas sobre as APIs e as ferramentas que você pode usar na criação da experiência no Android XR.
- A página Diretrizes de qualidade do aplicativo Android XR descreve os critérios para criar uma ótima experiência do usuário.
- Analise o exemplo do Hello Android XR.
Desafios
- Encontre ou crie outros recursos de ambiente e adicione-os como opções.
- Modifique o controlador de ambiente e a interface para permitir que o usuário defina as preferências de passagem usando a API
setPassthroughOpacityPreference
. O controle de passagem é limitado por um recurso diferente do que a mudança dos recursos do ambiente.