XR용 Jetpack Compose로 UI 개발

XR용 Jetpack Compose를 사용하면 행 및 열과 같은 익숙한 Compose 개념을 사용하여 공간 UI 및 레이아웃을 선언적으로 빌드할 수 있습니다. 이를 통해 기존 Android UI를 3D 공간으로 확장하거나 완전히 새로운 몰입형 3D 애플리케이션을 빌드할 수 있습니다.

기존 Android 뷰 기반 앱을 공간화하는 경우 몇 가지 개발 옵션이 있습니다. 상호 운용성 API를 사용하거나 Compose와 뷰를 함께 사용하거나 SceneCore 라이브러리를 직접 사용할 수 있습니다. 자세한 내용은 뷰 작업 가이드를 참고하세요.

하위 공간 및 공간화된 구성요소 정보

Android XR용 앱을 작성할 때는 하위 공간공간화된 구성요소의 개념을 이해하는 것이 중요합니다.

하위 스페이스 정보

Android XR용으로 개발할 때는 앱 또는 레이아웃에 하위 공간을 추가해야 합니다. 하위 스페이스는 앱 내 3D 공간의 파티션으로, 여기에 3D 콘텐츠를 배치하고, 3D 레이아웃을 빌드하고, 2D 콘텐츠에 깊이를 추가할 수 있습니다. 하위 공간은 공간화가 사용 설정된 경우에만 렌더링됩니다. Home Space 또는 XR이 아닌 기기에서는 해당 하위 공간 내의 모든 코드가 무시됩니다.

하위 공간을 만드는 방법에는 두 가지가 있습니다.

  • setSubspaceContent: 이 함수는 앱 수준 하위 공간을 만듭니다. setContent를 사용하는 것과 동일한 방식으로 MainActivity에서 호출할 수 있습니다. 앱 수준 하위 공간은 높이, 너비, 깊이가 제한되지 않으므로 본질적으로 공간 콘텐츠에 무한한 캔버스를 제공합니다.
  • Subspace: 이 컴포저블은 앱의 UI 계층 구조 내 어디에나 배치할 수 있으므로 파일 간에 컨텍스트를 잃지 않고 2D 및 공간 UI의 레이아웃을 유지할 수 있습니다. 이렇게 하면 전체 UI 트리를 통해 상태를 호이스팅하거나 앱을 재구성하지 않고도 XR과 다른 폼 팩터 간에 기존 앱 아키텍처와 같은 항목을 더 쉽게 공유할 수 있습니다.

자세한 내용은 앱에 하위 공간 추가를 참고하세요.

공간화된 구성요소 정보

하위 공간 컴포저블: 이러한 구성요소는 하위 공간에서만 렌더링할 수 있습니다. 2D 레이아웃 내에 배치하기 전에 Subspace 또는 setSubspaceContent 내에 묶어야 합니다. SubspaceModifier를 사용하면 깊이, 오프셋, 위치 지정과 같은 속성을 하위 공간 컴포저블에 추가할 수 있습니다.

  • 하위 공간 수정자에 관한 참고사항: SubspaceModifier API의 순서에 각별히 주의하세요.
    • 오프셋은 수정자 체인에서 먼저 발생해야 함
    • Movable 및 resizable은 마지막에 실행되어야 합니다.
    • 크기 조정 전에 회전을 적용해야 합니다.

다른 공간화된 구성요소는 하위 스페이스 내에서 호출할 필요가 없습니다. 공간 컨테이너 내에 래핑된 기존 2D 요소로 구성됩니다. 이러한 요소는 둘 다에 대해 정의된 경우 2D 또는 3D 레이아웃 내에서 사용할 수 있습니다. 공간화가 사용 설정되지 않으면 공간화된 지형지물이 무시되고 2D 대응 항목으로 대체됩니다.

공간 패널 만들기

SpatialPanel는 앱 콘텐츠를 표시할 수 있는 하위 공간 컴포저블입니다. 예를 들어 동영상 재생, 스틸 이미지 또는 기타 콘텐츠를 공간 패널에 표시할 수 있습니다.

공간 UI 패널의 예

다음 예와 같이 SubspaceModifier를 사용하여 공간 패널의 크기, 동작, 위치를 변경할 수 있습니다.

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

코드 관련 핵심 사항

  • 하위 공간 수정자 관련 참고사항: SubspaceModifier API의 순서에 각별히 주의하세요.
    • 오프셋은 수정자 체인에서 먼저 발생해야 합니다.
    • 이동 가능하고 크기 조절 가능한 수정자는 마지막에 나와야 합니다.
    • 크기 조정 전에 회전을 적용해야 합니다.
  • SpatialPanel API는 하위 공간 컴포저블이므로 Subspace 또는 setSubspaceContent 내에서 호출해야 합니다. 하위 공간 외부에서 호출하면 예외가 발생합니다.
  • 사용자가 .movable 또는 .resizable SubspaceModifier를 추가하여 패널의 크기를 조절하거나 이동할 수 있도록 허용합니다.
  • 크기 및 위치에 관한 자세한 내용은 공간 패널 디자인 가이드를 참고하세요. 코드 구현에 관한 자세한 내용은 참조 문서를 참고하세요.

