Mengembangkan UI dengan Jetpack Compose untuk XR

Dengan Jetpack Compose untuk XR, Anda dapat mem-build UI dan tata letak spasial secara deklaratif menggunakan konsep Compose yang sudah dikenal seperti baris dan kolom. Hal ini memungkinkan Anda memperluas UI Android yang ada ke ruang 3D atau membuat aplikasi 3D imersif yang benar-benar baru.

Jika Anda melakukan spatialisasi pada aplikasi berbasis View Android yang ada, Anda memiliki beberapa opsi pengembangan. Anda dapat menggunakan API interoperabilitas, menggunakan Compose dan View secara bersamaan, atau bekerja langsung dengan library SceneCore. Lihat panduan untuk menggunakan tampilan kami untuk mengetahui detail selengkapnya.

Tentang subspace dan komponen yang di-spatialisasi

Saat menulis aplikasi untuk Android XR, penting untuk memahami konsep subruang dan komponen spasial.

Tentang subspace

Saat mengembangkan untuk Android XR, Anda harus menambahkan subspace ke aplikasi atau tata letak. Subruang adalah partisi ruang 3D dalam aplikasi tempat Anda dapat menempatkan konten 3D, membuat tata letak 3D, dan menambahkan kedalaman ke konten 2D. Subruang hanya dirender jika spatialisasi diaktifkan. Di Ruang Rumah atau di perangkat non-XR, kode apa pun dalam subruang tersebut akan diabaikan.

Ada dua cara untuk membuat subruang:

  • setSubspaceContent(): Fungsi ini membuat subruang tingkat aplikasi. Ini dapat dipanggil di aktivitas utama dengan cara yang sama seperti Anda menggunakan setContent(). Subruang tingkat aplikasi tidak terbatas dalam tinggi, lebar, dan kedalaman, yang pada dasarnya menyediakan kanvas tanpa batas untuk konten spasial.
  • Subspace: Composable ini dapat ditempatkan di mana saja dalam hierarki UI aplikasi, sehingga Anda dapat mempertahankan tata letak untuk UI 2D dan spasial tanpa kehilangan konteks antar-file. Hal ini memudahkan Anda berbagi hal-hal seperti arsitektur aplikasi yang ada antara XR dan faktor bentuk lainnya tanpa perlu mengangkat status melalui seluruh hierarki UI atau mendesain ulang aplikasi.

Untuk mengetahui informasi selengkapnya, lihat Menambahkan subspace ke aplikasi Anda.

Tentang komponen spasial

Composable subspace: Komponen ini hanya dapat dirender di subspace. Elemen ini harus diapit dalam Subspace atau setSubspaceContent sebelum ditempatkan dalam tata letak 2D. SubspaceModifier memungkinkan Anda menambahkan atribut seperti kedalaman, offset, dan pemosisian ke composable subspace.

Komponen spasial lainnya tidak perlu dipanggil di dalam subruang. Elemen ini terdiri dari elemen 2D konvensional yang digabungkan dalam penampung spasial. Elemen ini dapat digunakan dalam tata letak 2D atau 3D jika ditentukan untuk keduanya. Jika pemosisian spasial tidak diaktifkan, fitur yang diposisikan secara spasial akan diabaikan dan akan kembali ke versi 2D-nya.

Membuat panel spasial

SpatialPanel adalah composable subspace yang memungkinkan Anda menampilkan konten aplikasi–misalnya, Anda dapat menampilkan pemutaran video, gambar diam, atau konten lainnya di panel spasial.

Contoh panel UI spasial

Anda dapat menggunakan SubspaceModifier untuk mengubah ukuran, perilaku, dan pemosisian panel spasial, seperti yang ditunjukkan dalam contoh berikut.

Subspace {
   SpatialPanel(
        SubspaceModifier
           .height(824.dp)
           .width(1400.dp)
           .movable()
           .resizable()
           ) {
          SpatialPanelContent()
      }
}

// 2D content placed within the spatial panel
@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
        )
    }
}

