Jetpack Compose for XR ช่วยให้คุณสร้าง UI และเลย์เอาต์เชิงพื้นที่แบบประกาศได้โดยใช้แนวคิด Compose ที่คุ้นเคย เช่น แถวและคอลัมน์ ซึ่งจะช่วยให้คุณขยาย UI ของ Android ที่มีอยู่ไปยังพื้นที่ 3 มิติ หรือสร้างแอปพลิเคชัน 3 มิติแบบสมจริงใหม่ทั้งหมดได้
หากคุณกำลังสร้างพื้นที่ให้กับแอปที่มีอยู่ซึ่งอิงตาม Android Views คุณจะมีตัวเลือกการพัฒนาหลายอย่าง คุณสามารถใช้ API การทำงานร่วมกัน ใช้ Compose และ Views ร่วมกัน หรือทำงานกับไลบรารี SceneCore โดยตรง ดูรายละเอียดเพิ่มเติมได้ในคำแนะนำเกี่ยวกับการทำงาน กับ Views
เกี่ยวกับพื้นที่ย่อยและคอมโพเนนต์เชิงพื้นที่
เมื่อเขียนแอปสำหรับ Android XR คุณต้องทำความเข้าใจแนวคิดเรื่อง พื้นที่ย่อยและ คอมโพเนนต์เชิงพื้นที่
เกี่ยวกับพื้นที่ย่อย
เมื่อพัฒนาแอปสำหรับ Android XR คุณจะต้องเพิ่มพื้นที่ย่อยลงในแอปหรือเลย์เอาต์ พื้นที่ย่อยคือพาร์ติชันของพื้นที่ 3 มิติภายในแอปที่คุณสามารถวางเนื้อหา 3 มิติ สร้างเลย์เอาต์ 3 มิติ และเพิ่มความลึกให้กับเนื้อหา 2 มิติ ระบบจะแสดงพื้นที่ย่อยก็ต่อเมื่อเปิดใช้การสร้างพื้นที่ ใน Home Space หรือในอุปกรณ์ที่ไม่ใช่ XR ระบบจะไม่สนใจโค้ดใดๆ ภายในพื้นที่ย่อยนั้น
การสร้างพื้นที่ย่อยทำได้หลายวิธีดังนี้
Subspace: คอมโพสได้นี้จะสร้างลำดับชั้น UI เชิงพื้นที่ใหม่ที่เป็นอิสระ โดยจะไม่รับช่วงตำแหน่งเชิงพื้นที่ การวางแนว หรือขนาดของSubspaceระดับบนสุดที่ซ้อนอยู่ ระบบจะผูกSubspaceกับกล่องเนื้อหาที่แนะนำโดยอัตโนมัติPlanarEmbeddedSubspace: คอมโพสได้นี้สามารถวางไว้ในลำดับชั้น UI ของแอป ซึ่งช่วยให้คุณรักษาเลย์เอาต์สำหรับ UI 2 มิติและเชิงพื้นที่ได้PlanarEmbeddedSubspaceจะคำนึงถึงข้อจำกัดและการวางตำแหน่งของคอมโพเนนต์ระดับบนสุด จากนั้นระบบจะวางตำแหน่งเนื้อหา 3 มิติที่วางไว้ภายในคอมโพเนนต์นี้โดยอิงตามพื้นที่ที่กำหนดในแบบ 2 มิติ
ดูข้อมูลเพิ่มเติมได้ที่หัวข้อเพิ่มพื้นที่ย่อยลงในแอป
เกี่ยวกับคอมโพเนนต์เชิงพื้นที่
คอมโพสได้ของพื้นที่ย่อย: คอมโพเนนต์เหล่านี้จะแสดงผลได้ในพื้นที่ย่อยเท่านั้น
คุณต้องใส่คอมโพเนนต์เหล่านี้ไว้ใน Subspace ก่อนที่จะวางไว้ในเลย์เอาต์ 2 มิติ
A SubspaceModifier ช่วยให้คุณเพิ่มแอตทริบิวต์ต่างๆ เช่น ความลึก ออฟเซ็ต และ
การวางตำแหน่ง ลงในคอมโพสได้ของพื้นที่ย่อย
คอมโพเนนต์เชิงพื้นที่อื่นๆ ไม่จำเป็นต้องเรียกใช้ภายในพื้นที่ย่อย โดยคอมโพเนนต์เหล่านี้ประกอบด้วยองค์ประกอบ 2 มิติแบบเดิมที่ห่อหุ้มไว้ในคอนเทนเนอร์เชิงพื้นที่ คุณสามารถใช้องค์ประกอบเหล่านี้ภายในเลย์เอาต์ 2 มิติหรือ 3 มิติได้หากกำหนดไว้สำหรับทั้ง 2 อย่าง เมื่อไม่ได้เปิดใช้การสร้างพื้นที่ ระบบจะไม่สนใจฟีเจอร์เชิงพื้นที่ของคอมโพเนนต์เหล่านี้ และคอมโพเนนต์จะกลับไปใช้คอมโพเนนต์ 2 มิติที่เกี่ยวข้อง
สร้างแผงเชิงพื้นที่
A SpatialPanel คือคอมโพสได้ของพื้นที่ย่อยที่ช่วยให้คุณแสดงเนื้อหาของแอป
ได้ เช่น คุณสามารถแสดงการเล่นวิดีโอ รูปภาพนิ่ง หรือเนื้อหา
อื่นๆ ในแผงเชิงพื้นที่