궤도선 만들기

오리터는 공간 UI 구성요소입니다. 상응하는 공간 패널에 연결되도록 설계되었으며 해당 공간 패널과 관련된 탐색 및 문맥 작업 항목을 포함합니다. 예를 들어 동영상 콘텐츠를 표시하기 위해 공간 패널을 만든 경우 오리터 내부에 동영상 재생 컨트롤을 추가할 수 있습니다.

궤도선의 예

다음 예와 같이 SpatialPanel 내에서 궤도 도구를 호출하여 탐색과 같은 사용자 컨트롤을 래핑합니다. 이렇게 하면 2D 레이아웃에서 객체가 추출되고 구성에 따라 공간 패널에 연결됩니다.

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

코드 관련 핵심 사항

  • 하위 공간 수정자 관련 참고: SubspaceModifier API의 순서에 각별히 주의하세요.
    • 오프셋은 수정자 체인에서 먼저 발생해야 함
    • Movable 및 resizable은 마지막에 실행되어야 합니다.
    • 크기 조정 전에 회전을 적용해야 합니다.
  • 궤도 소멸자는 공간 UI 구성요소이므로 코드를 2D 또는 3D 레이아웃에서 재사용할 수 있습니다. 2D 레이아웃에서 앱은 궤도선 내부의 콘텐츠만 렌더링하고 궤도선 자체는 무시합니다.
  • 궤도 에어캡을 사용하고 디자인하는 방법에 관한 자세한 내용은 디자인 가이드를 참고하세요.

공간 레이아웃에 여러 개의 공간 패널 추가

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는 모두 하위 공간 컴포저블이며 하위 공간 내에 배치해야 합니다.
  • SubspaceModifier를 사용하여 레이아웃을 맞춤설정합니다.
  • 한 줄에 여러 패널이 있는 레이아웃의 경우 패널이 사용자를 둘러싸도록 SubspaceModifier를 사용하여 곡선 반경을 825dp로 설정하는 것이 좋습니다. 자세한 내용은 디자인 가이드를 참고하세요.

볼륨을 사용하여 레이아웃에 3D 객체 배치

레이아웃에 3D 객체를 배치하려면 볼륨이라는 하위 공간 컴포저블을 사용해야 합니다. 다음은 이 작업을 실행하는 방법을 보여주는 예입니다.

레이아웃의 3D 객체 예

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

코드 관련 핵심 사항

  • 하위 공간 수정자 관련 참고: SubspaceModifier API의 순서에 각별히 주의하세요.
    • 오프셋은 수정자 체인에서 먼저 발생해야 함
    • Movable 및 resizable은 마지막에 실행되어야 합니다.
    • 크기 조정 전에 회전을 적용해야 합니다.
  • 볼륨 내에 3D 콘텐츠를 로드하는 방법을 자세히 알아보려면 3D 콘텐츠 추가를 참고하세요.

다른 공간 UI 구성요소 추가

공간 UI 구성요소는 애플리케이션의 UI 계층 구조 어디에나 배치할 수 있습니다. 이러한 요소는 2D UI에서 재사용할 수 있으며 공간 기능이 사용 설정된 경우에만 공간 속성이 표시됩니다. 이렇게 하면 코드를 두 번 작성하지 않고도 메뉴, 대화상자, 기타 구성요소에 고도를 추가할 수 있습니다. 다음의 공간 UI 예시를 통해 이러한 요소를 사용하는 방법을 자세히 알아보세요.

UI 구성요소

공간화 기능이 사용 설정된 경우

2D 환경에서

SpatialDialog

패널이 z-depth에서 약간 뒤로 밀려 올라간 대화상자를 표시합니다.

2D Dialog로 대체됩니다.

SpatialPopUp

패널이 z-depth에서 약간 뒤로 밀려 올라간 팝업을 표시합니다.

2D PopUp로 대체됩니다.

SpatialElevation

SpatialElevationLevel를 설정하여 고도를 추가할 수 있습니다.

공간적 고도가 없는 쇼입니다.

SpatialDialog

다음은 잠시 지연된 후 열리는 대화상자의 예입니다. SpatialDialog를 사용하면 대화상자가 공간 패널과 동일한 z-depth에 표시되고 공간화가 사용 설정되면 패널이 125dp 뒤로 밀립니다. SpatialDialog는 공간화가 사용 설정되지 않은 경우에도 계속 사용할 수 있으며 2D 대응 항목인 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")
               }
           }
       }
   }
}

코드 관련 핵심 사항

맞춤 패널 및 레이아웃 만들기

XR용 Compose에서 지원되지 않는 맞춤 패널을 만들려면 SceneCore API를 사용하여 PanelEntities 및 장면 그래프를 직접 사용할 수 있습니다.

참고 항목