Poin-poin penting tentang kode

Membuat orbiter

Orbiter adalah komponen UI spasial. Panel ini dirancang untuk dilampirkan ke panel spasial, tata letak, atau entitas lainnya yang sesuai. Orbiter biasanya berisi navigasi dan item tindakan kontekstual yang terkait dengan entity yang diikat. Misalnya, jika telah membuat panel spasial untuk menampilkan konten video, Anda dapat menambahkan kontrol pemutaran video di dalam orbiter.

Contoh orbiter

Seperti yang ditunjukkan pada contoh berikut, panggil orbiter di dalam tata letak 2D di SpatialPanel untuk menggabungkan kontrol pengguna seperti navigasi. Tindakan ini akan mengekstraknya dari tata letak 2D dan melampirkannya ke panel spasial sesuai dengan konfigurasi Anda.

setContent {
    Subspace {
        SpatialPanel(
            SubspaceModifier
                .height(824.dp)
                .width(1400.dp)
                .movable()
                .resizable()
        ) {
            SpatialPanelContent()
            OrbiterExample()
        }
    }
}

//2D content inside Orbiter
@Composable
fun OrbiterExample() {
    Orbiter(
        position = OrbiterEdge.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
                )
            }
        }
    }
}

Poin-poin penting tentang kode

  • Karena orbiter adalah komponen UI spasial, kode dapat digunakan kembali dalam tata letak 2D atau 3D. Dalam tata letak 2D, aplikasi Anda hanya merender konten di dalam orbiter dan mengabaikan orbiter itu sendiri.
  • Lihat panduan desain kami untuk informasi selengkapnya tentang cara menggunakan dan mendesain orbiter.

Menambahkan beberapa panel spasial ke tata letak spasial

Anda dapat membuat beberapa panel spasial dan menempatkannya dalam tata letak spasial menggunakan SpatialRow, SpatialColumn, SpatialBox, dan SpatialLayoutSpacer.

Contoh beberapa panel spasial dalam tata letak spasial

Contoh kode berikut menunjukkan cara melakukannya.

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
        )
    }
}

Poin-poin penting tentang kode

  • SpatialRow, SpatialColumn, SpatialBox, dan SpatialLayoutSpacer adalah semua composable subspace dan harus ditempatkan dalam subspace.
  • Gunakan SubspaceModifier untuk menyesuaikan tata letak.
  • Untuk tata letak dengan beberapa panel dalam satu baris, sebaiknya tetapkan radius kurva 825dp menggunakan SubspaceModifier sehingga panel akan mengelilingi pengguna. Lihat panduan desain kami untuk mengetahui detailnya.

Menggunakan volume untuk menempatkan objek 3D dalam tata letak

Untuk menempatkan objek 3D dalam tata letak, Anda harus menggunakan composable subspace yang disebut volume. Berikut adalah contoh cara melakukannya.

Contoh objek 3D dalam tata letak

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,
                )
            }
        }
    }
}

@Composable
fun ObjectInAVolume(show3DObject: Boolean) {
    val xrCoreSession = checkNotNull(LocalSession.current)
    val scope = rememberCoroutineScope()
    if (show3DObject) {
        Subspace {
            Volume(
                modifier = SubspaceModifier
                    .offset(volumeXOffset, volumeYOffset, volumeZOffset) //
Relative position
                    .scale(1.2f) // Scale to 120% of the size

            ) { parent ->
                scope.launch {
                   // Load your 3D Object here
                }
            }
        }
    }
}

Poin-poin penting tentang kode

Menambahkan komponen UI spasial lainnya

Komponen UI spasial dapat ditempatkan di mana saja dalam hierarki UI aplikasi Anda. Elemen ini dapat digunakan kembali di UI 2D Anda, dan atribut spasialnya hanya akan terlihat saat kemampuan spasial diaktifkan. Hal ini memungkinkan Anda menambahkan elevasi ke menu, dialog, dan komponen lainnya tanpa perlu menulis kode dua kali. Lihat contoh UI spasial berikut untuk lebih memahami cara menggunakan elemen ini.