คุณสามารถใช้ SubspaceModifier เพื่อเปลี่ยนขนาด ลักษณะการทำงาน และการวางตำแหน่งของแผงเชิงพื้นที่ได้ ดังที่แสดงในตัวอย่างต่อไปนี้
Subspace { SpatialPanel( SubspaceModifier .height(824.dp) .width(1400.dp), dragPolicy = MovePolicy(), resizePolicy = ResizePolicy(), ) { 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 ) } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- เนื่องจาก API ของ
SpatialPanelเป็นคอมโพสได้ของพื้นที่ย่อย คุณจึงต้องเรียกใช้ API เหล่านี้ภายในSubspaceการเรียกใช้ API เหล่านี้ภายนอกพื้นที่ย่อยจะทำให้เกิดข้อยกเว้น - ขนาดของ
SpatialPanelได้รับการตั้งค่าโดยใช้ข้อกำหนดheightและwidthในSubspaceModifierการละเว้นข้อกำหนดเหล่านี้จะทำให้ขนาดของแผงกำหนดโดยการวัดขนาดของเนื้อหา - อนุญาตให้ผู้ใช้ย้ายแผงได้โดยเพิ่มตัวปรับแต่งพื้นที่ย่อย
movable - อนุญาตให้ผู้ใช้ปรับขนาดแผงได้โดยเพิ่มตัวปรับแต่งพื้นที่ย่อย
resizablesubspace - ดูรายละเอียดเกี่ยวกับการปรับขนาดและ การวางตำแหน่งได้ในคำแนะนำการออกแบบแผงเชิงพื้นที่ ดูรายละเอียดเพิ่มเติมเกี่ยวกับการติดตั้งใช้งานโค้ดได้ในเอกสารอ้างอิง
วิธีทำงานของตัวปรับแต่ง movable
เมื่อผู้ใช้ย้ายแผงออกจากตัวผู้ใช้ ตัวปรับแต่ง movable
จะปรับขนาดแผงในลักษณะเดียวกับที่ระบบปรับขนาดแผงใน
Home Space โดยค่าเริ่มต้น เนื้อหาที่เป็นองค์ประกอบย่อยทั้งหมดจะรับช่วงลักษณะการทำงานนี้ หากต้องการปิดใช้ลักษณะการทำงานนี้ ให้ตั้งค่าพารามิเตอร์ shouldScaleWithDistance เป็น false
สร้างออร์บิเตอร์
ออร์บิเตอร์คือคอมโพเนนต์ UI เชิงพื้นที่ โดยได้รับการออกแบบมาให้แนบกับคอมโพเนนต์แผงเชิงพื้นที่หรือเลย์เอาต์เชิงพื้นที่ที่เกี่ยวข้อง เช่น SpatialColumn, SpatialRow หรือ SpatialBox โดยปกติแล้วออร์บิเตอร์จะมีรายการการนำทางและการดำเนินการตามบริบทที่เกี่ยวข้องกับเอนทิตีที่ออร์บิเตอร์ยึดอยู่ ตัวอย่างเช่น หากคุณสร้างแผงเชิงพื้นที่เพื่อแสดงเนื้อหาวิดีโอ คุณสามารถเพิ่มการควบคุมการเล่นวิดีโอไว้ในออร์บิเตอร์ได้

