Với Jetpack Compose cho XR, bạn có thể khai báo để tạo giao diện người dùng và bố cục không gian bằng các khái niệm quen thuộc của Compose, chẳng hạn như hàng và cột. Điều này cho phép bạn mở rộng giao diện người dùng Android hiện có sang không gian 3D hoặc tạo các ứng dụng 3D sống động hoàn toàn mới.
Nếu đang không gian hoá một ứng dụng hiện có dựa trên Khung hiển thị Android, bạn có một số lựa chọn phát triển. Bạn có thể sử dụng các API có khả năng tương tác, sử dụng Compose và Khung hiển thị cùng nhau hoặc làm việc trực tiếp với thư viện SceneCore. Hãy xem hướng dẫn về cách sử dụng chế độ xem của chúng tôi để biết thêm thông tin chi tiết.
Giới thiệu về không gian con và các thành phần được không gian hoá
Khi viết ứng dụng cho Android XR, bạn cần hiểu rõ các khái niệm về không gian con và thành phần không gian hoá.
Giới thiệu về subspace
Khi phát triển cho Android XR, bạn cần thêm một Subspace
vào ứng dụng hoặc bố cục của mình. Không gian phụ là một phân vùng của không gian 3D trong ứng dụng của bạn. Trong không gian phụ, bạn có thể đặt nội dung 3D, tạo các cấu trúc 3D hoặc thêm chiều sâu cho nội dung 2D. Không gian con chỉ được kết xuất khi bạn bật tính năng không gian hoá. Trong Không gian chính hoặc trên các thiết bị không phải XR, mọi mã trong không gian con đó đều bị bỏ qua.
Có hai cách để tạo không gian con:
Subspace
: Bạn có thể đặt thành phần kết hợp này ở bất kỳ vị trí nào trong hệ phân cấp giao diện người dùng của ứng dụng, cho phép bạn duy trì bố cục cho giao diện người dùng 2D và không gian mà không làm mất ngữ cảnh giữa các tệp. Điều này giúp bạn dễ dàng chia sẻ những thứ như cấu trúc ứng dụng hiện có giữa XR và các kiểu dáng thiết bị khác mà không cần nâng trạng thái thông qua toàn bộ cây giao diện người dùng hoặc tái cấu trúc ứng dụng.ApplicationSubspace
: Hàm này chỉ tạo không gian con ở cấp ứng dụng và phải được đặt ở cấp cao nhất trong hệ phân cấp giao diện người dùng không gian của ứng dụng.ApplicationSubspace
kết xuất nội dung không gian bằngVolumeConstraints
không bắt buộc. Không giống nhưSubspace
,ApplicationSubspace
không thể được lồng trong mộtSubspace
hoặcApplicationSubspace
khác.
Để biết thêm thông tin, hãy xem bài viết Thêm không gian con vào ứng dụng.
Giới thiệu về các thành phần được không gian hoá
Thành phần kết hợp trong không gian con: Các thành phần này chỉ có thể được kết xuất trong một không gian con.
Chúng phải được đặt trong Subspace
hoặc setSubspaceContent()
trước khi được đặt trong bố cục 2D. SubspaceModifier
cho phép bạn thêm các thuộc tính như chiều sâu, độ lệch và vị trí vào các thành phần kết hợp không gian con.
Các thành phần được không gian hoá khác không yêu cầu được gọi trong không gian con. Chúng bao gồm các phần tử 2D thông thường được bao bọc trong một vùng chứa không gian. Bạn có thể dùng các phần tử này trong bố cục 2D hoặc 3D nếu được xác định cho cả hai. Khi tính năng tạo hiệu ứng âm thanh không gian không được bật, các tính năng tạo hiệu ứng âm thanh không gian của những đối tượng này sẽ bị bỏ qua và chúng sẽ quay về các đối tượng 2D tương ứng.
Tạo bảng điều khiển không gian
SpatialPanel
là một thành phần kết hợp không gian con cho phép bạn hiển thị nội dung ứng dụng. Ví dụ: bạn có thể hiển thị nội dung phát video, hình ảnh tĩnh hoặc bất kỳ nội dung nào khác trong một bảng điều khiển không gian.
Bạn có thể dùng SubspaceModifier
để thay đổi kích thước, hành vi và vị trí của bảng điều khiển không gian, như minh hoạ trong ví dụ sau.
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 ) } }
Các điểm chính về mã
- Vì
SpatialPanel
là các thành phần kết hợp không gian con, nên bạn phải gọi chúng trongSubspace
. Việc gọi các hàm này bên ngoài không gian con sẽ tạo ra một trường hợp ngoại lệ. - Kích thước của
SpatialPanel
đã được đặt bằng cách sử dụng các quy cáchheight
vàwidth
trênSubspaceModifier
. Nếu bỏ qua các thông số này, kích thước của bảng điều khiển sẽ được xác định bằng kích thước của nội dung trong bảng điều khiển. - Cho phép người dùng đổi kích thước hoặc di chuyển bảng điều khiển bằng cách thêm các đối tượng sửa đổi
movable
hoặcresizable
. - Hãy xem hướng dẫn thiết kế bảng điều khiển không gian của chúng tôi để biết thông tin chi tiết về kích thước và vị trí. Hãy xem tài liệu tham khảo của chúng tôi để biết thêm thông tin cụ thể về việc triển khai mã.
Cách hoạt động của đối tượng sửa đổi không gian con có thể di chuyển
Khi người dùng di chuyển một bảng điều khiển ra xa, theo mặc định, đối tượng sửa đổi không gian phụ có thể di chuyển sẽ điều chỉnh tỷ lệ bảng điều khiển theo cách tương tự như cách hệ thống đổi kích thước bảng điều khiển trong không gian chính. Tất cả nội dung con đều kế thừa hành vi này. Để tắt chế độ này, hãy đặt tham số scaleWithDistance
thành false
.
Tạo một vệ tinh
Orbiter là một thành phần giao diện người dùng không gian. Thành phần này được thiết kế để gắn vào một bảng điều khiển không gian, bố cục hoặc thực thể tương ứng. Thông thường, orbiter chứa các mục hành động theo bối cảnh và điều hướng liên quan đến thực thể mà orbiter được liên kết. Ví dụ: nếu đã tạo một bảng điều khiển không gian để hiển thị nội dung video, bạn có thể thêm các nút điều khiển phát video vào bên trong một orbiter.
Như trong ví dụ sau, hãy gọi một orbiter bên trong bố cục 2D trong một SpatialPanel
để bao bọc các chế độ điều khiển của người dùng như thao tác điều hướng. Thao tác này sẽ trích xuất các thành phần đó từ bố cục 2D và đính kèm chúng vào bảng điều khiển không gian theo cấu hình của bạn.
Subspace { SpatialPanel( SubspaceModifier .height(824.dp) .width(1400.dp) .movable() .resizable() ) { SpatialPanelContent() OrbiterExample() } }
@Composable fun OrbiterExample() { Orbiter( position = ContentEdge.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 ) } } } }
Các điểm chính về mã
- Vì các orbiter là thành phần giao diện người dùng không gian, nên bạn có thể dùng lại mã này trong bố cục 2D hoặc 3D. Trong bố cục 2D, ứng dụng của bạn chỉ hiển thị nội dung bên trong orbiter và bỏ qua chính orbiter.
- Hãy xem hướng dẫn thiết kế của chúng tôi để biết thêm thông tin về cách sử dụng và thiết kế các thành phần chuyển động.
Thêm nhiều bảng điều khiển không gian vào bố cục không gian
Bạn có thể tạo nhiều bảng điều khiển không gian và đặt chúng trong một bố cục không gian bằng cách dùng SpatialRow
, SpatialColumn
, SpatialBox
và SpatialLayoutSpacer
.
Ví dụ về mã sau đây cho thấy cách thực hiện việc này.
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 ) } }
Các điểm chính về mã
SpatialRow
,SpatialColumn
,SpatialBox
vàSpatialLayoutSpacer
đều là các thành phần kết hợp không gian con và phải được đặt trong một không gian con.- Sử dụng biểu tượng
SubspaceModifier
để tuỳ chỉnh bố cục. - Đối với bố cục có nhiều bảng điều khiển liên tiếp, bạn nên đặt bán kính đường cong là 825 dp bằng cách sử dụng
SubspaceModifier
để các bảng điều khiển sẽ bao quanh người dùng. Hãy xem hướng dẫn thiết kế của chúng tôi để biết thông tin chi tiết.
Sử dụng một khối để đặt đối tượng 3D vào bố cục
Để đặt một đối tượng 3D trong bố cục, bạn cần sử dụng một thành phần kết hợp không gian con có tên là âm lượng. Sau đây là ví dụ về cách thực hiện.
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, ) } } }
@OptIn(ExperimentalSubspaceVolumeApi::class) @Composable fun ObjectInAVolume(show3DObject: Boolean) {
Thông tin khác
- Hãy xem phần Thêm mô hình 3D vào ứng dụng để hiểu rõ hơn về cách tải nội dung 3D trong một khối.
Thêm một vị trí xuất hiện cho nội dung hình ảnh hoặc video
SpatialExternalSurface
là một thành phần kết hợp không gian con có thể tạo và quản lý Surface
mà ứng dụng của bạn có thể vẽ nội dung, chẳng hạn như hình ảnh hoặc video. SpatialExternalSurface
hỗ trợ nội dung lập thể hoặc đơn lập thể.
Ví dụ này minh hoạ cách tải video lập thể song song bằng Media3 Exoplayer và SpatialExternalSurface
:
@OptIn(ExperimentalComposeApi::class) @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() } } } }
Các điểm chính về mã
- Đặt
StereoMode
thànhMono
,SideBySide
hoặcTopBottom
tuỳ thuộc vào loại nội dung mà bạn đang kết xuất:Mono
: Khung hình ảnh hoặc video bao gồm một hình ảnh duy nhất, giống hệt nhau được hiển thị cho cả hai mắt.SideBySide
: Khung hình ảnh hoặc video chứa một cặp hình ảnh hoặc khung hình video được sắp xếp cạnh nhau, trong đó hình ảnh hoặc khung hình ở bên trái thể hiện chế độ xem bằng mắt trái và hình ảnh hoặc khung hình ở bên phải thể hiện chế độ xem bằng mắt phải.TopBottom
: Khung hình ảnh hoặc video chứa một cặp hình ảnh hoặc khung hình video xếp chồng theo chiều dọc, trong đó hình ảnh hoặc khung hình ở trên cùng thể hiện chế độ xem bằng mắt trái và hình ảnh hoặc khung hình ở dưới cùng thể hiện chế độ xem bằng mắt phải.
SpatialExternalSurface
chỉ hỗ trợ các bề mặt hình chữ nhật.Surface
này không ghi lại các sự kiện đầu vào.- Không thể đồng bộ hoá các thay đổi
StereoMode
với quá trình kết xuất ứng dụng hoặc giải mã video. - Thành phần kết hợp này không thể kết xuất ở phía trước các bảng điều khiển khác, vì vậy, bạn không nên dùng các đối tượng sửa đổi có thể di chuyển nếu có các bảng điều khiển khác trong bố cục.
Thêm một nền tảng cho nội dung video được bảo vệ bằng DRM
SpatialExternalSurface
cũng hỗ trợ phát các luồng video được bảo vệ bằng DRM. Để bật tính năng này, bạn phải tạo một bề mặt bảo mật để kết xuất vào các vùng đệm đồ hoạ được bảo vệ. Việc này giúp ngăn nội dung bị ghi màn hình hoặc bị các thành phần hệ thống không an toàn truy cập.
Để tạo một nền tảng an toàn, hãy đặt tham số surfaceProtection
thành SurfaceProtection.Protected
trên thành phần kết hợp SpatialExternalSurface
.
Ngoài ra, bạn phải định cấu hình Media3 Exoplayer bằng thông tin DRM thích hợp để xử lý việc mua giấy phép từ máy chủ cấp phép.
Ví dụ sau đây minh hoạ cách định cấu hình SpatialExternalSurface
và ExoPlayer
để phát một luồng video được bảo vệ bằng DRM:
@OptIn(ExperimentalComposeApi::class) @Composable fun DrmSpatialVideoPlayer() { val context = LocalContext.current Subspace { SpatialExternalSurface( modifier = SubspaceModifier .width(1200.dp) .height(676.dp), stereoMode = StereoMode.SideBySide, surfaceProtection = SurfaceProtection.Protected ) { val exoPlayer = remember { ExoPlayer.Builder(context).build() } // Define the URI for your DRM-protected content and license server. val videoUri = "https://your-content-provider.com/video.mpd" val drmLicenseUrl = "https://your-license-server.com/license" // Build a MediaItem with the necessary DRM configuration. val mediaItem = 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() } } } }
Các điểm chính về mã
- Protected Surface: Việc đặt
surfaceProtection = SurfaceProtection.Protected
trênSpatialExternalSurface
là điều cần thiết đểSurface
cơ bản được hỗ trợ bởi các vùng đệm bảo mật phù hợp với nội dung DRM. - Cấu hình DRM: Bạn phải định cấu hình
MediaItem
bằng sơ đồ DRM (ví dụ:C.WIDEVINE_UUID
) và URI của máy chủ cấp phép. ExoPlayer sử dụng thông tin này để quản lý phiên DRM. - Nội dung bảo mật: Khi kết xuất sang một nền tảng được bảo vệ, nội dung video sẽ được giải mã và hiển thị trên một đường dẫn bảo mật, giúp đáp ứng các yêu cầu về cấp phép nội dung. Việc này cũng ngăn nội dung xuất hiện trong ảnh chụp màn hình.
Thêm các thành phần giao diện người dùng không gian khác
Bạn có thể đặt các thành phần giao diện người dùng không gian ở bất cứ đâu trong hệ phân cấp giao diện người dùng của ứng dụng. Bạn có thể dùng lại các phần tử này trong giao diện người dùng 2D và các thuộc tính không gian của chúng sẽ chỉ xuất hiện khi bạn bật các chức năng không gian. Điều này cho phép bạn thêm độ nâng cho các trình đơn, hộp thoại và các thành phần khác mà không cần viết mã hai lần. Hãy xem các ví dụ sau về giao diện người dùng không gian để hiểu rõ hơn về cách sử dụng các phần tử này.
Thành phần giao diện người dùng |
Khi bật tính năng tạo hiệu ứng âm thanh không gian |
Trong môi trường 2D |
---|---|---|
|
Bảng điều khiển sẽ đẩy nhẹ về phía sau theo chiều sâu z để hiển thị một hộp thoại nổi |
Quay lại |
|
Bảng điều khiển sẽ đẩy nhẹ về phía sau theo chiều sâu để hiển thị một cửa sổ bật lên nổi |
Quay lại |
|
Bạn có thể đặt |
Hiển thị không có độ cao không gian. |
SpatialDialog
Đây là ví dụ về một hộp thoại mở ra sau một khoảng thời gian ngắn. Khi SpatialDialog
được dùng, hộp thoại sẽ xuất hiện ở cùng độ sâu z như bảng điều khiển không gian và bảng điều khiển sẽ được đẩy lùi 125 dp khi tính năng không gian hoá được bật. SpatialDialog
cũng có thể được dùng khi tính năng không gian hoá không được bật. Trong trường hợp này, SpatialDialog
sẽ quay về phiên bản 2D tương ứng là 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") } } } } }
Các điểm chính về mã
- Đây là một ví dụ về
SpatialDialog
. Việc sử dụngSpatialPopup
vàSpatialElevation
rất giống nhau. Hãy xem tài liệu tham khảo API để biết thêm thông tin chi tiết.
Tạo bảng điều khiển và bố cục tuỳ chỉnh
Để tạo các bảng điều khiển tuỳ chỉnh không được Compose cho XR hỗ trợ, bạn có thể làm việc trực tiếp với các thực thể PanelEntity
và biểu đồ cảnh bằng cách sử dụng API SceneCore
.
Liên kết các đối tượng chuyển động theo quỹ đạo với bố cục không gian và các thực thể khác
Bạn có thể liên kết một đối tượng chuyển động tròn với bất kỳ thực thể nào được khai báo trong Compose. Việc này liên quan đến việc khai báo một orbiter trong bố cục không gian của các phần tử trên giao diện người dùng, chẳng hạn như SpatialRow
, SpatialColumn
hoặc SpatialBox
. Orbiter sẽ liên kết với thực thể mẹ gần nhất với vị trí mà bạn đã khai báo.
Hành vi của orbiter được xác định bằng vị trí bạn khai báo:
- Trong bố cục 2D được bao bọc trong một
SpatialPanel
(như minh hoạ trong đoạn mã trước đó), orbiter sẽ liên kết vớiSpatialPanel
đó. - Trong
Subspace
, orbiter liên kết với thực thể mẹ gần nhất, đó là bố cục không gian mà orbiter được khai báo.
Ví dụ sau đây cho thấy cách liên kết một orbiter với một hàng không gian:
Subspace { SpatialRow { Orbiter( position = ContentEdge.Top, offset = 8.dp, offsetType = OrbiterOffsetType.InnerEdge, shape = SpatialRoundedCornerShape(size = CornerSize(50)) ) { Text( "Hello World!", style = MaterialTheme.typography.titleMedium, 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) ) } } }
Các điểm chính về mã
- Khi bạn khai báo một orbiter bên ngoài bố cục 2D, orbiter sẽ liên kết với thực thể mẹ gần nhất. Trong trường hợp này, orbiter sẽ liên kết với đầu của
SpatialRow
mà orbiter được khai báo. - Các bố cục không gian như
SpatialRow
,SpatialColumn
,SpatialBox
đều có các thực thể không có nội dung liên kết với chúng. Do đó, một orbiter được khai báo trong bố cục không gian sẽ liên kết với bố cục đó.
Xem thêm
- Thêm mô hình 3D vào ứng dụng
- Phát triển giao diện người dùng cho các ứng dụng Android dựa trên Khung hiển thị
- Triển khai Material Design cho XR