Komponen UI

Saat spasialisasi diaktifkan

Di lingkungan 2D

SpatialDialog

Panel akan sedikit didorong kembali dalam kedalaman z untuk menampilkan dialog yang ditinggikan

Melakukan fallback ke 2D Dialog.

SpatialPopUp

Panel akan sedikit didorong kembali dalam kedalaman z untuk menampilkan pop-up yang ditinggikan

Melakukan fallback ke PopUp 2D.

SpatialElevation

SpatialElevationLevel dapat disetel untuk menambahkan elevasi.

Menampilkan tanpa elevasi spasial.

SpatialDialog

Ini adalah contoh dialog yang terbuka setelah penundaan singkat. Saat SpatialDialog digunakan, dialog akan muncul pada kedalaman z yang sama dengan panel spasial, dan panel akan didorong kembali sebesar 125dp saat spatialisasi diaktifkan. SpatialDialog juga dapat digunakan jika spatialisasi tidak diaktifkan, dan dalam hal ini SpatialDialog akan kembali ke versi 2D-nya, Dialog.

@Composable
fun DelayedDialog() {
   var showDialog by remember { mutableStateOf(false) }
   LaunchedEffect(Unit) {
       Handler(Looper.getMainLooper()).postDelayed({
           showDialog = true
       }, 3000)
   }
   if (showDialog) {
       SpatialDialog (
           onDismissRequest = { showDialog = false },
           SpatialDialogProperties(
               dismissOnBackPress = true)
       ){
           Box(Modifier
               .height(150.dp)
               .width(150.dp)
           ) {
               Button(onClick = { showDialog = false }) {
                   Text("OK")
               }
           }
       }
   }
}

Poin-poin penting tentang kode

Membuat panel dan tata letak kustom

Untuk membuat panel kustom yang tidak didukung oleh Compose untuk XR, Anda dapat bekerja langsung dengan PanelEntities dan grafik tampilan menggunakan SceneCore API.

Mengaitkan orbiter ke tata letak spasial dan entitas lainnya

Anda dapat mengaitkan orbiter ke entitas apa pun yang dideklarasikan di Compose. Hal ini melibatkan deklarasi orbiter dalam tata letak spasial elemen UI seperti SpatialRow, SpatialColumn, atau SpatialBox. Orbiter akan di-anchor ke entity induk yang terdekat dengan tempat Anda mendeklarasikannya.

Perilaku orbiter ditentukan oleh tempat Anda mendeklarasikannya:

  • Dalam tata letak 2D yang digabungkan dalam SpatialPanel (seperti yang ditunjukkan dalam cuplikan kode sebelumnya), orbiter akan di-anchor ke SpatialPanel tersebut.
  • Dalam Subspace, orbiter akan di-anchor ke entity induk terdekat, yang merupakan tata letak spasial tempat orbiter dideklarasikan.

Contoh berikut menunjukkan cara mengaitkan orbiter ke baris spasial:

Subspace {
    SpatialRow {
        Orbiter(
            position = OrbiterEdge.Top,
            offset = EdgeOffset.inner(8.dp),
            shape = SpatialRoundedCornerShape(size = CornerSize(50))
        ) {
            Text(
                "Hello World!",
                style = MaterialTheme.typography.titleLarge,
                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)
            )
        }
    }
}

Poin-poin penting tentang kode

  • Saat Anda mendeklarasikan orbiter di luar tata letak 2D, orbiter akan di-anchor ke entitas induk terdekatnya. Dalam hal ini, orbiter akan di-anchor ke bagian atas SpatialRow tempatnya dideklarasikan.
  • Tata letak spasial seperti SpatialRow, SpatialColumn, SpatialBox semuanya memiliki entitas tanpa konten yang terkait dengannya. Oleh karena itu, orbiter yang dideklarasikan dalam tata letak spasial akan di-anchor ke tata letak tersebut.

Lihat juga