Android XR SDK теперь доступен в предварительной версии для разработчиков. Нам нужны ваши отзывы! Посетите нашу
страницу поддержки , чтобы связаться с нами.
Разработка пользовательского интерфейса с помощью Jetpack Compose для XR
Оптимизируйте свои подборки
Сохраняйте и классифицируйте контент в соответствии со своими настройками.
С помощью Jetpack Compose для XR вы можете декларативно создавать пространственный пользовательский интерфейс и макет, используя знакомые концепции Compose, такие как строки и столбцы. Это позволяет вам расширить существующий пользовательский интерфейс Android в 3D-пространстве или создавать совершенно новые иммерсивные 3D-приложения.
Если вы занимаетесь пространственным определением существующего приложения на базе Android Views, у вас есть несколько вариантов разработки. Вы можете использовать API-интерфейсы совместимости, использовать Compose и Views вместе или работать напрямую с библиотекой SceneCore. Более подробную информацию можно найти в нашем руководстве по работе с представлениями .

Кодлаб
Изучите основы Android XR: Часть 1. Режимы и пространственные панели
arrow_forward О подпространствах и пространственных компонентах
Когда вы пишете приложение для Android XR, важно понимать концепции подпространства и пространственных компонентов .
О подпространстве
При разработке для Android XR вам потребуется добавить подпространство в свое приложение или макет. Подпространство — это раздел 3D-пространства внутри вашего приложения, где вы можете размещать 3D-контент, создавать 3D-макеты и добавлять глубину к 2D-контенту. Подпространство визуализируется только тогда, когда включена пространственная обработка. В домашнем пространстве или на устройствах, отличных от XR, любой код в этом подпространстве игнорируется.
Существует два способа создания подпространства:
-
setSubspaceContent()
: эта функция создает подпространство уровня приложения. Это можно вызвать в вашей основной деятельности так же, как вы используете setContent()
. Подпространство уровня приложения не ограничено по высоте, ширине и глубине, по сути предоставляя бесконечный холст для пространственного контента. -
Subspace
: этот составной элемент можно разместить в любом месте иерархии пользовательского интерфейса вашего приложения, что позволяет вам поддерживать макеты для 2D- и пространственного пользовательского интерфейса без потери контекста между файлами. Это упрощает совместное использование таких вещей, как существующая архитектура приложения, между XR и другими форм-факторами без необходимости поднимать состояние по всему дереву пользовательского интерфейса или перепроектировать свое приложение.
Дополнительную информацию см. в разделе Добавление подпространства в ваше приложение .
О пространственных компонентах
Составные части подпространства : эти компоненты можно визуализировать только в подпространстве. Прежде чем помещать их в 2D-макет, они должны быть заключены в Subspace
или setSubspaceContent
. SubspaceModifier
позволяет добавлять такие атрибуты, как глубина, смещение и позиционирование, к составным объектам подпространства.
Другие пространственные компоненты не требуют вызова внутри подпространства. Они состоят из обычных 2D-элементов, заключенных в пространственный контейнер. Эти элементы можно использовать в 2D- или 3D-макетах, если они определены для обоих. Если пространственная обработка не включена, их пространственные объекты будут игнорироваться и будут возвращаться к своим 2D-аналогам.
Создайте пространственную панель
SpatialPanel
— это компонуемое подпространство, которое позволяет отображать содержимое приложения — например, вы можете отображать воспроизведение видео, неподвижные изображения или любой другой контент на пространственной панели.

Вы можете использовать SubspaceModifier
для изменения размера, поведения и положения пространственной панели, как показано в следующем примере.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
}
}
@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
)
}
}
Ключевые моменты о коде
Как работает модификатор подвижного подпространства
Когда пользователь перемещает панель от себя, модификатор movable subspace по умолчанию масштабирует панель аналогично тому, как система изменяет размер панелей в домашнем пространстве . Все дочерние элементы наследуют это поведение. Чтобы отключить это, установите для параметра scaleWithDistance
значение false
.
Создать орбитальный аппарат
Орбитальный аппарат — это пространственный компонент пользовательского интерфейса. Он предназначен для прикрепления к соответствующей пространственной панели, макету или другому объекту. Орбитальный аппарат обычно содержит элементы навигации и контекстных действий, связанные с объектом, к которому он привязан. Например, если вы создали пространственную панель для отображения видеоконтента, вы можете добавить элементы управления воспроизведением видео внутри орбитального аппарата.