เรียกใช้ออร์บิเตอร์ภายในเลย์เอาต์ 2 มิติใน SpatialPanel เพื่อห่อหุ้มการควบคุมของผู้ใช้ เช่น การนำทาง ดังที่แสดงในตัวอย่างต่อไปนี้ การทำเช่นนี้จะแยกการควบคุมออกจากเลย์เอาต์ 2 มิติและแนบการควบคุมเหล่านั้นกับแผงเชิงพื้นที่ตามการกำหนดค่าของคุณ
Subspace { SpatialPanel( SubspaceModifier .height(824.dp) .width(1400.dp), dragPolicy = MovePolicy(), resizePolicy = ResizePolicy(), ) { 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 ) } } } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- เนื่องจากออร์บิเตอร์เป็นคอมโพเนนต์ UI เชิงพื้นที่ คุณจึงนำโค้ดไปใช้ซ้ำในเลย์เอาต์ 2 มิติหรือ 3 มิติได้ ในเลย์เอาต์ 2 มิติ แอปจะแสดงผลเฉพาะเนื้อหาภายในออร์บิเตอร์และไม่สนใจออร์บิเตอร์เอง
- ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้ออร์บิเตอร์และการออกแบบออร์บิเตอร์ได้ในคำแนะนำการออกแบบ
เพิ่มแผงเชิงพื้นที่หลายแผงลงในเลย์เอาต์เชิงพื้นที่
คุณสามารถสร้างแผงเชิงพื้นที่หลายแผงและวางแผงเหล่านั้นไว้ในเลย์เอาต์เชิงพื้นที่ได้
โดยใช้ SpatialRow, SpatialColumn, SpatialBox และ
SpatialSpacer

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีดำเนินการ
Subspace { SpatialRow { SpatialColumn { SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) { SpatialPanelContent("Top Left") } SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) { SpatialPanelContent("Middle Left") } SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) { SpatialPanelContent("Bottom Left") } } SpatialColumn { SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) { SpatialPanelContent("Top Right") } SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) { SpatialPanelContent("Middle Right") } SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) { SpatialPanelContent("Bottom Right") } } } }
@Composable fun SpatialPanelContent(text: String) { Column( Modifier .background(color = Color.Black) .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "Panel", color = Color.White, fontSize = 15.sp ) Text( text = text, color = Color.White, fontSize = 25.sp, fontWeight = FontWeight.Bold ) } }
ประเด็นสำคัญเกี่ยวกับโค้ด
SpatialRow,SpatialColumn,SpatialBoxและSpatialSpacerเป็นคอมโพสได้ของพื้นที่ย่อยทั้งหมดและต้องวางไว้ ภายในพื้นที่ย่อย- ใช้
SubspaceModifierเพื่อปรับแต่งเลย์เอาต์ - สำหรับเลย์เอาต์ที่มีหลายแผงในแถวเดียว เราขอแนะนำให้ตั้งค่ารัศมีเส้นโค้งเป็น 825dp โดยใช้
SubspaceModifierเพื่อให้แผงล้อมรอบผู้ใช้ ดูรายละเอียดได้ในคำแนะนำการออกแบบ
เพิ่มออบเจ็กต์ 3 มิติลงในเลย์เอาต์โดยใช้ SpatialGltfModel
Android XR รองรับรูปแบบ glTF สำหรับโมเดล 3 มิติ ซึ่งโดยปกติจะบันทึกเป็นไฟล์
.glb หากต้องการเพิ่มออบเจ็กต์เหล่านี้ลงในเลย์เอาต์ คุณควรใช้คอมโพสได้
SpatialGltfModel API นี้จะช่วยลดความซับซ้อนของกระบวนการโหลดชิ้นงานและการจัดการสถานะของชิ้นงาน
หากต้องการแสดงโมเดล ให้กำหนดแหล่งที่มาและสถานะของโมเดลก่อนโดยใช้
rememberSpatialGltfModelState คุณสามารถโหลด
โมเดลจากโฟลเดอร์ assets ของแอป, URI หรือ
raw data
val modelState = rememberSpatialGltfModelState( source = SpatialGltfModelSource.fromPath( Paths.get("models/model_name.glb") ) )
เมื่อกำหนดสถานะแล้ว ให้ใช้คอมโพสได้ SpatialGltfModel เพื่อแสดงผลโมเดลภายในพื้นที่ย่อย
SpatialGltfModel(state = modelState, modifier = SubspaceModifier)
ประเด็นสำคัญเกี่ยวกับโค้ด
- การโหลดแบบไม่พร้อมกัน: ระบบจะโหลดโมเดลแบบไม่พร้อมกัน ในระหว่างการคอมโพสครั้งแรก ขนาดโดยธรรมชาติของโมเดลอาจเป็น 0 เลย์เอาต์จะวัดขนาดอีกครั้งเมื่อโมเดลพร้อม
- การควบคุมสถานะ: ใช้
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. } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- บล็อก Factory: บล็อก Factory คือตำแหน่งที่คุณเริ่มต้นเอนทิตี
SceneCoreที่อยู่เบื้องหลัง - บล็อก Update: ใช้บล็อก Update เพื่อแก้ไขพร็อพเพอร์ตี้ของเอนทิตีเพื่อตอบสนองต่อการเปลี่ยนแปลงสถานะ Compose
- การปรับขนาด:
sizeAdapterจะสื่อสารขนาดของเอนทิตีกลับไปยังระบบเลย์เอาต์ Compose
ข้อมูลเพิ่มเติม
- ดูหัวข้อเพิ่มโมเดล 3 มิติลงในแอปเพื่อทำความเข้าใจวิธีโหลดเนื้อหา 3 มิติ
ภายใน
SceneCoreEntityให้ดียิ่งขึ้น
เพิ่มพื้นผิวสำหรับเนื้อหารูปภาพหรือวิดีโอ
A SpatialExternalSurface คือคอมโพสได้ของพื้นที่ย่อยที่สร้างและ
จัดการ Surface ซึ่งแอปสามารถวาดเนื้อหา เช่น รูปภาพหรือ
วิดีโอ ลงใน Surface นี้ได้ SpatialExternalSurface รองรับเนื้อหาแบบสามมิติหรือโมโนสโคปิก
ตัวอย่างนี้แสดงวิธีโหลดวิดีโอสเตอริโอสโคปิกแบบเคียงข้างกันโดยใช้
Media3 Exoplayer และ 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() } } } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- ตั้งค่า
StereoModeเป็นMono,SideBySide, หรือTopBottomขึ้นอยู่กับประเภทเนื้อหาที่คุณแสดงผล ดังนี้Mono: เฟรมรูปภาพหรือวิดีโอประกอบด้วยรูปภาพเดียวที่เหมือนกันซึ่งแสดงต่อดวงตาทั้ง 2 ข้างSideBySide: เฟรมรูปภาพหรือวิดีโอมีรูปภาพหรือเฟรมวิดีโอ 2 รายการที่จัดเรียงเคียงข้างกัน โดยรูปภาพหรือเฟรมทางด้านซ้ายแสดงมุมมองของตาซ้าย และรูปภาพหรือเฟรมทางด้านขวาแสดงมุมมองของตาขวาTopBottom: เฟรมรูปภาพหรือวิดีโอมีรูปภาพหรือเฟรมวิดีโอ 2 รายการที่ซ้อนกันในแนวตั้ง โดยรูปภาพหรือเฟรมด้านบนแสดงมุมมองของตาซ้าย และรูปภาพหรือเฟรมด้านล่างแสดงมุมมองของตาขวา
SpatialExternalSurfaceรองรับเฉพาะพื้นผิวสี่เหลี่ยมผืนผ้าSurfaceนี้จะไม่บันทึกเหตุการณ์อินพุต- คุณไม่สามารถซิงโครไนซ์
StereoModeการเปลี่ยนแปลงกับการแสดงผลของแอปพลิเคชัน หรือการถอดรหัสวิดีโอ - คอมโพสได้นี้แสดงผลอยู่ด้านหน้าแผงอื่นๆ ไม่ได้ ดังนั้นคุณจึงไม่ควรใช้
MovePolicyหากมีแผงอื่นๆ ในเลย์เอาต์
เพิ่มพื้นผิวสำหรับเนื้อหาวิดีโอที่ได้รับการคุ้มครองโดย DRM
SpatialExternalSurface ยังรองรับการเล่นสตรีมวิดีโอที่ได้รับการคุ้มครองโดย DRM ด้วย หากต้องการเปิดใช้ฟีเจอร์นี้ คุณต้องสร้างพื้นผิวที่ปลอดภัยซึ่งแสดงผลไปยังบัฟเฟอร์กราฟิกที่ได้รับการคุ้มครอง ซึ่งจะป้องกันไม่ให้มีการบันทึกหน้าจอเนื้อหาหรือคอมโพเนนต์ระบบที่ไม่ปลอดภัยเข้าถึงเนื้อหา
หากต้องการสร้างพื้นผิวที่ปลอดภัย ให้ตั้งค่าพารามิเตอร์ SpatialExternalSurfaceProtection
เป็น SpatialExternalSurfaceProtection.Protected ใน
SpatialExternalSurface คอมโพสได้ นอกจากนี้ คุณต้องกำหนดค่า
Media3 Exoplayer ด้วยข้อมูล DRM ที่เหมาะสมเพื่อจัดการการ
ขอรับใบอนุญาตจากเซิร์ฟเวอร์ใบอนุญาต
ตัวอย่างต่อไปนี้แสดงวิธีกำหนดค่า SpatialExternalSurface และ ExoPlayer เพื่อเล่นสตรีมวิดีโอที่ได้รับการคุ้มครองโดย 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() } } } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- พื้นผิวที่ได้รับการคุ้มครอง: การตั้งค่า
surfaceProtection = SpatialExternalSurfaceProtection.ProtectedบนSpatialExternalSurfaceเป็นสิ่งสำคัญเพื่อให้Surfaceที่อยู่เบื้องหลังได้รับการสนับสนุนโดยบัฟเฟอร์ที่ปลอดภัยซึ่งเหมาะสำหรับเนื้อหา DRM - การกำหนดค่า DRM: คุณต้องกำหนดค่า
MediaItemด้วยรูปแบบ DRM (เช่นC.WIDEVINE_UUID) และ URI ของเซิร์ฟเวอร์ใบอนุญาต ExoPlayer จะใช้ข้อมูลนี้เพื่อจัดการเซสชัน DRM - เนื้อหาที่ปลอดภัย: เมื่อแสดงผลไปยังพื้นผิวที่ได้รับการคุ้มครอง ระบบจะถอดรหัสและแสดงเนื้อหาวิดีโอในเส้นทางที่ปลอดภัย ซึ่งช่วยให้เป็นไปตามข้อกำหนดการออกใบอนุญาตเนื้อหา นอกจากนี้ ยังป้องกันไม่ให้เนื้อหาปรากฏในภาพหน้าจอ
เพิ่มคอมโพเนนต์ UI เชิงพื้นที่อื่นๆ
คุณสามารถวางคอมโพเนนต์ UI เชิงพื้นที่ไว้ที่ใดก็ได้ในลำดับชั้น UI ของแอปพลิเคชัน คุณสามารถนำองค์ประกอบเหล่านี้ไปใช้ซ้ำใน UI 2 มิติได้ และแอตทริบิวต์เชิงพื้นที่ขององค์ประกอบจะปรากฏขึ้นเมื่อเปิดใช้ความสามารถเชิงพื้นที่เท่านั้น ซึ่งจะช่วยให้คุณเพิ่มระดับความสูงให้กับเมนู กล่องโต้ตอบ และคอมโพเนนต์อื่นๆ ได้โดยไม่ต้องเขียนโค้ด 2 ครั้ง ดูตัวอย่าง UI เชิงพื้นที่ต่อไปนี้เพื่อทำความเข้าใจวิธีใช้องค์ประกอบเหล่านี้ให้ดียิ่งขึ้น
คอมโพเนนต์ UI |
เมื่อเปิดใช้การสร้างพื้นที่ |
ในสภาพแวดล้อม 2 มิติ |
|---|---|---|
|
แผงจะเลื่อนถอยหลังเล็กน้อยในความลึกแกน Z เพื่อแสดงกล่องโต้ตอบที่ยกระดับ |
กลับไปใช้ 2 มิติ |
|
แผงจะเลื่อนถอยหลังเล็กน้อยในความลึกแกน Z เพื่อแสดงป๊อปอัปที่ยกระดับ |
กลับไปใช้ 2 มิติ |
|
ตั้งค่า |
แสดงโดยไม่มีระดับความสูงเชิงพื้นที่ |
SpatialDialog
นี่คือตัวอย่างกล่องโต้ตอบที่จะเปิดขึ้นหลังจากผ่านไปครู่หนึ่ง เมื่อใช้
SpatialDialog กล่องโต้ตอบจะปรากฏที่ความลึกแกน Z เดียวกับ
แผงเชิงพื้นที่ และแผงจะเลื่อนถอยหลัง 125dp เมื่อเปิดใช้การสร้างพื้นที่
SpatialDialog ยังใช้ได้เมื่อไม่ได้เปิดใช้การสร้างพื้นที่ ใน
กรณีนี้ SpatialDialog จะกลับไปใช้ Dialog ซึ่งเป็นคอมโพเนนต์ 2 มิติที่เกี่ยวข้อง
@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") } } } } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- นี่คือตัวอย่าง
SpatialDialogการใช้SpatialPopupและSpatialElevationมีลักษณะคล้ายกันมาก ดูรายละเอียดเพิ่มเติมได้ในเอกสารอ้างอิง API
สร้างแผงและเลย์เอาต์ที่กำหนดเอง
หากต้องการสร้างแผงที่กำหนดเองซึ่ง Compose for XR ไม่รองรับ คุณสามารถทำงาน
กับอินสแตนซ์ PanelEntity และกราฟฉากได้โดยตรงโดยใช้ API ของ
SceneCore
ยึดออร์บิเตอร์กับแผงและเลย์เอาต์เชิงพื้นที่
คุณสามารถยึดออร์บิเตอร์กับ SpatialPanels และคอมโพเนนต์เลย์เอาต์เชิงพื้นที่ที่ประกาศไว้ใน Compose ได้ ซึ่งเกี่ยวข้องกับการประกาศออร์บิเตอร์ในเลย์เอาต์เชิงพื้นที่ขององค์ประกอบ UI เช่น SpatialRow, SpatialColumn หรือ SpatialBox ออร์บิเตอร์จะยึดกับคอมโพเนนต์ระดับบนสุดที่อยู่ใกล้กับตำแหน่งที่คุณประกาศมากที่สุด
ลักษณะการทำงานของออร์บิเตอร์จะกำหนดโดยตำแหน่งที่คุณประกาศออร์บิเตอร์ ดังนี้
- ในเลย์เอาต์ 2 มิติที่ห่อหุ้มไว้ใน
SpatialPanel(ดังที่แสดงใน ข้อมูลโค้ดก่อนหน้า) ออร์บิเตอร์จะยึดกับSpatialPanelนั้น - ใน
Subspaceออร์บิเตอร์จะยึดกับเอนทิตีหลักที่อยู่ใกล้ที่สุด ซึ่งก็คือเลย์เอาต์เชิงพื้นที่ที่ประกาศออร์บิเตอร์ไว้
ตัวอย่างต่อไปนี้แสดงวิธียึดออร์บิเตอร์กับแถวเชิงพื้นที่
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) ) } } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- เมื่อประกาศออร์บิเตอร์ภายนอกเลย์เอาต์ 2 มิติ ออร์บิเตอร์จะยึดกับเอนทิตีหลักที่อยู่ใกล้ที่สุด ในกรณีนี้ ออร์บิเตอร์จะยึดกับด้านบนของ
SpatialRowที่ประกาศไว้ - เลย์เอาต์เชิงพื้นที่ เช่น
SpatialRow,SpatialColumn,SpatialBoxทั้งหมดมีเอนทิตีที่ไม่มีเนื้อหาเชื่อมโยงอยู่ ดังนั้น ออร์บิเตอร์ที่ประกาศไว้ในเลย์เอาต์เชิงพื้นที่จะยึดกับเลย์เอาต์นั้น