Com o Jetpack Compose para XR, é possível criar de forma declarativa a interface espacial e o layout usando conceitos conhecidos do Compose, como linhas e colunas. Isso permite que você estenda a interface do Android para o espaço 3D ou crie aplicativos 3D imersivos completamente novos.
Se você estiver espacializando um app baseado em Android Views, terá várias opções de desenvolvimento. Você pode usar APIs de interoperabilidade, usar o Compose e as visualizações juntos ou trabalhar diretamente com a biblioteca SceneCore. Consulte nosso guia de trabalho com visualizações para mais detalhes.
Sobre subespaços e componentes espaciais
Ao criar seu app para o Android XR, é importante entender os conceitos de subespaço e componentes espacializados.
Sobre o Subspace
Ao desenvolver para o Android XR, você precisa adicionar um subspace ao app ou layout. Um subespaço é uma partição do espaço 3D no app em que você pode colocar conteúdo 3D, criar layouts 3D e adicionar profundidade a conteúdo 2D. Um subespaço é renderizado somente quando a espacialização está ativada. No espaço doméstico ou em dispositivos que não são XR, qualquer código nesse subspace é ignorado.
Há duas maneiras de criar um subespaço:
setSubspaceContent()
: essa função cria um subespaço no nível do app. Ele pode ser chamado na atividade principal da mesma forma que você usasetContent()
. Um subspace no nível do app não tem limite de altura, largura e profundidade, fornecendo essencialmente uma tela infinita para conteúdo espacial.Subspace
: esse elemento combinável pode ser colocado em qualquer lugar na hierarquia da interface do app, permitindo que você mantenha layouts para interfaces 2D e espaciais sem perder o contexto entre os arquivos. Isso facilita o compartilhamento de coisas como a arquitetura de apps existente entre XR e outros formatos sem precisar elevar o estado em toda a árvore de interface ou reprojetar o app.
Para mais informações, consulte Adicionar um subspace ao app.
Sobre os componentes espaciais
Combináveis de subespaço: esses componentes só podem ser renderizados em um subespaço.
Elas precisam ser incluídas em Subspace
ou setSubspaceContent
antes de serem
colocadas em um layout 2D. Um SubspaceModifier
permite adicionar
atributos como profundidade, deslocamento e posicionamento aos elementos combináveis do subespaço.
Outros componentes espaciais não precisam ser chamados em um subspace. Eles consistem em elementos 2D convencionais unidos em um contêiner espacial. Esses elementos podem ser usados em layouts 2D ou 3D, se definidos para ambos. Quando a espacialização não está ativada, os recursos espaciais são ignorados e voltam para as versões 2D.
Criar um painel espacial
Um SpatialPanel
é um elemento combinável de subespaço que permite mostrar
o conteúdo do app. Por exemplo, é possível mostrar a reprodução de vídeo, imagens estáticas ou qualquer
outro conteúdo em um painel espacial.
Use SubspaceModifier
para mudar o tamanho, o comportamento e o posicionamento do
painel espacial, conforme mostrado no exemplo a seguir.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
}
}
// 2D content placed within the spatial panel
@Composable
fun SpatialPanelContent(){
Box(
Modifier
.background(color = Color.Black)
.height(500.dp)
.width(500.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "Spatial Panel",
color = Color.White,
fontSize = 25.sp
)
}
}
Pontos principais sobre o código
- Como as APIs
SpatialPanel
são combináveis de subespaço, é preciso chamá-las dentro deSubspace
ousetSubspaceContent
. Chamar esses métodos fora de um subespaço gera uma exceção. - Permita que o usuário redimensione ou mova o painel adicionando os modificadores
movable
ouresizable
. - Consulte nossas orientações de design de painel espacial para saber mais sobre dimensionamento e posicionamento. Consulte nossa documentação de referência para mais detalhes sobre a implementação do código.
Criar um orbitador
Um orbitador é um componente de interface espacial. Ele foi projetado para ser anexado a um painel espacial, layout ou outra entidade correspondente. Um orbitador normalmente contém itens de navegação e ação contextual relacionados à entidade a que ele está anexado. Por exemplo, se você criou um painel espacial para exibir conteúdo em vídeo, é possível adicionar controles de reprodução de vídeo dentro de um orbitador.
Como mostrado no exemplo abaixo, chame um orbitador dentro do layout 2D em um
SpatialPanel
para agrupar os controles do usuário, como a navegação. Isso extrai os elementos
do layout 2D e os anexa ao painel espacial de acordo com a
configuração.
setContent {
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
OrbiterExample()
}
}
}
//2D content inside Orbiter
@Composable
fun OrbiterExample() {
Orbiter(
position = OrbiterEdge.Bottom,
offset = 96.dp,
alignment = Alignment.CenterHorizontally
) {
Surface(Modifier.clip(CircleShape)) {
Row(
Modifier
.background(color = Color.Black)
.height(100.dp)
.width(600.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Orbiter",
color = Color.White,
fontSize = 50.sp
)
}
}
}
}
Pontos principais sobre o código
- Como os orbiters são componentes de interface espacial, o código pode ser reutilizado em layouts 2D ou 3D. Em um layout 2D, o app renderiza apenas o conteúdo dentro do orbitador e ignora o próprio orbitador.
- Confira nossas orientações de design para mais informações sobre como usar e projetar orbitadores.
Adicionar vários painéis espaciais a um layout espacial
É possível criar vários painéis espaciais e colocá-los em um layout espacial
usando SpatialRow
, SpatialColumn
,
SpatialBox
e SpatialLayoutSpacer
.
O exemplo de código abaixo mostra como fazer isso.
Subspace {
SpatialRow {
SpatialColumn {
SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
SpatialPanelContent("Top Left")
}
SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) {
SpatialPanelContent("Middle Left")
}
SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
SpatialPanelContent("Bottom Left")
}
}
SpatialColumn {
SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
SpatialPanelContent("Top Right")
}
SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) {
SpatialPanelContent("Middle Right")
}
SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
SpatialPanelContent("Bottom Right")
}
}
}
}
@Composable
fun SpatialPanelContent(text: String) {
Column(
Modifier
.background(color = Color.Black)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Panel",
color = Color.White,
fontSize = 15.sp
)
Text(
text = text,
color = Color.White,
fontSize = 25.sp,
fontWeight = FontWeight.Bold
)
}
}
Pontos principais sobre o código
SpatialRow
,SpatialColumn
,SpatialBox
eSpatialLayoutSpacer
são todos elementos combináveis de subespaço e precisam ser colocados em um subespaço.- Use
SubspaceModifier
para personalizar o layout. - Para layouts com vários painéis em uma linha, recomendamos definir um raio de curva
de 825 dp usando um
SubspaceModifier
para que os painéis rodem em torno do usuário. Consulte nossas orientações de design para mais detalhes.
Use um volume para colocar um objeto 3D no layout
Para colocar um objeto 3D no layout, você precisa usar um elemento combinável de subespaço chamado volume. Confira um exemplo de como fazer isso.
Subspace {
SpatialPanel(
SubspaceModifier.height(1500.dp).width(1500.dp)
.resizable().movable()
) {
ObjectInAVolume(true)
Box(
Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = "Welcome",
fontSize = 50.sp,
)
}
}
}
}
@Composable
fun ObjectInAVolume(show3DObject: Boolean) {
val xrCoreSession = checkNotNull(LocalSession.current)
val scope = rememberCoroutineScope()
if (show3DObject) {
Subspace {
Volume(
modifier = SubspaceModifier
.offset(volumeXOffset, volumeYOffset, volumeZOffset) //
Relative position
.scale(1.2f) // Scale to 120% of the size
) { parent ->
scope.launch {
// Load your 3D Object here
}
}
}
}
}
Pontos principais sobre o código
- Consulte Adicionar modelos 3D ao app para entender melhor como carregar conteúdo 3D em um volume.
Adicionar outros componentes de interface espacial
Os componentes de IU espacial podem ser colocados em qualquer lugar na hierarquia de IU do aplicativo. Esses elementos podem ser reutilizados na interface 2D, e os atributos espaciais só serão visíveis quando os recursos espaciais estiverem ativados. Isso permite adicionar elevação a menus, caixas de diálogo e outros componentes sem precisar escrever o código duas vezes. Confira os exemplos de interface espacial a seguir para entender melhor como usar esses elementos.
Componente da interface |
Quando a espacialização está ativada |
Em um ambiente 2D |
---|---|---|
|
O painel vai se afastar um pouco na profundidade Z para mostrar uma caixa de diálogo elevada. |
Volta para 2D |
|
O painel vai ser empurrado ligeiramente para trás na profundidade z para mostrar um pop-up elevado. |
Volta para um |
|
|
Mostra sem elevação espacial. |
SpatialDialog
Este é um exemplo de caixa de diálogo que é aberta após um breve atraso. Quando
SpatialDialog
é usado, a caixa de diálogo aparece na mesma profundidade
que o painel espacial, e o painel é empurrado para trás em 125dp quando a espacialização
é ativada. SpatialDialog
também pode ser usado quando a espacialização não está ativada.
Nesse caso, SpatialDialog
volta para a versão 2D, Dialog
.
@Composable
fun DelayedDialog() {
var showDialog by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
Handler(Looper.getMainLooper()).postDelayed({
showDialog = true
}, 3000)
}
if (showDialog) {
SpatialDialog (
onDismissRequest = { showDialog = false },
SpatialDialogProperties(
dismissOnBackPress = true)
){
Box(Modifier
.height(150.dp)
.width(150.dp)
) {
Button(onClick = { showDialog = false }) {
Text("OK")
}
}
}
}
}
Pontos principais sobre o código
- Este é um exemplo de
SpatialDialog
. O uso deSpatialPopUp
eSpatialElevation
é muito semelhante. Consulte nossa referência da API para mais detalhes.
Criar painéis e layouts personalizados
Para criar painéis personalizados que não têm suporte do Compose para XR, você pode trabalhar
diretamente com PanelEntities
e a cena usando as APIs
SceneCore
.
Fixar orbitadores em layouts espaciais e outras entidades
É possível ancorar um orbitador a qualquer entidade declarada no Compose. Isso envolve
declarar um orbitador em um layout espacial de elementos da interface, como SpatialRow
, SpatialColumn
ou SpatialBox
. O Orbiter é ancorado à entidade pai
mais próxima de onde foi declarado.
O comportamento do orbitador é determinado por onde você o declara:
- Em um layout 2D agrupado em um
SpatialPanel
(como mostrado em um snippet de código anterior), o orbitador é ancorado a esseSpatialPanel
. - Em um
Subspace
, o orbitador é ancorado à entidade pai mais próxima, que é o layout espacial em que o orbitador é declarado.
O exemplo a seguir mostra como ancorar um orbitador a uma linha espacial:
Subspace {
SpatialRow {
Orbiter(
position = OrbiterEdge.Top,
offset = EdgeOffset.inner(8.dp),
shape = SpatialRoundedCornerShape(size = CornerSize(50))
) {
Text(
"Hello World!",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier
.background(Color.White)
.padding(16.dp)
)
}
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
) {
Box(
modifier = Modifier
.background(Color.Red)
)
}
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
) {
Box(
modifier = Modifier
.background(Color.Blue)
)
}
}
}
Pontos principais sobre o código
- Quando você declara um orbitador fora de um layout 2D, ele é ancorado à
entidade pai mais próxima. Nesse caso, o orbitador é ancorado na parte de cima do
SpatialRow
em que ele é declarado. - Layouts espaciais, como
SpatialRow
,SpatialColumn
,SpatialBox
, têm entidades sem conteúdo associadas a eles. Portanto, um orbitador declarado em um layout espacial é ancorado a esse layout.
Veja também
- Adicionar modelos 3D ao app
- Desenvolver interfaces para apps baseados em visualizações do Android
- Implementar o Material Design para XR