Как показано в следующем примере, вызовите орбитальный аппарат внутри 2D-макета в SpatialPanel
, чтобы обернуть пользовательские элементы управления, такие как навигация. При этом они извлекаются из вашего 2D-макета и прикрепляются к пространственной панели в соответствии с вашей конфигурацией.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
OrbiterExample()
}
}
@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
)
}
}
}
}
Ключевые моменты о коде
- Поскольку орбитальные аппараты являются пространственными компонентами пользовательского интерфейса, код можно повторно использовать в 2D- или 3D-макетах. В 2D-макете ваше приложение отображает только содержимое внутри орбитального аппарата и игнорирует сам орбитальный аппарат.
- Ознакомьтесь с нашим руководством по проектированию, чтобы получить дополнительную информацию о том, как использовать и проектировать орбитальные аппараты.
Добавление нескольких пространственных панелей в пространственный макет
Вы можете создать несколько пространственных панелей и разместить их в пространственном макете с помощью 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
)
}
}
Ключевые моменты о коде
Используйте объем для размещения 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) {
Дополнительная информация
Добавьте поверхность для изображения или видеоконтента
SpatialExternalSurface
— это компонуемое подпространство, которое создает Surface
и управляет ею, в которую ваше приложение может отображать контент, например изображение или видео . SpatialExternalSurface
поддерживает стереоскопическое или моноскопическое содержимое.
В этом примере показано, как загрузить параллельное стереоскопическое видео с помощью Media3 Exoplayer и SpatialExternalSurface
:
@Composable
fun SpatialExternalSurfaceContent() {
val context = LocalContext.current
Subspace {
SpatialExternalSurface(
modifier = SubspaceModifier
.width(1200.dp) // Default width is 400.dp if no width modifier is specified
.height(676.dp), // Default height is 400.dp if no height modifier is specified
// Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending
// upon which type of content you are rendering: monoscopic content, side-by-side stereo
// content, or top-bottom stereo content
stereoMode = StereoMode.SideBySide,
) {
val exoPlayer = remember { ExoPlayer.Builder(context).build() }
val videoUri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
// Represents a side-by-side stereo video, where each frame contains a pair of
// video frames arranged side-by-side. The frame on the left represents the left
// eye view, and the frame on the right represents the right eye view.
.path("sbs_video.mp4")
.build()
val mediaItem = MediaItem.fromUri(videoUri)
// onSurfaceCreated is invoked only one time, when the Surface is created
onSurfaceCreated { surface ->
exoPlayer.setVideoSurface(surface)
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()
}
// onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its
// associated Surface are destroyed
onSurfaceDestroyed { exoPlayer.release() }
}
}
}
Ключевые моменты о коде
- Установите для
StereoMode
значение Mono
, SideBySide
или TopBottom
в зависимости от типа контента, который вы визуализируете:-
Mono
: изображение или видеокадр состоит из одного идентичного изображения, отображаемого обоим глазам. -
SideBySide
: изображение или видеокадр содержит пару изображений или видеокадров, расположенных рядом, где изображение или кадр слева представляет вид для левого глаза, а изображение или кадр справа представляет вид для правого глаза. -
TopBottom
: изображение или видеокадр содержит пару изображений или видеокадров, расположенных вертикально, где изображение или кадр вверху представляет вид для левого глаза, а изображение или кадр внизу представляет вид для правого глаза.
-
SpatialExternalSurface
поддерживает только прямоугольные поверхности. - Эта
Surface
не фиксирует события ввода. - Невозможно синхронизировать изменения
StereoMode
с рендерингом приложения или декодированием видео. - Этот составной элемент не может отображаться перед другими панелями, поэтому не следует использовать подвижные модификаторы, если в макете есть другие панели.
Добавьте другие компоненты пространственного пользовательского интерфейса.
Компоненты пространственного пользовательского интерфейса можно размещать в любом месте иерархии пользовательского интерфейса вашего приложения. Эти элементы можно повторно использовать в вашем 2D-интерфейсе, а их пространственные атрибуты будут видны только при включении пространственных возможностей. Это позволяет вам добавлять высоту к меню, диалоговым окнам и другим компонентам без необходимости писать код дважды. См. следующие примеры пространственного пользовательского интерфейса, чтобы лучше понять, как использовать эти элементы.
Компонент пользовательского интерфейса | Когда пространственное определение включено | В 2D-среде |
---|
SpatialDialog | Панель слегка отодвинется назад по оси Z, чтобы отобразить диалоговое окно с повышенными правами. | Возвращается к 2D- Dialog . |
SpatialPopup | Панель слегка отодвинется назад по оси Z, чтобы отобразить приподнятое всплывающее окно. | Возвращается к 2D Popup . |
SpatialElevation | SpatialElevationLevel можно установить для добавления высоты. | Показывает без пространственной возвышенности. |
Пространственный Диалог
Это пример диалогового окна, которое открывается после небольшой задержки. При использовании SpatialDialog
диалоговое окно отображается на той же глубине по оси Z, что и пространственная панель, а панель отодвигается на 125dp, когда пространственная обработка включена. SpatialDialog
также можно использовать, когда пространственная обработка не включена, и в этом случае SpatialDialog
возвращается к своему 2D-аналогу Dialog
.
@Composable
fun DelayedDialog() {
var showDialog by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
delay(3000)
showDialog = true
}
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
.
Привязывайте орбитальные аппараты к пространственным макетам и другим объектам.
Вы можете привязать орбитальный аппарат к любому объекту, объявленному в Compose. Это включает в себя объявление орбитального аппарата в пространственной компоновке элементов пользовательского интерфейса, таких как SpatialRow
, SpatialColumn
или SpatialBox
. Орбитальный аппарат привязывается к родительскому объекту, ближайшему к тому месту, где вы его объявили.
Поведение орбитального аппарата определяется тем, где вы его объявляете:
- В 2D-макете, завернутом в
SpatialPanel
(как показано в предыдущем фрагменте кода ), орбитальный аппарат привязывается к этому SpatialPanel
. - В
Subspace
орбитальный аппарат привязывается к ближайшему родительскому объекту, который представляет собой пространственный макет, в котором объявлен орбитальный аппарат.
В следующем примере показано, как привязать орбитальный аппарат к пространственной строке:
Subspace {
SpatialRow {
Orbiter(
position = OrbiterEdge.Top,
offset = EdgeOffset.inner(8.dp),
shape = SpatialRoundedCornerShape(size = CornerSize(50))
) {
Text(
"Hello World!",
style = MaterialTheme.typography.h2,
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)
)
}
}
}
Ключевые моменты о коде
- Когда вы объявляете орбитальный аппарат вне 2D-макета, он привязывается к ближайшему родительскому объекту. В этом случае орбитальный аппарат привязывается к верхней части
SpatialRow
, в котором он объявлен. - Пространственные макеты, такие как
SpatialRow
, SpatialColumn
, SpatialBox
, имеют связанные с ними бессодержательные объекты. Таким образом, орбитальный аппарат, объявленный в пространственном макете, привязывается к этому макету.
См. также
, С помощью Jetpack Compose для XR вы можете декларативно создавать пространственный пользовательский интерфейс и макет, используя знакомые концепции Compose, такие как строки и столбцы. Это позволяет вам расширить существующий пользовательский интерфейс Android в 3D-пространстве или создавать совершенно новые иммерсивные 3D-приложения.
Если вы занимаетесь пространственным определением существующего приложения на базе Android Views, у вас есть несколько вариантов разработки. Вы можете использовать API-интерфейсы совместимости, использовать Compose и Views вместе или работать напрямую с библиотекой SceneCore. Более подробную информацию можно найти в нашем руководстве по работе с представлениями .

