Разработка пользовательского интерфейса с помощью Jetpack Compose для XR

С помощью Jetpack Compose для XR вы можете декларативно создавать пространственный пользовательский интерфейс и макет, используя знакомые концепции Compose, такие как строки и столбцы. Это позволяет расширить существующий пользовательский интерфейс Android в трехмерном пространстве или создавать совершенно новые иммерсивные трехмерные приложения.

Если вы занимаетесь пространственным определением существующего приложения на базе Android Views, у вас есть несколько вариантов разработки. Вы можете использовать API-интерфейсы совместимости, использовать Compose и Views вместе или работать напрямую с библиотекой SceneCore. Более подробную информацию можно найти в нашем руководстве по работе с представлениями .

О подпространствах и пространственных компонентах

Когда вы пишете приложение для Android XR, важно понимать концепции подпространства и пространственных компонентов .

О подпространстве

При разработке для Android XR вам потребуется добавить подпространство в ваше приложение или макет. Подпространство — это раздел 3D-пространства внутри вашего приложения, где вы можете размещать 3D-контент, создавать 3D-макеты и добавлять глубину к 2D-контенту. Подпространство визуализируется только тогда, когда включена пространственная обработка. В «Домашнем пространстве» или на устройствах, отличных от XR, любой код в этом подпространстве игнорируется.

Существует два способа создания подпространства:

  • setSubspaceContent : эта функция создает подпространство уровня приложения. Это можно вызвать в MainActivity так же, как вы используете setContent . Подпространство уровня приложения не ограничено по высоте, ширине и глубине, что по сути обеспечивает бесконечный холст для пространственного контента.
  • Subspace : этот составной элемент можно разместить в любом месте иерархии пользовательского интерфейса вашего приложения, что позволяет вам поддерживать макеты для 2D- и пространственного пользовательского интерфейса без потери контекста между файлами. Это упрощает совместное использование таких вещей, как существующая архитектура приложения, между XR и другими форм-факторами без необходимости поднимать состояние по всему дереву пользовательского интерфейса или перепроектировать ваше приложение.

Для получения дополнительной информации прочитайте о добавлении подпространства в ваше приложение .

О пространственных компонентах

Составные части подпространства : эти компоненты можно визуализировать только в подпространстве. Прежде чем помещать их в 2D-макет, они должны быть заключены в Subspace или setSubspaceContent . SubspaceModifier позволяет добавлять такие атрибуты, как глубина, смещение и позиционирование, к составным объектам подпространства.

  • Примечание о модификаторах подпространства . Обратите особое внимание на порядок API SubspaceModifier .
    • Смещение должно идти первым в цепочке модификаторов.
    • Подвижные и изменяемые размеры должны идти последними.
    • Поворот должен применяться перед масштабированием

Другие пространственные компоненты не требуют вызова внутри подпространства. Они состоят из обычных 2D-элементов, заключенных в пространственный контейнер. Эти элементы можно использовать в 2D- или 3D-макетах, если они определены для обоих. Если пространственная обработка не включена, их пространственные объекты будут игнорироваться и будут возвращаться к своим 2D-аналогам.

Создайте пространственную панель

SpatialPanel — это компонуемое подпространство, которое позволяет отображать содержимое приложения — например, вы можете отображать воспроизведение видео, неподвижные изображения или любой другой контент на пространственной панели.

Пример пространственной панели пользовательского интерфейса

Вы можете использовать SubspaceModifier для изменения размера, поведения и положения пространственной панели, как показано в следующем примере.

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
        )
    }
}

Ключевые моменты о коде

  • Примечание о модификаторах подпространства . Обратите особое внимание на порядок API SubspaceModifier .
    • Смещение должно идти первым в цепочке модификаторов.
    • Модификаторы Movable и изменяемого размера должны располагаться последними.
    • Поворот необходимо применять перед масштабированием.
  • Поскольку API-интерфейсы SpatialPanel являются компонуемыми подпространствами, вы должны вызывать их внутри Subspace или setSubspaceContent . Вызов их за пределами подпространства вызовет исключение.
  • Разрешите пользователю изменять размер или перемещать панель, добавляя .movable или .resizable SubspaceModifier .
  • Подробную информацию о размерах и расположении см. в нашем руководстве по проектированию пространственных панелей . Дополнительную информацию о реализации кода см. в нашей справочной документации .

Создать орбитальный аппарат

Орбитальный аппарат — это пространственный компонент пользовательского интерфейса. Он предназначен для прикрепления к соответствующей пространственной панели и содержит элементы навигации и контекстных действий, связанные с этой пространственной панелью. Например, если вы создали пространственную панель для отображения видеоконтента, вы можете добавить элементы управления воспроизведением видео внутри орбитального аппарата.

