Разработка пространственного пользовательского интерфейса с помощью Jetpack Compose для XR
Оптимизируйте свои подборки
Сохраняйте и классифицируйте контент в соответствии со своими настройками.
Применимые устройства XR
Данное руководство поможет вам создавать приложения для устройств XR такого типа.
С помощью Jetpack Compose for XR вы можете декларативно создавать пространственный пользовательский интерфейс и макеты, используя знакомые концепции Compose, такие как строки и столбцы. Это позволяет расширить существующий пользовательский интерфейс Android в трехмерное пространство или создавать совершенно новые иммерсивные 3D-приложения.
Если вы занимаетесь пространственной интеграцией существующего приложения Android на основе Views, у вас есть несколько вариантов разработки. Вы можете использовать API для обеспечения взаимодействия, использовать Compose и Views вместе или работать напрямую с библиотекой SceneCore. Подробнее см. в нашем руководстве по работе с Views .
Кодлаб
Изучение основ Android XR: Часть 1 — Режимы и пространственные панели
arrow_forward
О подпространствах и пространственно-ориентированных компонентах
При разработке приложения для Android XR важно понимать концепции подпространства и пространственных компонентов .
О подпространстве
При разработке для Android XR вам потребуется добавить подпространство в ваше приложение или макет. Подпространство — это раздел трёхмерного пространства внутри вашего приложения, где вы можете размещать трёхмерный контент, создавать трёхмерные макеты и добавлять глубину к контенту, который в противном случае был бы двухмерным. Подпространство отображается только тогда, когда включена пространственная обработка. В Home Space или на устройствах, не поддерживающих XR, любой код внутри этого подпространства игнорируется.
Существует несколько способов создания подпространства:
Subspace : Этот компонент создает новую, независимую иерархию пространственного пользовательского интерфейса. Он не наследует пространственное положение, ориентацию или масштаб какого-либо родительского Subspace в которое он вложен. Subspace автоматически ограничивается рекомендуемым системой блоком контента.
PlanarEmbeddedSubspace : Этот компонент можно разместить в иерархии пользовательского интерфейса вашего приложения, что позволяет сохранять макеты для 2D и пространственного интерфейса. PlanarEmbeddedSubspace учитывает ограничения и позиционирование своего родительского компонента. 3D-контент, размещенный внутри него, позиционируется относительно этой 2D-области.
Компоненты, создаваемые в подпространстве : Эти компоненты могут быть отрисованы только в подпространстве. Перед размещением в 2D-макете они должны быть заключены в Subspace . SubspaceModifier позволяет добавлять к компонентам, создаваемым в подпространстве, такие атрибуты, как глубина, смещение и позиционирование .
Другие пространственные компоненты не требуют вызова внутри подпространства. Они представляют собой обычные 2D-элементы, заключенные в пространственный контейнер. Эти элементы могут использоваться в 2D или 3D макетах, если они определены для обоих типов. Если пространственная обработка не включена, их пространственные характеристики будут игнорироваться, и они будут заменены своими 2D-аналогами.
Создать пространственную панель
SpatialPanel — это компонуемый подпространственный объект, позволяющий отображать контент приложения — например, воспроизведение видео, статичные изображения или любой другой контент в пространственной панели.
С помощью SubspaceModifier можно изменить размер, поведение и положение пространственной панели, как показано в следующем примере.
Поскольку API SpatialPanel являются компонентами подпространства, их необходимо вызывать внутри Subspace . Вызов вне подпространства вызовет исключение.
Размер SpatialPanel задается с помощью параметров height и width заданных в SubspaceModifier . Если эти параметры не указаны, размер панели определяется размерами ее содержимого.
Предоставьте пользователю возможность перемещать панель, добавив модификатор movable подпространства.
Предоставьте пользователю возможность изменять размер панели, добавив модификатор resizable размер подпространства".
По умолчанию, когда пользователь перемещает панель, модификатор movable масштабирует панель аналогично тому, как панели изменяются в системном пространстве . Все дочерние элементы наследуют это поведение. Чтобы отключить это, установите параметр shouldScaleWithDistance в false .
Создать орбитальный аппарат
Компонент «Орбитер» — это пространственный элемент пользовательского интерфейса. Он предназначен для прикрепления к соответствующей пространственной панели или компоненту пространственной компоновки, такому как SpatialColumn , SpatialRow или SpatialBox . Обычно орбитер содержит элементы навигации и контекстные действия, связанные с сущностью, к которой он прикреплен. Например, если вы создали пространственную панель для отображения видеоконтента, вы можете добавить элементы управления воспроизведением видео внутрь орбитера.
Как показано в следующем примере, вызовите обработчик событий внутри 2D-макета в SpatialPanel , чтобы обернуть элементы управления пользователя, такие как навигация. При этом они будут извлечены из вашего 2D-макета и прикреплены к пространственной панели в соответствии с вашей конфигурацией.
Поскольку орбитеры являются пространственными компонентами пользовательского интерфейса, код можно повторно использовать в 2D или 3D макетах. В 2D макете ваше приложение отображает только содержимое внутри орбитера и игнорирует сам орбитер.
Ознакомьтесь с нашими рекомендациями по проектированию, чтобы получить дополнительную информацию о том, как использовать и проектировать орбитальные аппараты.
Добавьте несколько пространственных панелей в пространственную схему.
Для макетов с несколькими панелями в ряд мы рекомендуем установить радиус кривизны 825dp с помощью SubspaceModifier , чтобы панели окружали пользователя. Подробности см. в наших рекомендациях по дизайну .
Добавьте 3D-объект в свой макет, используя SpatialGltfModel.
Android XR поддерживает формат glTF для 3D-моделей, обычно сохраняемых в виде файлов .glb . Чтобы добавить эти объекты в макет, следует использовать компонент SpatialGltfModel . Этот API упрощает процесс загрузки ресурсов и управления их состоянием.
Для отображения модели сначала определите её источник и состояние с помощью rememberSpatialGltfModelState . Вы можете загружать модели из папки assets вашего приложения, по URI или raw data .
Асинхронная загрузка: Модель загружается асинхронно. На этапе первоначального компоновочного процесса ее собственный размер может быть равен нулю; после готовности модели производится повторное измерение параметров компоновки.
Управление состоянием: используйте SpatialGltfModelState.status для запроса статуса загрузки или управления анимацией.
Изменение размера и масштабирование: по умолчанию размер макета соответствует ограничивающему прямоугольнику объекта. Вы можете переопределить это значение с помощью SubspaceModifier.size , чтобы масштабировать модель равномерно и она соответствовала заданным границам.
Используйте SceneCoreEntity для размещения объектов в вашем макете.
Компонент SceneCoreEntity связывает библиотеки Jetpack SceneCore и Compose for XR , позволяя использовать сущности, созданные с помощью SceneCore, в макетах Compose. Это позволяет создавать сущности нижнего уровня и пользовательские компоненты, в то время как Compose позволяет изменять размер, позиционирование, изменять родительский элемент, добавлять дочерние элементы и применять модификаторы к этим сущностям.
Subspace{SceneCoreEntity(modifier=SubspaceModifier.offset(x=50.dp),factory={SurfaceEntity.create(session=session,pose=Pose.Identity,stereoMode=SurfaceEntity.StereoMode.MONO)},update={entity->
// compose state changes may be applied to the// SceneCore entity here.entity.stereoMode=SurfaceEntity.StereoMode.SIDE_BY_SIDE},sizeAdapter=SceneCoreEntitySizeAdapter({IntSize2d(it.width,it.height)}),){// Content here will be children of the SceneCoreEntity// in the scene graph.}}
Добавьте поверхность для размещения изображений или видеоконтента.
SpatialExternalSurface — это компонуемый подпространство объект, который создает и управляет Surface , на которую ваше приложение может отображать контент, например, изображение или видео . SpatialExternalSurface поддерживает как стереоскопический, так и моноскопический контент.
@OptIn(ExperimentalComposeApi::class)@ComposablefunSpatialExternalSurfaceContent(){valcontext=LocalContext.currentSubspace{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 contentstereoMode=StereoMode.SideBySide,){valexoPlayer=remember{ExoPlayer.Builder(context).build()}valvideoUri=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()valmediaItem=MediaItem.fromUri(videoUri)// onSurfaceCreated is invoked only one time, when the Surface is createdonSurfaceCreated{surface->
exoPlayer.setVideoSurface(surface)exoPlayer.setMediaItem(mediaItem)exoPlayer.prepare()exoPlayer.play()}// onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its// associated Surface are destroyedonSurfaceDestroyed{exoPlayer.release()}}}}
В зависимости от типа отображаемого контента установите для параметра StereoMode значение Mono , SideBySide или TopBottom :
Mono : Кадр изображения или видео состоит из одного, идентичного изображения, отображаемого обоим глазам.
SideBySide : Кадр изображения или видео содержит пару изображений или кадров видео, расположенных рядом друг с другом, где изображение или кадр слева представляет собой вид для левого глаза, а изображение или кадр справа — вид для правого глаза.
TopBottom : Кадр изображения или видео содержит пару изображений или кадров видео, расположенных вертикально друг над другом, где изображение или кадр сверху представляет собой вид для левого глаза, а изображение или кадр снизу — вид для правого глаза.
Синхронизация изменений режима StereoMode с рендерингом приложения или декодированием видео невозможна.
Этот компонент не может отображаться поверх других панелей, поэтому не следует использовать MovePolicy если в макете есть другие панели.
Добавьте поверхность для размещения видеоконтента, защищенного DRM.
SpatialExternalSurface также поддерживает воспроизведение видеопотоков, защищенных DRM. Для этого необходимо создать защищенную поверхность, которая отображает контент в защищенные графические буферы. Это предотвратит запись экрана или доступ к контенту со стороны незащищенных системных компонентов.
Для создания защищенной поверхности установите параметр surfaceProtection в значение SurfaceProtection.Protected для составного объекта SpatialExternalSurface . Кроме того, необходимо настроить Media3 Exoplayer с соответствующей информацией DRM для обработки получения лицензий с лицензионного сервера.
В следующем примере показано, как настроить SpatialExternalSurface и ExoPlayer для воспроизведения видеопотока, защищенного DRM:
@OptIn(ExperimentalComposeApi::class)@ComposablefunDrmSpatialVideoPlayer(){valcontext=LocalContext.currentSubspace{SpatialExternalSurface(modifier=SubspaceModifier.width(1200.dp).height(676.dp),stereoMode=StereoMode.SideBySide,surfaceProtection=SurfaceProtection.Protected){valexoPlayer=remember{ExoPlayer.Builder(context).build()}// Define the URI for your DRM-protected content and license server.valvideoUri="https://your-content-provider.com/video.mpd"valdrmLicenseUrl="https://your-license-server.com/license"// Build a MediaItem with the necessary DRM configuration.valmediaItem=MediaItem.Builder().setUri(videoUri).setDrmConfiguration(MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).setLicenseUri(drmLicenseUrl).build()).build()onSurfaceCreated{surface->
// The created surface is secure and can be used by the player.exoPlayer.setVideoSurface(surface)exoPlayer.setMediaItem(mediaItem)exoPlayer.prepare()exoPlayer.play()}onSurfaceDestroyed{exoPlayer.release()}}}}
Защищенная поверхность: Установка surfaceProtection = SurfaceProtection.Protected для SpatialExternalSurface необходима для того, чтобы базовая Surface была защищена буферами, подходящими для контента с DRM-защитой.
Настройка DRM: Необходимо настроить MediaItem , указав схему DRM (например, C.WIDEVINE_UUID ) и URI вашего лицензионного сервера. ExoPlayer использует эту информацию для управления сеансом DRM.
Защищенный контент: При рендеринге на защищенную поверхность видеоконтент декодируется и отображается по защищенному каналу, что помогает выполнить требования лицензирования контента. Это также предотвращает появление контента в снимках экрана.
Добавьте другие пространственные компоненты пользовательского интерфейса.
Пространственные компоненты пользовательского интерфейса можно размещать в любом месте иерархии пользовательского интерфейса вашего приложения. Эти элементы можно повторно использовать в вашем 2D-интерфейсе, и их пространственные атрибуты будут видны только при включенных пространственных возможностях. Это позволяет добавлять возвышения к меню, диалоговым окнам и другим компонентам без необходимости писать код дважды. См. следующие примеры пространственного пользовательского интерфейса, чтобы лучше понять, как использовать эти элементы.
Компонент пользовательского интерфейса
Когда включена пространственная синхронизация
В двухмерной среде
SpatialDialog
Панель немного сдвинется назад по оси Z, чтобы отобразить диалоговое окно с увеличенным экраном.
Панель немного сдвинется назад по оси Z, чтобы отобразить приподнятое всплывающее окно.
В случае появления всплывающего окна в 2D-формате, происходит переход к следующему Popup .
SpatialElevation
SpatialElevationLevel позволяет добавлять отметки высоты.
Отображается без учета пространственного возвышения.
SpatialDialog
Это пример диалогового окна, которое открывается с небольшой задержкой. При использовании SpatialDialog диалоговое окно отображается на той же глубине по оси Z, что и пространственная панель, а панель сдвигается на 125dp, когда включена пространственная ориентация. SpatialDialog также можно использовать, когда пространственная ориентация не включена, в этом случае SpatialDialog возвращается к своему 2D-аналогу, Dialog .
Для создания пользовательских панелей, не поддерживаемых Compose for XR, вы можете работать напрямую с экземплярами PanelEntity и графом сцены, используя API SceneCore .
Привязать орбитальные аппараты к космическим панелям и макетам.
Вы можете привязать орбитер к SpatialPanels и компонентам пространственной компоновки, объявленным в Compose. Это включает в себя объявление орбитера в пространственной компоновке элементов пользовательского интерфейса, таких как SpatialRow , SpatialColumn или SpatialBox . Орбитер привязывается к родительскому элементу, ближайшему к месту его объявления.
Поведение орбитального аппарата определяется местом его объявления:
В двухмерной компоновке, заключенной в SpatialPanel (как показано в предыдущем фрагменте кода ), орбитер привязывается к этому SpatialPanel .
В Subspace орбитальный аппарат привязывается к ближайшему родительскому объекту, которым является пространственная конфигурация, в которой он объявлен.
В следующем примере показано, как привязать орбитальный аппарат к пространственной строке:
Когда вы объявляете орбитер вне двумерной структуры, орбитер привязывается к ближайшему родительскому объекту. В данном случае орбитер привязывается к верхней части SpatialRow , в котором он объявлен.
Пространственные макеты, такие как SpatialRow , SpatialColumn , SpatialBox , содержат сущности, не имеющие содержимого. Поэтому объект-орбитер, объявленный в пространственном макете, привязывается к этому макету.
Контент и образцы кода на этой странице предоставлены по лицензиям. Java и OpenJDK – это зарегистрированные товарные знаки корпорации Oracle и ее аффилированных лиц.
Последнее обновление: 2026-05-06 UTC.
[[["Прост для понимания","easyToUnderstand","thumb-up"],["Помог мне решить мою проблему","solvedMyProblem","thumb-up"],["Другое","otherUp","thumb-up"]],[["Отсутствует нужная мне информация","missingTheInformationINeed","thumb-down"],["Слишком сложен/слишком много шагов","tooComplicatedTooManySteps","thumb-down"],["Устарел","outOfDate","thumb-down"],["Проблема с переводом текста","translationIssue","thumb-down"],["Проблемы образцов/кода","samplesCodeIssue","thumb-down"],["Другое","otherDown","thumb-down"]],["Последнее обновление: 2026-05-06 UTC."],[],[]]