Кодлаб
Изучите основы Android XR: Часть 1. Режимы и пространственные панели
arrow_forward О подпространствах и пространственных компонентах
Когда вы пишете приложение для Android XR, важно понимать концепции подпространства и пространственных компонентов .
О подпространстве
При разработке для Android XR вам потребуется добавить подпространство в свое приложение или макет. Подпространство — это раздел 3D-пространства внутри вашего приложения, где вы можете размещать 3D-контент, создавать 3D-макеты и добавлять глубину к 2D-контенту. Подпространство визуализируется только тогда, когда включена пространственная обработка. В домашнем пространстве или на устройствах, отличных от XR, любой код в этом подпространстве игнорируется.
Существует два способа создания подпространства:
-
setSubspaceContent()
: эта функция создает подпространство уровня приложения. Это можно вызвать в вашей основной деятельности так же, как вы используете setContent()
. Подпространство уровня приложения не ограничено по высоте, ширине и глубине, по сути предоставляя бесконечный холст для пространственного контента. -
Subspace
: этот составной элемент можно разместить в любом месте иерархии пользовательского интерфейса вашего приложения, что позволяет вам поддерживать макеты для 2D- и пространственного пользовательского интерфейса без потери контекста между файлами. Это упрощает совместное использование таких вещей, как существующая архитектура приложения, между XR и другими форм-факторами без необходимости поднимать состояние по всему дереву пользовательского интерфейса или перепроектировать ваше приложение.
Дополнительную информацию см. в разделе Добавление подпространства в ваше приложение .
О пространственных компонентах
Составные части подпространства : эти компоненты можно визуализировать только в подпространстве. Прежде чем помещать их в 2D-макет, они должны быть заключены в Subspace
или setSubspaceContent
. SubspaceModifier
позволяет добавлять такие атрибуты, как глубина, смещение и позиционирование, к составным объектам подпространства.
Другие пространственные компоненты не требуют вызова внутри подпространства. Они состоят из обычных 2D-элементов, заключенных в пространственный контейнер. Эти элементы можно использовать в 2D- или 3D-макетах, если они определены для обоих. Если пространственная обработка не включена, их пространственные объекты будут игнорироваться и будут возвращаться к своим 2D-аналогам.
Создайте пространственную панель
SpatialPanel
— это компонуемое подпространство, которое позволяет отображать содержимое приложения — например, вы можете отображать воспроизведение видео, неподвижные изображения или любой другой контент на пространственной панели.

Вы можете использовать SubspaceModifier
для изменения размера, поведения и положения пространственной панели, как показано в следующем примере.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
}
}
@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
)
}
}
Ключевые моменты о коде
Как работает модификатор подвижного подпространства
Когда пользователь перемещает панель от себя, модификатор movable subspace по умолчанию масштабирует панель аналогично тому, как система изменяет размер панелей в домашнем пространстве . Все дочерние элементы наследуют это поведение. Чтобы отключить это, установите для параметра scaleWithDistance
значение false
.
Создать орбитальный аппарат
Орбитальный аппарат — это пространственный компонент пользовательского интерфейса. Он предназначен для прикрепления к соответствующей пространственной панели, макету или другому объекту. Орбитальный аппарат обычно содержит элементы навигации и контекстных действий, связанные с объектом, к которому он привязан. Например, если вы создали пространственную панель для отображения видеоконтента, вы можете добавить элементы управления воспроизведением видео внутри орбитального аппарата.

