Jetpack Compose สำหรับ XR ช่วยให้คุณสร้าง UI เชิงพื้นที่และเลย์เอาต์ได้โดยใช้แนวคิด Compose ที่คุ้นเคย เช่น แถวและคอลัมน์ ซึ่งช่วยให้คุณ ขยาย UI ของ Android ที่มีอยู่ไปยังพื้นที่ 3 มิติ หรือสร้างแอปพลิเคชัน 3 มิติที่สมจริงใหม่ทั้งหมดได้
หากต้องการเพิ่มมิติให้กับแอปที่มีอยู่ซึ่งอิงตาม Android Views คุณมีตัวเลือกการพัฒนาหลายอย่าง คุณสามารถใช้ API การทำงานร่วมกัน ใช้ Compose และ Views ร่วมกัน หรือทำงานกับไลบรารี SceneCore โดยตรง ดูรายละเอียดเพิ่มเติมได้ในคำแนะนำในการทำงาน กับมุมมอง
เกี่ยวกับพื้นที่ย่อยและคอมโพเนนต์เชิงพื้นที่
เมื่อเขียนแอปสำหรับ Android XR คุณควรทำความเข้าใจแนวคิดของพื้นที่ย่อยและคอมโพเนนต์เชิงพื้นที่
เกี่ยวกับพื้นที่ย่อย
เมื่อพัฒนาสำหรับ Android XR คุณจะต้องเพิ่ม Subspace
ลงในแอป
หรือเลย์เอาต์ Subspace คือพาร์ติชันของพื้นที่ 3 มิติภายในแอปที่คุณสามารถ
วางเนื้อหา 3 มิติ สร้างเลย์เอาต์ 3 มิติ และเพิ่มมิติให้กับเนื้อหา 2 มิติ ระบบจะแสดง
พื้นที่ย่อยเมื่อเปิดใช้การปรับเสียงตามพื้นที่เท่านั้น ในพื้นที่บ้านหรือในอุปกรณ์ที่ไม่ใช่ XR ระบบจะไม่สนใจโค้ดใดๆ ภายในพื้นที่ย่อยนั้น
คุณสร้างพื้นที่ย่อยได้ 2 วิธีดังนี้
Subspace
: วาง Composable นี้ได้ทุกที่ภายในลำดับชั้น UI ของแอป ซึ่งช่วยให้คุณรักษาเลย์เอาต์สำหรับ UI 2 มิติและเชิงพื้นที่ได้โดยไม่ สูญเสียบริบทระหว่างไฟล์ ซึ่งจะช่วยให้แชร์สิ่งต่างๆ เช่น สถาปัตยกรรมของแอปที่มีอยู่ระหว่าง XR กับอุปกรณ์รูปแบบอื่นๆ ได้ง่ายขึ้นโดยไม่ต้อง ย้ายสถานะผ่านทั้งโครงสร้าง UI หรือออกแบบแอปใหม่ApplicationSubspace
: ฟังก์ชันนี้จะสร้างพื้นที่ย่อยระดับแอปเท่านั้น และต้องวางไว้ที่ระดับบนสุดในลำดับชั้น UI เชิงพื้นที่ของแอปพลิเคชันApplicationSubspace
แสดงเนื้อหาเชิงพื้นที่พร้อมกับVolumeConstraints
(ไม่บังคับ)Subspace
ต่างจากApplicationSubspace
ตรงที่ซ้อนอยู่ภายในSubspace
หรือApplicationSubspace
ไม่ได้
ดูข้อมูลเพิ่มเติมได้ที่เพิ่มพื้นที่ย่อยลงในแอป
เกี่ยวกับคอมโพเนนต์เชิงพื้นที่
Subspace composables: คอมโพเนนต์เหล่านี้จะแสดงผลได้ใน Subspace เท่านั้น
โดยต้องอยู่ใน Subspace
หรือ setSubspaceContent()
ก่อนที่จะ
วางในเลย์เอาต์ 2 มิติ SubspaceModifier
ช่วยให้คุณเพิ่มแอตทริบิวต์
เช่น ความลึก ออฟเซ็ต และการวางตำแหน่ง ลงใน Composable ของ Subspace ได้
ส่วนประกอบอื่นๆ ที่มีมิติเชิงพื้นที่ไม่จำเป็นต้องเรียกใช้ภายในพื้นที่ย่อย โดยประกอบด้วยองค์ประกอบ 2 มิติแบบเดิมที่ห่อหุ้มอยู่ภายในคอนเทนเนอร์เชิงพื้นที่ องค์ประกอบเหล่านี้สามารถใช้ภายในเลย์เอาต์ 2 มิติหรือ 3 มิติได้หากกำหนดไว้สำหรับทั้ง 2 อย่าง เมื่อไม่ได้เปิดใช้ การปรับเสียงตามตำแหน่ง ระบบจะไม่สนใจฟีเจอร์ที่ปรับเสียงตามตำแหน่งของเสียงเหล่านั้น และ จะกลับไปใช้ฟีเจอร์ 2 มิติแทน
สร้างแผงเชิงพื้นที่
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 ) } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- เนื่องจาก API ของ
SpatialPanel
เป็นแบบ Subspace Composables คุณจึงต้องเรียกใช้ API เหล่านี้ภายในSubspace
การเรียกใช้ฟังก์ชันเหล่านี้นอก Subspace จะทำให้เกิดข้อยกเว้น - ระบบได้ตั้งค่าขนาดของ
SpatialPanel
โดยใช้ข้อกำหนดheight
และwidth
ในSubspaceModifier
การละเว้นข้อกำหนดเหล่านี้จะทำให้ ระบบกำหนดขนาดของแผงตามการวัดเนื้อหาของแผง - อนุญาตให้ผู้ใช้ปรับขนาดหรือย้ายแผงโดยการเพิ่มตัวแก้ไข
movable
หรือresizable
- ดูรายละเอียดเกี่ยวกับการปรับขนาดและการวางตำแหน่งได้ในคำแนะนำการออกแบบแผงเชิงพื้นที่ ดูรายละเอียดเพิ่มเติมเกี่ยวกับการติดตั้งโค้ดได้ในเอกสารประกอบอ้างอิง
วิธีการทำงานของตัวแก้ไขพื้นที่ย่อยที่เคลื่อนย้ายได้
เมื่อผู้ใช้เลื่อนแผงออกไปจากตัว ผู้แก้ไขพื้นที่ย่อยที่เลื่อนได้จะปรับขนาดแผงในลักษณะเดียวกับที่ระบบปรับขนาดแผงในพื้นที่บ้านโดยค่าเริ่มต้น
เนื้อหาทั้งหมดสำหรับเด็กจะรับช่วงลักษณะการทำงานนี้ หากต้องการปิดใช้ ให้
ตั้งค่าพารามิเตอร์ scaleWithDistance
เป็น false
สร้างยานโคจร
Orbiter เป็นคอมโพเนนต์ UI เชิงพื้นที่ โดยออกแบบมาให้แนบกับ แผงเชิงพื้นที่ เลย์เอาต์ หรือเอนทิตีอื่นๆ ที่เกี่ยวข้อง โดยปกติแล้ว Orbiter จะมีรายการการนำทางและรายการการดำเนินการตามบริบทที่เกี่ยวข้องกับเอนทิตีที่ยึดไว้ เช่น หากคุณสร้างแผงเชิงพื้นที่เพื่อแสดงเนื้อหาวิดีโอ คุณก็เพิ่มตัวควบคุมการเล่นวิดีโอภายใน Orbiter ได้
ดังที่แสดงในตัวอย่างต่อไปนี้ ให้เรียกใช้ Orbiter ภายในเลย์เอาต์ 2 มิติใน
SpatialPanel
เพื่อห่อหุ้มการควบคุมของผู้ใช้ เช่น การนำทาง การทำเช่นนี้จะดึงข้อมูลจากเลย์เอาต์ 2 มิติและแนบไปกับแผงเชิงพื้นที่ตามการกำหนดค่าของคุณ
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 ) } } } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- เนื่องจากออบิเตอร์เป็นคอมโพเนนต์ UI เชิงพื้นที่ จึงสามารถนำโค้ดไปใช้ซ้ำในเลย์เอาต์ 2 มิติหรือ 3 มิติได้ ในเลย์เอาต์ 2 มิติ แอปจะแสดงเฉพาะเนื้อหาภายใน orbiter และไม่สนใจ orbiter เอง
- ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้และออกแบบออร์บิเตอร์ได้ในคำแนะนำด้านการออกแบบ
เพิ่มแผงเชิงพื้นที่หลายแผงไปยังเลย์เอาต์เชิงพื้นที่
คุณสร้างแผงเชิงพื้นที่ได้หลายแผงและวางไว้ในการจัดวางเชิงพื้นที่
โดยใช้ 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 ) } }
ประเด็นสำคัญเกี่ยวกับโค้ด
SpatialRow
,SpatialColumn
,SpatialBox
และSpatialLayoutSpacer
เป็นคอมโพสเซเบิลของ Subspace ทั้งหมดและต้องวาง ภายใน Subspace- ใช้
SubspaceModifier
เพื่อปรับแต่งเลย์เอาต์ - สำหรับเลย์เอาต์ที่มีหลายแผงในแถว เราขอแนะนำให้ตั้งค่ารัศมีโค้งเป็น 825dp โดยใช้
SubspaceModifier
เพื่อให้แผงล้อมรอบผู้ใช้ ดูรายละเอียดได้ในคำแนะนำด้านการออกแบบ
ใช้ปริมาตรเพื่อวางออบเจ็กต์ 3 มิติในเลย์เอาต์
หากต้องการวางออบเจ็กต์ 3 มิติในเลย์เอาต์ คุณจะต้องใช้ Subspace ที่ประกอบได้ ซึ่งเรียกว่าวอลุ่ม ตัวอย่างวิธีการมีดังนี้
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) {
ข้อมูลเพิ่มเติม
- ดูเพิ่มโมเดล 3 มิติลงในแอปเพื่อให้เข้าใจวิธีโหลดเนื้อหา 3 มิติภายในวอลุ่มได้ดียิ่งขึ้น
เพิ่มพื้นผิวสำหรับเนื้อหารูปภาพหรือวิดีโอ
SpatialExternalSurface
คือ Subspace ที่สามารถคอมโพสได้ซึ่งสร้างและ
จัดการ 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
กับการแสดงผลของแอปพลิเคชัน หรือการถอดรหัสวิดีโอ - Composable นี้แสดงผลอยู่หน้าแผงอื่นๆ ไม่ได้ ดังนั้นคุณจึงไม่ควรใช้ ตัวแก้ไขที่เคลื่อนย้ายได้หากมีแผงอื่นๆ ในเลย์เอาต์
เพิ่มแพลตฟอร์มสำหรับเนื้อหาวิดีโอที่ได้รับการคุ้มครองโดย DRM
SpatialExternalSurface
ยังรองรับการเล่นวิดีโอสตรีมที่ได้รับการคุ้มครองโดย DRM
ด้วย หากต้องการเปิดใช้ฟีเจอร์นี้ คุณต้องสร้างพื้นผิวที่ปลอดภัยซึ่งแสดงผลไปยังบัฟเฟอร์กราฟิกที่ได้รับการป้องกัน วิธีนี้จะช่วยป้องกันไม่ให้มีการบันทึกหน้าจอเนื้อหา
หรือเข้าถึงเนื้อหาโดยคอมโพเนนต์ของระบบที่ไม่ปลอดภัย
หากต้องการสร้างพื้นผิวที่ปลอดภัย ให้ตั้งค่าพารามิเตอร์ surfaceProtection
เป็น
SurfaceProtection.Protected
ใน Composable 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 = SurfaceProtection.Protected
เป็นSpatialExternalSurface
เป็นสิ่งจำเป็นเพื่อให้Surface
พื้นฐานได้รับการสนับสนุนโดยบัฟเฟอร์ที่ปลอดภัยซึ่งเหมาะสำหรับเนื้อหา DRM - การกำหนดค่า DRM: คุณต้องกำหนดค่า
MediaItem
ด้วยรูปแบบ DRM (เช่นC.WIDEVINE_UUID
) และ URI ของเซิร์ฟเวอร์ใบอนุญาต ExoPlayer ใช้ข้อมูลนี้เพื่อจัดการเซสชัน DRM - เนื้อหาที่ปลอดภัย: เมื่อแสดงผลไปยังพื้นผิวที่ได้รับการป้องกัน ระบบจะถอดรหัสและแสดงเนื้อหาวิดีโอในเส้นทางที่ปลอดภัย ซึ่งจะช่วยให้เป็นไปตามข้อกำหนดในการอนุญาตให้ใช้เนื้อหา นอกจากนี้ ยังช่วยป้องกันไม่ให้เนื้อหาปรากฏในภาพหน้าจอด้วย
เพิ่มคอมโพเนนต์ UI เชิงพื้นที่อื่นๆ
วางคอมโพเนนต์ UI เชิงพื้นที่ไว้ที่ใดก็ได้ในลําดับชั้น UI ของแอปพลิเคชัน คุณสามารถนำองค์ประกอบเหล่านี้ไปใช้ซ้ำใน UI 2 มิติได้ และแอตทริบิวต์เชิงพื้นที่จะ แสดงก็ต่อเมื่อเปิดใช้ความสามารถเชิงพื้นที่เท่านั้น ซึ่งช่วยให้คุณเพิ่ม ระดับความสูงให้กับเมนู กล่องโต้ตอบ และคอมโพเนนต์อื่นๆ ได้โดยไม่ต้องเขียน โค้ดซ้ำ ดูตัวอย่างต่อไปนี้ของ UI เชิงพื้นที่เพื่อให้เข้าใจวิธี ใช้องค์ประกอบเหล่านี้ได้ดียิ่งขึ้น
คอมโพเนนต์ UI |
เมื่อเปิดใช้การจัดวางเสียงตามพื้นที่ |
ในสภาพแวดล้อม 2 มิติ |
---|---|---|
|
แผงจะเลื่อนกลับไปเล็กน้อยในความลึกของแกน Z เพื่อแสดงกล่องโต้ตอบที่ยกระดับ |
กลับไปเป็น 2 มิติ |
|
แผงจะเลื่อนกลับไปเล็กน้อยในความลึกของแกน Z เพื่อแสดงป๊อปอัปที่ยกระดับ |
กลับไปใช้ |
|
|
รายการที่ไม่มีการยกระดับเชิงพื้นที่ |
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
และกราฟฉากได้โดยตรงโดยใช้
SceneCore
API
ยึดวงโคจรของสมอไปยังเลย์เอาต์เชิงพื้นที่และเอนทิตีอื่นๆ
คุณยึด Orbiter กับเอนทิตีใดก็ได้ที่ประกาศใน 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) ) } } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- เมื่อประกาศ Orbiter นอกเลย์เอาต์ 2 มิติ Orbiter จะยึดกับ
เอนทิตีระดับบนสุดที่ใกล้ที่สุด ในกรณีนี้ ออร์บิเตอร์จะยึดที่ด้านบนของ
SpatialRow
ที่ประกาศไว้ - เลย์เอาต์เชิงพื้นที่ เช่น
SpatialRow
,SpatialColumn
,SpatialBox
มีเอนทิตีที่ไม่มีเนื้อหาเชื่อมโยงอยู่ ดังนั้น ออบิเตอร์ที่ประกาศใน เลย์เอาต์เชิงพื้นที่จะยึดกับเลย์เอาต์นั้น
ดูเพิ่มเติม
- เพิ่มโมเดล 3 มิติลงในแอป
- พัฒนา UI สำหรับแอปที่อิงตามมุมมองของ Android
- ใช้การออกแบบ Material สำหรับ XR