Разработка пространственного пользовательского интерфейса с помощью 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 ][4]. Вызов вне подпространства вызовет исключение.
Размер SpatialPanel задается с помощью параметров height и width заданных в SubspaceModifier . Если эти параметры не указаны, размер панели определяется размерами ее содержимого.
Предоставьте пользователю возможность перемещать панель, добавив MovePolicy .
Предоставьте пользователю возможность изменять размер панели, добавив ResizePolicy .
По умолчанию, когда пользователь перемещает панель от себя, MovePolicy масштабирует панель аналогично тому, как система изменяет размер панелей в домашнем пространстве . Все дочерние элементы наследуют это поведение. Чтобы отключить это, установите параметр shouldScaleWithDistance в false .
Создать орбитальный аппарат
Компонент «Орбитер» — это пространственный элемент пользовательского интерфейса. Он предназначен для прикрепления к соответствующей пространственной панели, макету или другому объекту. Обычно орбитер содержит элементы навигации и контекстные действия, связанные с объектом, к которому он прикреплен. Например, если вы создали пространственную панель для отображения видеоконтента, вы можете добавить элементы управления воспроизведением видео внутрь орбитера.
Как показано в следующем примере, вызовите обработчик событий внутри 2D-макета в SpatialPanel , чтобы обернуть элементы управления пользователя, такие как навигация. При этом они будут извлечены из вашего 2D-макета и прикреплены к пространственной панели в соответствии с вашей конфигурацией.
Поскольку орбитеры являются пространственными компонентами пользовательского интерфейса, код можно повторно использовать в 2D или 3D макетах. В 2D макете ваше приложение отображает только содержимое внутри орбитера и игнорирует сам орбитер.
Ознакомьтесь с нашими рекомендациями по проектированию, чтобы получить дополнительную информацию о том, как использовать и проектировать орбитальные аппараты.
Добавьте несколько пространственных панелей в пространственную схему.
Для макетов с несколькими панелями в ряд мы рекомендуем установить радиус кривизны 825dp с помощью SubspaceModifier , чтобы панели окружали пользователя. Подробности см. в наших рекомендациях по дизайну .
Используйте SceneCoreEntity для размещения объектов в вашем макете.
Для размещения 3D-объекта в макете вам потребуется использовать компонуемый подпространственный объект, называемый SceneCoreEntity . Вот пример того, как это сделать.
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 .
Привязывайте орбитальные аппараты к пространственным конфигурациям и другим объектам.
Вы можете привязать орбитер к любому объекту, объявленному в Compose. Это включает в себя объявление орбитера в пространственной компоновке элементов пользовательского интерфейса, таких как SpatialRow , SpatialColumn или SpatialBox . Орбитер привязывается к ближайшему родительскому объекту, расположенному рядом с местом его объявления.
Поведение орбитального аппарата определяется местом его объявления:
В двухмерной компоновке, заключенной в SpatialPanel (как показано в предыдущем фрагменте кода ), орбитер привязывается к этому SpatialPanel .
В Subspace орбитальный аппарат привязывается к ближайшему родительскому объекту, которым является пространственная конфигурация, в которой он объявлен.
В следующем примере показано, как привязать орбитальный аппарат к пространственной строке:
Когда вы объявляете орбитер вне двумерной структуры, орбитер привязывается к ближайшему родительскому объекту. В данном случае орбитер привязывается к верхней части SpatialRow , в котором он объявлен.
Пространственные макеты, такие как SpatialRow , SpatialColumn , SpatialBox , содержат сущности, не имеющие содержимого. Поэтому объект-орбитер, объявленный в пространственном макете, привязывается к этому макету.
Контент и образцы кода на этой странице предоставлены по лицензиям. Java и OpenJDK – это зарегистрированные товарные знаки корпорации Oracle и ее аффилированных лиц.