Как показано в следующем примере, вызовите орбитальный аппарат внутри 2D-макета в SpatialPanel
, чтобы обернуть пользовательские элементы управления, такие как навигация. При этом они извлекаются из вашего 2D-макета и прикрепляются к пространственной панели в соответствии с вашей конфигурацией.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
OrbiterExample()
}
}
@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
)
}
}
}
}
Ключевые моменты о коде
- Поскольку орбитальные аппараты являются пространственными компонентами пользовательского интерфейса, код можно повторно использовать в 2D- или 3D-макетах. В 2D-макете ваше приложение отображает только содержимое внутри орбитального аппарата и игнорирует сам орбитальный аппарат.
- Ознакомьтесь с нашим руководством по проектированию, чтобы получить дополнительную информацию о том, как использовать и проектировать орбитальные аппараты.
Добавление нескольких пространственных панелей в пространственный макет
Вы можете создать несколько пространственных панелей и разместить их в пространственном макете с помощью 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
)
}
}
Ключевые моменты о коде
Используйте объем для размещения 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) {
Дополнительная информация
Добавьте поверхность для изображения или видеоконтента
SpatialExternalSurface
— это компонуемое подпространство, которое создает Surface
и управляет ею, в которую ваше приложение может отображать контент, например изображение или видео . SpatialExternalSurface
поддерживает стереоскопическое или моноскопическое содержимое.
В этом примере показано, как загрузить параллельное стереоскопическое видео с помощью Media3 Exoplayer и SpatialExternalSurface
:
@Composable
fun SpatialExternalSurfaceContent() {
val context = LocalContext.current
Subspace {
SpatialExternalSurface(
modifier = SubspaceModifier
.width(1200.dp) // Default width is 400.dp if no width modifier is specified
.height(676.dp), // Default height is 400.dp if no height modifier is specified
// Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending
// upon which type of content you are rendering: monoscopic content, side-by-side stereo
// content, or top-bottom stereo content
stereoMode = StereoMode.SideBySide,
) {
val exoPlayer = remember { ExoPlayer.Builder(context).build() }
val videoUri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
// Represents a side-by-side stereo video, where each frame contains a pair of
// video frames arranged side-by-side. The frame on the left represents the left
// eye view, and the frame on the right represents the right eye view.
.path("sbs_video.mp4")
.build()
val mediaItem = MediaItem.fromUri(videoUri)
// onSurfaceCreated is invoked only one time, when the Surface is created
onSurfaceCreated { surface ->
exoPlayer.setVideoSurface(surface)
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()
}
// onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its
// associated Surface are destroyed
onSurfaceDestroyed { exoPlayer.release() }
}
}
}
Ключевые моменты о коде
- Установите для
StereoMode
значение Mono
, SideBySide
или TopBottom
в зависимости от типа контента, который вы визуализируете:-
Mono
: изображение или видеокадр состоит из одного идентичного изображения, отображаемого обоим глазам. -
SideBySide
: изображение или видеокадр содержит пару изображений или видеокадров, расположенных рядом, где изображение или кадр слева представляет вид для левого глаза, а изображение или кадр справа представляет вид для правого глаза. -
TopBottom
: изображение или видеокадр содержит пару изображений или видеокадров, расположенных вертикально, где изображение или кадр вверху представляет вид для левого глаза, а изображение или кадр внизу представляет вид для правого глаза.
-
SpatialExternalSurface
поддерживает только прямоугольные поверхности. - Эта
Surface
не фиксирует события ввода. - Невозможно синхронизировать изменения
StereoMode
с рендерингом приложения или декодированием видео. - Этот составной элемент не может отображаться перед другими панелями, поэтому не следует использовать подвижные модификаторы, если в макете есть другие панели.
Добавьте другие компоненты пространственного пользовательского интерфейса.
Компоненты пространственного пользовательского интерфейса можно размещать в любом месте иерархии пользовательского интерфейса вашего приложения. Эти элементы можно повторно использовать в вашем 2D-интерфейсе, а их пространственные атрибуты будут видны только при включении пространственных возможностей. Это позволяет вам добавлять высоту к меню, диалоговым окнам и другим компонентам без необходимости писать код дважды. См. следующие примеры пространственного пользовательского интерфейса, чтобы лучше понять, как использовать эти элементы.
Компонент пользовательского интерфейса | Когда пространственное определение включено | В 2D-среде |
---|
SpatialDialog | Панель слегка отодвинется назад по оси Z, чтобы отобразить диалоговое окно с повышенными правами. | Возвращается к 2D- Dialog . |
SpatialPopup | Панель слегка отодвинется назад по оси Z, чтобы отобразить приподнятое всплывающее окно. | Возвращается к 2D Popup . |
SpatialElevation | SpatialElevationLevel можно установить для добавления высоты. | Показывает без пространственной возвышенности. |
Пространственный Диалог
Это пример диалогового окна, которое открывается после небольшой задержки. При использовании SpatialDialog
диалоговое окно отображается на той же глубине по оси Z, что и пространственная панель, а панель отодвигается на 125dp, когда пространственная обработка включена. SpatialDialog
также можно использовать, когда пространственная обработка не включена, и в этом случае SpatialDialog
возвращается к своему 2D-аналогу Dialog
.
@Composable
fun DelayedDialog() {
var showDialog by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
delay(3000)
showDialog = true
}
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
.
Привязывайте орбитальные аппараты к пространственным макетам и другим объектам.
Вы можете привязать орбитальный аппарат к любому объекту, объявленному в Compose. Это включает в себя объявление орбитального аппарата в пространственной компоновке элементов пользовательского интерфейса, таких как SpatialRow
, SpatialColumn
или SpatialBox
. Орбитальный аппарат привязывается к родительскому объекту, ближайшему к тому месту, где вы его объявили.
Поведение орбитального аппарата определяется тем, где вы его объявляете:
- В 2D-макете, завернутом в
SpatialPanel
(как показано в предыдущем фрагменте кода ), орбитальный аппарат привязывается к этому SpatialPanel
. - В
Subspace
орбитальный аппарат привязывается к ближайшему родительскому объекту, который представляет собой пространственный макет, в котором объявлен орбитальный аппарат.
В следующем примере показано, как привязать орбитальный аппарат к пространственной строке:
Subspace {
SpatialRow {
Orbiter(
position = OrbiterEdge.Top,
offset = EdgeOffset.inner(8.dp),
shape = SpatialRoundedCornerShape(size = CornerSize(50))
) {
Text(
"Hello World!",
style = MaterialTheme.typography.h2,
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)
)
}
}
}
Ключевые моменты о коде
- Когда вы объявляете орбитальный аппарат вне 2D-макета, он привязывается к ближайшему родительскому объекту. В этом случае орбитальный аппарат привязывается к верхней части
SpatialRow
, в котором он объявлен. - Пространственные макеты, такие как
SpatialRow
, SpatialColumn
, SpatialBox
, имеют связанные с ними бессодержательные объекты. Таким образом, орбитальный аппарат, объявленный в пространственном макете, привязывается к этому макету.
См. также
, С помощью Jetpack Compose для XR вы можете декларативно создавать пространственный пользовательский интерфейс и макет, используя знакомые концепции Compose, такие как строки и столбцы. Это позволяет вам расширить существующий пользовательский интерфейс Android в 3D-пространстве или создавать совершенно новые иммерсивные 3D-приложения.
Если вы занимаетесь пространственным определением существующего приложения на базе Android Views, у вас есть несколько вариантов разработки. Вы можете использовать API-интерфейсы совместимости, использовать Compose и Views вместе или работать напрямую с библиотекой SceneCore. Более подробную информацию можно найти в нашем руководстве по работе с представлениями .