Пример орбитального корабля

Как показано в следующем примере, вызовите орбитальный аппарат внутри SpatialPanel чтобы обернуть пользовательские элементы управления, такие как навигация. При этом они извлекаются из вашего 2D-макета и прикрепляются к пространственной панели в соответствии с вашей конфигурацией.

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
                )
            }
        }
    }
}

Ключевые моменты о коде

  • Примечание о модификаторах подпространства . Обратите особое внимание на порядок API-интерфейсов SubspaceModifier .
    • Смещение должно идти первым в цепочке модификаторов.
    • Подвижные и изменяемые размеры должны идти последними.
    • Поворот должен применяться перед масштабированием
  • Поскольку орбитальные аппараты являются пространственными компонентами пользовательского интерфейса, код можно повторно использовать в 2D- или 3D-макетах. В 2D-макете ваше приложение отображает только содержимое внутри орбитального аппарата и игнорирует сам орбитальный аппарат.
  • Ознакомьтесь с нашим руководством по проектированию , чтобы получить дополнительную информацию о том, как использовать и проектировать орбитальные аппараты.

Добавление нескольких пространственных панелей в пространственный макет

Вы можете создать несколько пространственных панелей и разместить их в SpatialLayout используя SpatialRow , SpatialColumn , SpatialBox и SpatialLayoutSpacer .

Пример нескольких пространственных панелей в пространственной компоновке

В следующем примере кода показано, как это сделать.

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
        )
    }
}

Ключевые моменты о коде

  • SpatialRow , SpatialColumn , SpatialBox и SpatialLayoutSpacer являются компонуемыми подпространствами и должны быть размещены внутри подпространства.
  • Используйте SubspaceModifier для настройки макета.
  • Для макетов с несколькими панелями подряд мы рекомендуем установить радиус кривой 825dp с помощью SubspaceModifier , чтобы панели окружали пользователя. Подробности смотрите в нашем руководстве по проектированию .

Используйте объем для размещения 3D-объекта в макете.

Чтобы разместить 3D-объект в макете, вам нужно будет использовать составное подпространство, называемое объемом. Вот пример того, как это сделать.

Пример 3D-объекта в макете

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
                }
            }
        }
    }
}

Ключевые моменты о коде

  • Примечание о модификаторах подпространства . Обратите особое внимание на порядок API-интерфейсов SubspaceModifier .
    • Смещение должно идти первым в цепочке модификаторов.
    • Подвижные и изменяемые размеры должны идти последними.
    • Поворот должен применяться перед масштабированием
  • См. раздел «Добавление 3D-контента» , чтобы лучше понять, как загружать 3D-контент в объем.

Добавьте другие компоненты пространственного пользовательского интерфейса.

Компоненты пространственного пользовательского интерфейса можно размещать в любом месте иерархии пользовательского интерфейса вашего приложения. Эти элементы можно повторно использовать в вашем 2D-интерфейсе, а их пространственные атрибуты будут видны только при включении пространственных возможностей. Это позволяет вам добавлять высоту к меню, диалоговым окнам и другим компонентам без необходимости писать код дважды. См. следующие примеры пространственного пользовательского интерфейса, чтобы лучше понять, как использовать эти элементы.

Компонент пользовательского интерфейса

Когда пространственное определение включено

В 2D-среде

SpatialDialog

Панель слегка отодвинется назад по оси Z, чтобы отобразить диалоговое окно с повышенными правами.

Возвращается к 2D- Dialog .

SpatialPopUp

Панель слегка отодвинется назад по оси Z, чтобы отобразить приподнятое всплывающее окно.

Возвращается к 2D PopUp .

SpatialElevation

SpatialElevationLevel можно установить для добавления высоты.

Показывает без пространственной возвышенности.

Пространственный Диалог

Это пример диалогового окна, которое открывается после небольшой задержки. При использовании SpatialDialog диалоговое окно отображается на той же глубине по оси Z, что и пространственная панель, а панель смещается на 125dp, когда пространственная обработка включена. SpatialDialog по-прежнему можно использовать, когда пространственная обработка также не включена, и он возвращается к своему 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")
               }
           }
       }
   }
}

Ключевые моменты о коде

Создавайте собственные панели и макеты

Чтобы создавать пользовательские панели, которые не поддерживаются Compose для XR, вы можете работать напрямую с PanelEntities и графом сцены, используя API-интерфейсы SceneCore .

См. также