Кодлаб
Изучите основы Android XR: Часть 1. Режимы и пространственные панели
arrow_forward О подпространствах и пространственных компонентах
Когда вы пишете приложение для Android XR, важно понимать концепции подпространства и пространственных компонентов .
О подпространстве
При разработке для Android XR вам потребуется добавить подпространство в ваше приложение или макет. Подпространство — это раздел 3D-пространства внутри вашего приложения, где вы можете размещать 3D-контент, создавать 3D-макеты и добавлять глубину к 2D-контенту. Подпространство визуализируется только тогда, когда включена пространственная обработка. В домашнем пространстве или на устройствах, отличных от XR, любой код в этом подпространстве игнорируется.
Существует два способа создания подпространства:
-
setSubspaceContent()
: эта функция создает подпространство уровня приложения. Это можно вызвать в вашей основной деятельности так же, как вы используете setContent()
. Подпространство уровня приложения не ограничено по высоте, ширине и глубине, по сути предоставляя бесконечный холст для пространственного контента. -
Subspace
: этот составной элемент можно разместить в любом месте иерархии пользовательского интерфейса вашего приложения, что позволяет вам поддерживать макеты для 2D- и пространственного пользовательского интерфейса без потери контекста между файлами. Это упрощает совместное использование таких вещей, как существующая архитектура приложения, между XR и другими форм-факторами без необходимости поднимать состояние по всему дереву пользовательского интерфейса или перепроектировать ваше приложение.
Дополнительную информацию см. в разделе Добавление подпространства в ваше приложение .
О пространственных компонентах
Составные части подпространства : эти компоненты можно визуализировать только в подпространстве. Прежде чем помещать их в 2D-макет, они должны быть заключены в Subspace
или setSubspaceContent
. SubspaceModifier
позволяет добавлять такие атрибуты, как глубина, смещение и позиционирование, к составным объектам подпространства.
Другие пространственные компоненты не требуют вызова внутри подпространства. Они состоят из обычных 2D-элементов, заключенных в пространственный контейнер. Эти элементы можно использовать в 2D- или 3D-макетах, если они определены для обоих. Если пространственная обработка не включена, их пространственные объекты будут игнорироваться и будут возвращаться к своим 2D-аналогам.
Создайте пространственную панель
SpatialPanel
— это компонуемое подпространство, которое позволяет отображать содержимое приложения — например, вы можете отображать воспроизведение видео, неподвижные изображения или любой другой контент на пространственной панели.

Вы можете использовать SubspaceModifier
для изменения размера, поведения и положения пространственной панели, как показано в следующем примере.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
}
}
@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
)
}
}
Ключевые моменты о коде
Как работает модификатор подвижного подпространства
Когда пользователь перемещает панель от себя, модификатор movable subspace по умолчанию масштабирует панель аналогично тому, как система изменяет размер панелей в домашнем пространстве . Все дочерние элементы наследуют это поведение. Чтобы отключить это, установите для параметра scaleWithDistance
значение false
.
Создать орбитальный аппарат
Орбитальный аппарат — это пространственный компонент пользовательского интерфейса. Он предназначен для прикрепления к соответствующей пространственной панели, макету или другому объекту. Орбитальный аппарат обычно содержит элементы навигации и контекстных действий, связанные с объектом, к которому он привязан. Например, если вы создали пространственную панель для отображения видеоконтента, вы можете добавить элементы управления воспроизведением видео внутри орбитального аппарата.

Как показано в следующем примере, вызовите орбитальный аппарат внутри 2D-макета в SpatialPanel
, чтобы обернуть пользовательские элементы управления, такие как навигация. При этом они извлекаются из вашего 2D-макета и прикрепляются к пространственной панели в соответствии с вашей конфигурацией.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
OrbiterExample()
}
}
@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
)
}
}
}
}
Ключевые моменты о коде
- Поскольку орбитальные аппараты являются пространственными компонентами пользовательского интерфейса, код можно повторно использовать в 2D- или 3D-макетах. В 2D-макете ваше приложение отображает только содержимое внутри орбитального аппарата и игнорирует сам орбитальный аппарат.
- Ознакомьтесь с нашим руководством по проектированию, чтобы получить дополнительную информацию о том, как использовать и проектировать орбитальные аппараты.
Добавление нескольких пространственных панелей в пространственный макет
Вы можете создать несколько пространственных панелей и разместить их в пространственном макете с помощью 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
)
}
}
Ключевые моменты о коде
Используйте объем для размещения 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) {
Дополнительная информация
Добавьте поверхность для изображения или видеоконтента
SpatialExternalSurface
— это компонуемое подпространство, которое создает Surface
и управляет ею, в которую ваше приложение может отображать контент, например изображение или видео . SpatialExternalSurface
поддерживает стереоскопическое или моноскопическое содержимое.
В этом примере показано, как загрузить параллельное стереоскопическое видео с помощью Media3 Exoplayer и SpatialExternalSurface
:
@Composable
fun SpatialExternalSurfaceContent() {
val context = LocalContext.current
Subspace {
SpatialExternalSurface(
modifier = SubspaceModifier
.width(1200.dp) // Default width is 400.dp if no width modifier is specified
.height(676.dp), // Default height is 400.dp if no height modifier is specified
// Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending
// upon which type of content you are rendering: monoscopic content, side-by-side stereo
// content, or top-bottom stereo content
stereoMode = StereoMode.SideBySide,
) {
val exoPlayer = remember { ExoPlayer.Builder(context).build() }
val videoUri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
// Represents a side-by-side stereo video, where each frame contains a pair of
// video frames arranged side-by-side. The frame on the left represents the left
// eye view, and the frame on the right represents the right eye view.
.path("sbs_video.mp4")
.build()
val mediaItem = MediaItem.fromUri(videoUri)
// onSurfaceCreated is invoked only one time, when the Surface is created
onSurfaceCreated { surface ->
exoPlayer.setVideoSurface(surface)
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()
}
// onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its
// associated Surface are destroyed
onSurfaceDestroyed { exoPlayer.release() }
}
}
}
Ключевые моменты о коде
- Установите для
StereoMode
значение Mono
, SideBySide
или TopBottom
в зависимости от типа контента, который вы визуализируете:-
Mono
: изображение или видеокадр состоит из одного идентичного изображения, отображаемого обоим глазам. -
SideBySide
: изображение или видеокадр содержит пару изображений или видеокадров, расположенных рядом, где изображение или кадр слева представляет вид для левого глаза, а изображение или кадр справа представляет вид для правого глаза. -
TopBottom
: изображение или видеокадр содержит пару изображений или видеокадров, расположенных вертикально, где изображение или кадр вверху представляет вид для левого глаза, а изображение или кадр внизу представляет вид для правого глаза.
-
SpatialExternalSurface
поддерживает только прямоугольные поверхности. - Эта
Surface
не фиксирует события ввода. - Невозможно синхронизировать изменения
StereoMode
с рендерингом приложения или декодированием видео. - Этот составной элемент не может отображаться перед другими панелями, поэтому не следует использовать подвижные модификаторы, если в макете есть другие панели.
Добавьте другие компоненты пространственного пользовательского интерфейса.
Компоненты пространственного пользовательского интерфейса можно размещать в любом месте иерархии пользовательского интерфейса вашего приложения. Эти элементы можно повторно использовать в вашем 2D-интерфейсе, а их пространственные атрибуты будут видны только при включении пространственных возможностей. Это позволяет вам добавлять высоту к меню, диалоговым окнам и другим компонентам без необходимости писать код дважды. См. следующие примеры пространственного пользовательского интерфейса, чтобы лучше понять, как использовать эти элементы.
Компонент пользовательского интерфейса | Когда пространственное определение включено | В 2D-среде |
---|
SpatialDialog | Панель слегка отодвинется назад по оси Z, чтобы отобразить диалоговое окно с повышенными правами. | Возвращается к 2D- Dialog . |
SpatialPopup | Панель слегка отодвинется назад по оси Z, чтобы отобразить приподнятое всплывающее окно. | Возвращается к 2D Popup . |
SpatialElevation | SpatialElevationLevel можно установить для добавления высоты. | Показывает без пространственной возвышенности. |
Пространственный Диалог
Это пример диалогового окна, которое открывается после небольшой задержки. При использовании SpatialDialog
диалоговое окно отображается на той же глубине по оси Z, что и пространственная панель, а панель отодвигается на 125dp, когда пространственная обработка включена. SpatialDialog
также можно использовать, когда пространственная обработка не включена, и в этом случае SpatialDialog
возвращается к своему 2D-аналогу Dialog
.
@Composable
fun DelayedDialog() {
var showDialog by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
delay(3000)
showDialog = true
}
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
.
Привязывайте орбитальные аппараты к пространственным макетам и другим объектам.
Вы можете привязать орбитальный аппарат к любому объекту, объявленному в Compose. Это включает в себя объявление орбитального аппарата в пространственной компоновке элементов пользовательского интерфейса, таких как SpatialRow
, SpatialColumn
или SpatialBox
. Орбитальный аппарат привязывается к родительскому объекту, ближайшему к тому месту, где вы его объявили.
Поведение орбитального аппарата определяется тем, где вы его объявляете:
- В 2D-макете, завернутом в
SpatialPanel
(как показано в предыдущем фрагменте кода ), орбитальный аппарат привязывается к этому SpatialPanel
. - В
Subspace
орбитальный аппарат привязывается к ближайшему родительскому объекту, который представляет собой пространственный макет, в котором объявлен орбитальный аппарат.
В следующем примере показано, как привязать орбитальный аппарат к пространственной строке:
Subspace {
SpatialRow {
Orbiter(
position = OrbiterEdge.Top,
offset = EdgeOffset.inner(8.dp),
shape = SpatialRoundedCornerShape(size = CornerSize(50))
) {
Text(
"Hello World!",
style = MaterialTheme.typography.h2,
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)
)
}
}
}
Ключевые моменты о коде
- Когда вы объявляете орбитальный аппарат вне 2D-макета, он привязывается к ближайшему родительскому объекту. В этом случае орбитальный аппарат привязывается к верхней части
SpatialRow
, в котором он объявлен. - Пространственные макеты, такие как
SpatialRow
, SpatialColumn
, SpatialBox
, имеют связанные с ними бессодержательные объекты. Таким образом, орбитальный аппарат, объявленный в пространственном макете, привязывается к этому макету.
См. также
, С помощью Jetpack Compose для XR вы можете декларативно создавать пространственный пользовательский интерфейс и макет, используя знакомые концепции Compose, такие как строки и столбцы. Это позволяет вам расширить существующий пользовательский интерфейс Android в 3D-пространстве или создавать совершенно новые иммерсивные 3D-приложения.
Если вы занимаетесь пространственным определением существующего приложения на базе Android Views, у вас есть несколько вариантов разработки. Вы можете использовать API-интерфейсы совместимости, использовать Compose и Views вместе или работать напрямую с библиотекой SceneCore. Более подробную информацию можно найти в нашем руководстве по работе с представлениями .

Кодлаб
Изучите основы Android XR: Часть 1. Режимы и пространственные панели
arrow_forward О подпространствах и пространственных компонентах
Когда вы пишете приложение для Android XR, важно понимать концепции подпространства и пространственных компонентов .
О подпространстве
При разработке для Android XR вам потребуется добавить подпространство в свое приложение или макет. Подпространство — это раздел 3D-пространства внутри вашего приложения, где вы можете размещать 3D-контент, создавать 3D-макеты и добавлять глубину к 2D-контенту. Подпространство визуализируется только тогда, когда включена пространственная обработка. В домашнем пространстве или на устройствах, отличных от XR, любой код в этом подпространстве игнорируется.
Существует два способа создания подпространства:
-
setSubspaceContent()
: эта функция создает подпространство уровня приложения. Это можно вызвать в вашей основной деятельности так же, как вы используете setContent()
. Подпространство уровня приложения не ограничено по высоте, ширине и глубине, по сути предоставляя бесконечный холст для пространственного контента. -
Subspace
: этот составной элемент можно разместить в любом месте иерархии пользовательского интерфейса вашего приложения, что позволяет вам поддерживать макеты для 2D- и пространственного пользовательского интерфейса без потери контекста между файлами. Это упрощает совместное использование таких вещей, как существующая архитектура приложения, между XR и другими форм-факторами без необходимости поднимать состояние по всему дереву пользовательского интерфейса или перепроектировать ваше приложение.
Дополнительную информацию см. в разделе Добавление подпространства в ваше приложение .
О пространственных компонентах
Подпространственные композиции : эти компоненты могут быть отображены только в подпространстве. Они должны быть заключены в Subspace
или setSubspaceContent
прежде чем помещать в 2D -макет. SubspaceModifier
позволяет добавлять атрибуты, такие как глубина, смещение и позиционирование в ваши подпространственные композиты.
Другие пространственные компоненты не требуют, чтобы их называли подпространством. Они состоят из обычных 2D -элементов, завернутых в пространственный контейнер. Эти элементы могут использоваться в пределах 2D или 3D -макетов, если они определены для обоих. Когда пространственно не включена, их пространственные функции будут игнорироваться, и они вернутся к своим 2D -аналогам.
Создать пространственную панель
SpatialPanel
- это подпространство, которое позволяет отображать содержимое приложения - например, вы можете отображать воспроизведение видео, неподвижные изображения или любой другой контент на пространственной панели.

Вы можете использовать SubspaceModifier
для изменения размера, поведения и позиционирования пространственной панели, как показано в следующем примере.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
}
}
@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
)
}
}
Ключевые моменты о коде
Как работает подвижный модификатор подпространства
По мере того, как пользователь отодвигает панель от них, по умолчанию подвижный модификатор подпространства масштабирует панель аналогично тому, как панели изменяются по системе в домашнем пространстве . Все дети содержат наследуют это поведение. Чтобы отключить это, установите параметр scaleWithDistance
на false
.
Создать орбитаж
Орбитатор - это пространственный компонент пользовательского интерфейса. Он предназначен для прикрепления к соответствующей пространственной панели, макете или другой сущности. Орбитатор обычно содержит навигационные и контекстуальные элементы действий, связанные с сущностью, к которой он прикреплен. Например, если вы создали пространственную панель для отображения видеоконтента, вы можете добавить элементы управления воспроизведением видео на орбитальный аппарат.

Как показано в следующем примере, вызовите орбитаж внутри 2D -макета в SpatialPanel
, чтобы обернуть элементы управления пользователями, такие как навигация. Это извлекает их из вашего 2D -макета и прикрепляет их к пространственной панели в соответствии с вашей конфигурацией.
Subspace {
SpatialPanel(
SubspaceModifier
.height(824.dp)
.width(1400.dp)
.movable()
.resizable()
) {
SpatialPanelContent()
OrbiterExample()
}
}
@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
)
}
}
}
}
Ключевые моменты о коде
- Поскольку орбитальные аппараты являются пространственными компонентами пользовательского интерфейса, код может быть повторно использован в макетах 2D или 3D. В 2D -макете ваше приложение отображает только контент внутри орбитального аппарата и игнорирует сам орбитаж.
- Ознакомьтесь с нашим руководством по проектированию для получения дополнительной информации о том, как использовать и разработать орбитальные операции.
Добавить несколько пространственных панелей в пространственную компоновку
Вы можете создать несколько пространственных панелей и поместить их в пространственную компоновку, используя 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
)
}
}
Ключевые моменты о коде
Используйте объем, чтобы поместить 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) {
Дополнительная информация
Добавить поверхность для изображения или видеоконтента
SpatialExternalSurface
- это подпространство, которое создает и управляет Surface
, на которую ваше приложение может рисовать контент, например, изображение или видео . SpatialExternalSurface
поддерживает либо стереоскопическое, либо моноскопическое содержание.
Этот пример демонстрирует, как загрузить стереоскопическое видео с использованием Media3 Exoplayer и SpatialExternalSurface
:
@Composable
fun SpatialExternalSurfaceContent() {
val context = LocalContext.current
Subspace {
SpatialExternalSurface(
modifier = SubspaceModifier
.width(1200.dp) // Default width is 400.dp if no width modifier is specified
.height(676.dp), // Default height is 400.dp if no height modifier is specified
// Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending
// upon which type of content you are rendering: monoscopic content, side-by-side stereo
// content, or top-bottom stereo content
stereoMode = StereoMode.SideBySide,
) {
val exoPlayer = remember { ExoPlayer.Builder(context).build() }
val videoUri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
// Represents a side-by-side stereo video, where each frame contains a pair of
// video frames arranged side-by-side. The frame on the left represents the left
// eye view, and the frame on the right represents the right eye view.
.path("sbs_video.mp4")
.build()
val mediaItem = MediaItem.fromUri(videoUri)
// onSurfaceCreated is invoked only one time, when the Surface is created
onSurfaceCreated { surface ->
exoPlayer.setVideoSurface(surface)
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()
}
// onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its
// associated Surface are destroyed
onSurfaceDestroyed { exoPlayer.release() }
}
}
}
Ключевые моменты о коде
- Установите
StereoMode
на Mono
, SideBySide
или TopBottom
в зависимости от того, какой тип контента вы выполняете:-
Mono
: изображение или видео кадр состоит из одного идентичного изображения, показанного обоим глазам. -
SideBySide
: изображение или видео кадр содержит пару изображений или видео кадров, расположенных бок о бок, где изображение или кадр слева представляют собой вид левого глаза, а изображение или кадр справа представляют вид правого глаза. -
TopBottom
: изображение или видео кадр содержит пару изображений или видео кадров, сложенных вертикально, где изображение или рамка сверху представляют собой вид левого глаза, а изображение или кадр внизу представляют собой вид правого глаза.
-
SpatialExternalSurface
поддерживает только прямоугольные поверхности. - Эта
Surface
не захватывает входных событий. - Невозможно синхронизировать изменения
StereoMode
с рендерингом приложений или декодированием видео. - Этот композиционный не может рендеринг перед другими панелями, поэтому вы не должны использовать подвижные модификаторы, если в макете есть другие панели.
Добавить другие пространственные компоненты пользовательского интерфейса
Пространственные компоненты пользовательского интерфейса могут быть размещены в любом месте иерархии пользовательского интерфейса вашего приложения. Эти элементы могут быть повторно использованы в вашем 2D -интерфейсе, и их пространственные атрибуты будут видны только тогда, когда будут включены пространственные возможности. Это позволяет добавлять высоту в меню, диалоги и другие компоненты без необходимости писать ваш код дважды. См. Следующие примеры пространственного пользовательского интерфейса, чтобы лучше понять, как использовать эти элементы.
Компонент пользовательского интерфейса | Когда пространственная пространство включена | В 2D средах |
---|
SpatialDialog | Панель будет слегка натолкнуться в глубину Z, чтобы отобразить повышенный диалог | Возвращается к 2D Dialog . |
SpatialPopup | Панель будет слегка натолкнуться назад в глубину Z, чтобы отобразить повышенное всплывающее окно | Возвращается к 2D -всплывающему Popup . |
SpatialElevation | SpatialElevationLevel может быть установлен для добавления возвышения. | Показывает без пространственного возвышения. |
Spatialdialog
Это пример диалога, который открывается после короткой задержки. Когда используется SpatialDialog
, диалог появляется на том же Z-глубине, что и пространственная панель, и панель отталкивается на 125DP, когда включена пространственная пространственная способность. SpatialDialog
также может использоваться, когда пространственная пространственная система не включена, и в этом случае SpatialDialog
возвращается к своему 2D Dialog
.
@Composable
fun DelayedDialog() {
var showDialog by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
delay(3000)
showDialog = true
}
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
.
Якоря орбитальных операций на пространственные макеты и другие сущности
Вы можете прикрепить орбитаж на любую организацию, объявленную в Compose. Это включает в себя объявление орбитального отверстия в пространственном расположении элементов пользовательского интерфейса, таких как SpatialRow
, SpatialColumn
или SpatialBox
. Орбитатор прикрепляет к родительскому сущности, ближайшей к тому месту, где вы это объявили.
Поведение орбитаря определяется тем, где вы его объявляете:
- В 2D -макете, завернутой в
SpatialPanel
(как показано в предыдущем фрагменте кода ), орбитальные операции привязаны к этой SpatialPanel
. - В
Subspace
орбитальные отверстия привязаны к ближайшему родительскому сущности, которая является пространственной планировкой, в которой объявляется орбитаж.
В следующем примере показано, как закрепить орбитаж на пространственную строку:
Subspace {
SpatialRow {
Orbiter(
position = OrbiterEdge.Top,
offset = EdgeOffset.inner(8.dp),
shape = SpatialRoundedCornerShape(size = CornerSize(50))
) {
Text(
"Hello World!",
style = MaterialTheme.typography.h2,
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)
)
}
}
}
Ключевые моменты о коде
- Когда вы объявляете орбитаж за пределами 2D -макета, орбитаж привязывает к своему ближайшему родительскому сущности. В этом случае орбитарь прикрепляет к вершине
SpatialRow
в котором он объявлен. - Пространственные макеты, такие как
SpatialRow
, SpatialColumn
, SpatialBox
имеют без контента, связанные с ними. Следовательно, орбитальный оператор объявил в пространственном макете привязки к этой макету.
См. также