Android XR 기본사항 알아보기: 1부. 모드 및 공간 패널

1. 시작하기 전에

학습할 내용

  • XR 폼 팩터로 가능해진 고유한 사용자 환경
  • Jetpack Compose XR 라이브러리에서 제공하는 컴포저블을 사용하여 Android XR 헤드셋에서 실행할 때 최대한 활용하도록 앱을 조정하는 방법에 관한 기본사항
  • Compose XR 라이브러리에서 제공하는 UI 요소를 사용하는 방법
  • Android XR용 앱 빌드에 관해 자세히 알아볼 수 있는 곳

이 Codelab의 의도하지 않은 용도

필요한 항목

빌드할 항목

이 Codelab에서는 Android XR을 통해 몰입감 높은 사용자 환경을 제공하도록 기본 단일 화면 앱을 개선합니다.

시작점

최종 결과

2. 설정

코드 가져오기

  1. 이 Codelab의 코드는 xr-codelabs GitHub 저장소의 xr-fundamentals 디렉터리에서 찾을 수 있습니다. 저장소를 클론하려면 다음 명령어를 실행하세요.
git clone https://github.com/android/xr-codelabs.git
  1. 또는 저장소를 ZIP 파일로 다운로드할 수도 있습니다.

프로젝트 열기

  • Android 스튜디오를 시작한 후 xr-fundamentals/start 디렉터리만 선택하여 프로젝트를 가져옵니다. xr-fundamentals/part1 디렉터리에는 솔루션 코드가 포함되어 있습니다. 솔루션 코드는 도움이 필요한 경우 또는 전체 프로젝트를 살펴보고 싶을 때 언제든지 참조할 수 있습니다.

코드 숙지하기

  • Android 스튜디오에서 프로젝트를 열고 시작 코드를 살펴봅니다.

3. XR 개념 알아보기: 모드 및 공간 패널

이 Codelab에서는 두 가지 Android XR 개념인 모드와 공간 패널을 알아봅니다. 이러한 개념을 Android XR 기기에서 실행되는 앱에 적용하는 방법도 알아봅니다.

모드

Android XR 기기에서 앱은 홈 공간 모드 또는 전체 공간 모드라는 두 가지 모드 중 하나로 실행됩니다.

홈 공간 모드

d779257a53898d36.jpeg

홈 공간 모드에서는 여러 앱이 나란히 실행되므로 사용자가 앱 간에 멀티태스킹을 할 수 있습니다. Android 앱은 수정 없이 홈 공간 모드에서 실행할 수 있습니다.

전체 공간 모드

c572cdee69669a23.jpeg

전체 공간 모드에서는 공간의 경계 없이 한 번에 한 앱만 실행됩니다. 다른 앱은 모두 숨겨집니다. 전체 공간 모드로 들어가 이 모드에서 제공되는 추가 기능을 활용하려면 앱에서 추가 작업을 실행해야 합니다.

이러한 모드에 관한 자세한 내용은 ​​홈 공간 모드와 전체 공간 모드를 참고하세요.

공간 패널

공간 패널은 Android XR 앱의 기본 구성요소 역할을 하는 컨테이너 요소입니다.

홈 공간 모드에서 실행될 때 앱은 대형 화면 Android 기기의 데스크톱 창 모드와 유사한 환경을 제공하기 위해 단일 패널 내에 포함됩니다.

전체 공간 모드에서 실행될 때는 앱의 콘텐츠를 하나 이상의 패널로 나눠 좀 더 몰입감 높은 환경을 제공할 수 있습니다.

패널에 관한 자세한 내용은 공간 패널을 참고하세요.

4. Android XR 에뮬레이터에서 앱 실행

Android XR용 앱 개선을 시작하기 전에 Android XR 에뮬레이터에서 앱을 실행하여 앱이 홈 공간 모드에서 어떻게 표시되는지 확인할 수 있습니다.

Android XR 시스템 이미지 설치

  1. 먼저 Android 스튜디오에서 SDK Manager를 열고, 아직 선택하지 않았다면 SDK Platforms 탭을 선택합니다. SDK Manager 창의 오른쪽 하단에서 Show package details 체크박스가 선택되어 있는지 확인합니다.
  2. Android 14 섹션에서 Android XR ARM 64 v8a 또는 Android XR Intel x86_64 에뮬레이터 이미지를 설치합니다. 이미지는 자신과 동일한 아키텍처(x86/ARM)를 사용하는 컴퓨터에서만 실행됩니다.

Android XR 가상 기기 만들기

  1. 기기 관리도구를 열고 창 왼쪽의 Category 열에서 XR을 선택합니다. 그런 다음 목록에서 XR Device 하드웨어 프로필을 선택하고 Next를 클릭합니다.

7a5f6b9c1766d837.png

  1. 다음 페이지에서 이전에 설치한 시스템 이미지를 선택합니다. Next를 클릭하고 원하는 고급 옵션을 선택한 다음 Finish를 클릭하여 AVD를 만듭니다.
  2. 방금 만든 AVD에서 앱을 실행합니다.

7cf6569ef7967d87.png

5. 종속 항목 설정

앱에 XR 관련 기능을 추가하려면 먼저 Android XR에 최적화된 앱 환경을 빌드하는 데 필요한 모든 컴포저블이 포함되어 있는 XR용 Jetpack Compose 라이브러리(androidx.xr.compose:compose)의 종속 항목을 추가해야 합니다.

libs.version.toml

[versions]
...
xrCompose = "1.0.0-alpha01"

[libraries]
...
androidx-xr-compose = { group = "androidx.xr.compose", name = "compose", version.ref = "xrCompose" }

build.gradle.kts(모듈 :app)

dependencies {
    ...
    implementation(libs.androidx.xr.compose)
    ...
}

이러한 파일을 업데이트한 후에는 종속 항목이 프로젝트에 다운로드되도록 Gradle 동기화를 실행해야 합니다.

6. 전체 공간 모드 시작

패널과 같은 XR 기능을 사용하려면 앱이 전체 공간 모드에서 실행되어야 합니다. 앱이 전체 공간 모드를 시작하는 방법은 두 가지가 있습니다.

  • 프로그래매틱 방식으로 시작(예: 앱 내에서 사용자 상호작용에 대한 응답)
  • 앱 매니페스트에 지시어를 추가하여 실행 시 즉시 시작

프로그래매틱 방식으로 전체 공간 모드 시작

프로그래매틱 방식으로 전체 공간 모드를 시작하려면 UI에서 어포던스를 제공하여 사용자가 앱을 사용할 모드를 제어하도록 할 수 있습니다. 또한 앱이 사용되는 방식의 컨텍스트 내에서 적절한 경우에도 전체 공간 모드를 시작할 수 있습니다. 예를 들어 동영상 콘텐츠를 보기 시작할 때 전체 공간 모드를 시작하고 재생이 완료될 때 종료합니다.

단순하게 이 작업은 먼저 상단 앱 바에 모드를 전환하는 버튼을 추가하면 됩니다.

  1. com.example.android.xrfundamentals.ui.component 패키지에 새 파일 ToggleSpaceModeButton.kt를 만들고 다음 컴포저블을 추가합니다.

ToggleSpaceModeButton.kt

package com.example.android.xrfundamentals.ui.component

import androidx.annotation.DrawableRes
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.xr.compose.platform.LocalSpatialCapabilities
import androidx.xr.compose.platform.LocalSpatialConfiguration
import com.example.android.xrfundamentals.R
import com.example.android.xrfundamentals.ui.theme.XRFundamentalsTheme

@Composable
fun ToggleSpaceModeButton(modifier: Modifier = Modifier) {
    val spatialConfiguration = LocalSpatialConfiguration.current

    if (LocalSpatialCapabilities.current.isSpatialUiEnabled) {
        ToggleSpaceModeButton(
            modifier = modifier,
            contentDescription = "Request Home Space mode",
            iconResource = R.drawable.ic_home_space_mode,
            onClick = { spatialConfiguration.requestHomeSpaceMode() }
        )
    } else {
        ToggleSpaceModeButton(
            modifier = modifier,
            contentDescription = "Request Full Space mode",
            iconResource = R.drawable.ic_full_space_mode,
            onClick = { spatialConfiguration.requestFullSpaceMode() }
        )
    }
}

@Composable
fun ToggleSpaceModeButton(
    contentDescription: String,
    @DrawableRes iconResource: Int,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    IconButton(
        modifier = modifier,
        onClick = onClick
    ) {
        Icon(
            painterResource(iconResource),
            contentDescription
        )
    }
}
  1. 앱이 XR 기기에서 실행될 때 TopAppBar에 버튼을 작업으로 추가합니다.

XRFundamentalsTopAppBar.kt

import androidx.xr.compose.platform.LocalHasXrSpatialFeature

...

TopAppBar(
    ...,
    actions = {
        // Only show the mode toggle if the device supports spatial UI
        if (LocalHasXrSpatialFeature.current) {
            ToggleSpaceModeButton()
        }
    }
)

앱을 실행합니다.

실행 시 앱이 홈 공간 모드로 실행됩니다. 패널 오른쪽 상단의 버튼을 탭하면 전체 공간 모드로 전환됩니다.

앱이 전체 공간 모드로 실행됩니다. 앱을 최소화하거나 닫는 시스템 UI가 없습니다. 패널 오른쪽 상단의 버튼을 탭하면 홈 공간 모드로 다시 전환됩니다.

다음 스니펫에는 주목할 만한 새로운 API가 포함되어 있습니다.

  • LocalSpatialConfiguration은 앱의 현재 공간 구성에 대한 액세스를 제공하는 컴포지션 로컬입니다. 모드 변경을 요청하는 메서드 외에도 앱이 포함된 볼륨 크기 등의 기타 정보가 포함되어 있습니다.
  • LocalSpatialCapabilities는 앱에서 현재 사용할 수 있는 공간 기능을 확인하는 데 사용할 수 있는 컴포지션 로컬입니다. 모드(홈 또는 전체 공간) 외에도 공간 음향, 3D 콘텐츠 지원 등의 기능이 포함되어 있습니다.
  • LocalHasXrSpatialFeature는 공간 UI 기능을 지원하는 기기에서 앱이 실행되는지 확인하는 데 사용할 수 있는 컴포지션 로컬입니다. 내부적으로 기기에 android.software.xr.immersive 시스템 기능이 있는지 확인합니다.

실행 시 전체 공간 모드 시작

전체 공간 모드에서 활동을 시작하라고 OS에 지시하려면 상응하는 <activity> 요소 내에 다음 속성이 있는 <property> 요소를 포함하면 됩니다. 이는 사용자가 내 앱을 사용하는 동시에 다른 앱을 사용할 가능성이 없는 경우에만 권장됩니다.

AndroidManifest.xml

<activity
    android:name=".MainActivity" 
    ... >
    <property
        android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
        android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED" />
</activity>

이제 앱이 실행되면 사용자는 즉시 전체 공간 모드로 전환됩니다.

abbf3d27cd2a4532.gif

계속 진행하기 전에 매니페스트에서 앞서 언급한 <property> 요소를 삭제하여 앱이 홈 공간 모드에서 열리는 기본 동작을 사용하도록 합니다.

7. 여러 패널로 UI 분할

이제 앱이 전체 공간 모드를 시작 및 종료할 수 있으므로 이를 더 효과적으로 사용해 보겠습니다. 이를 위한 좋은 방법은 앱의 콘텐츠를 여러 패널로 분할하여 공간을 채우고 원하는 경우 사용자가 적절하게 이러한 패널을 이동하고 크기를 조절하도록 하는 것입니다.

하위 공간에 앱 삽입

시작하려면 XRFundamentalsApp 컴포저블에서 Scaffold 컴포저블 뒤에 Subspace 컴포저블을 추가합니다. 하위 공간은 앱 내의 3D 공간 파티션으로, 여기에서 3D 레이아웃(예: 공간 패널 추가)을 빌드하고 3D 모델을 배치하고 2D 콘텐츠에 깊이를 더할 수 있습니다.

XR 기기가 아닌 기기에서 실행될 때는 Subspace 컴포저블의 콘텐츠가 컴포지션에 들어가지 않습니다. XR 기기에서 실행될 때는 앱이 전체 공간 모드에서 실행되는 때만 콘텐츠가 컴포지션에 들어갑니다.

XRFundamentalsApp.kt

import androidx.xr.compose.spatial.Subspace

...

HelloAndroidXRTheme {
    Scaffold(...)
    Subspace {
    }
}

이제 앱을 실행합니다.

2d47561a616f4a11.gif

앱에 Subspace 컴포저블이 포함되어 있으면 2D 콘텐츠 대신 표시됩니다. 즉, 버튼을 클릭하여 전체 공간 모드로 전환하면 더 이상 아무것도 표시되지 않습니다. 이 문제를 수정하기 위해 다음 몇 단계에서 공간 패널을 두 개 추가합니다. 하나는 기본 콘텐츠를 포함하고 다른 하나는 보조 콘텐츠를 포함합니다.

기본 콘텐츠 패널 추가

전체 공간 모드에서 기본 콘텐츠를 표시하려면 Subspace 컴포저블 내에 SpatialPanel을 추가하세요.

이는 앱의 기본 패널이므로 내부에 Scaffold를 포함하여 상단 앱 바 내의 컨트롤이 계속 표시되도록 할 수 있습니다. 다음 Codelab에서는 탐색 및 컨텍스트별 작업 등 일반적으로 앱 바에 포함된 컨트롤을 공간화하는 데 사용할 수 있는 orbiter를 알아봅니다.

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.SpatialPanel

...

Subspace {
    SpatialPanel() {
        Scaffold(
            topBar = { XRFundamentalsTopAppBar() }
        ) { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                PrimaryCard(
                    modifier = Modifier
                        .padding(16.dp)
                        .verticalScroll(rememberScrollState())
                )
            }
        }
    }
}

다시 앱을 실행하면 기본 콘텐츠가 있는 SpatialPanel이 전체 공간 모드에서 표시되지만 크기가 매우 작습니다.

89152c1991d422d4.gif

기본 패널 수정

기본 패널을 좀 더 유용하게 만들려면 SubspaceModifier를 제공하여 크게 만들면 됩니다. 하위 공간 수정자는 수정자와 비슷하며 패널과 같은 공간 구성요소를 수정하는 데 사용합니다.

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.layout.SubspaceModifier
import androidx.xr.compose.subspace.layout.height
import androidx.xr.compose.subspace.layout.width
import androidx.compose.ui.unit.dp

...

SpatialPanel(
    modifier = SubspaceModifier
        .width(1024.dp)
        .height(800.dp)
){
    ...
}

다시 앱을 실행하면 기본 패널이 훨씬 더 많은 공간을 차지합니다.

c4f28838e16a3eb8.gif

보조 콘텐츠 패널 추가

이제 앱이 전체 공간 모드로 실행되고 패널을 사용하여 기본 콘텐츠를 표시하므로 보조 콘텐츠를 자체 패널로 이동해 보겠습니다. 공간 패널 내에서 Surface를 사용해야 합니다. Surface가 없으면 보조 카드의 배경이 없습니다. 공간 패널 자체가 투명하기 때문입니다(Scaffold 컴포저블이 이전 단계에서 이를 처리함).

XRFundamentalsApp.kt

Subspace {
    SpatialPanel() { ... }
    SpatialPanel(
        modifier = SubspaceModifier
            .width(340.dp)
            .height(800.dp)
    ) {
        Surface {
            SecondaryCardList(
                modifier = Modifier
                    .padding(16.dp)
                    .verticalScroll(rememberScrollState())
            )
        }
    }
}

이제 앱을 다시 실행합니다. 얼핏 보면 두 번째 패널이 표시되지 않는 것 같을 수 있지만 실제로는 표시됩니다. 기본 패널 뒤에 숨겨져 있을 뿐입니다.

7db3c3428b64e482.gif

패널을 나란히 배치

2D 콘텐츠와 마찬가지로 행과 열을 사용하면 겹치지 않고 컴포저블을 나란히 정렬하는 데 도움이 됩니다. 패널과 같은 공간 구성요소를 사용할 때는 SpatialRowSpatialColumn 컴포저블을 사용하면 됩니다.

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.SpatialRow

...

Subspace {
    SpatialRow(
        curveRadius = 825.dp
    ) {
        SpatialPanel(...) { ... }
        SpatialPanel(...) { ... }
    }
}

다시 앱을 실행하면 패널이 나란히 배치되어 있습니다. 또한 SpatialRow에 제공된 curveRadius로 인해 패널이 같은 평면에 유지되지 않고 사용자를 중심으로 휘어져 좀 더 몰입도 높은 환경을 제공합니다.

7455811775088baf.gif

패널의 크기를 조절할 수 있도록 설정

앱의 모양을 사용자가 제어할 수 있도록 하려면 resizable 하위 공간 수정자를 사용하여 패널의 크기를 조절할 수 있도록 하면 됩니다.

기본적으로 크기를 조절할 수 있는 패널은 0까지 축소하거나 무한으로 확대할 수 있으므로 포함하게 될 콘텐츠에 따라 적절한 minimumSizemaximumSize 매개변수를 신중하게 설정하는 것이 좋습니다.

resizable 수정자가 지원하는 모든 매개변수에 관한 자세한 내용은 참고 문서를 확인하세요.

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.layout.resizable

...

SpatialPanel(
    modifier = SubspaceModifier
        ...
        .resizable(true)
)

2ff2db33032fd251.gif

패널을 이동할 수 있도록 설정

마찬가지로 movable 하위 공간 수정자를 사용하여 패널을 이동할 수 있도록 설정할 수 있습니다.

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.layout.movable

...

SpatialPanel(
    modifier = SubspaceModifier
        ...
        .movable(true)
)

12b6166645ea1be.gif

movable 수정자가 지원하는 모든 매개변수에 관한 자세한 내용은 참고 문서를 확인하세요.

8. 축하합니다

XR을 최대한 활용하는 방법에 관해 계속 알아보려면 다음 리소스와 연습을 확인하세요. 또한 XR 부트캠프 참여를 신청해 보세요.

추가 자료

도전과제

  • resizablemovable 하위 공간 수정자에 사용 가능한 추가 매개변수를 사용합니다.
  • 패널을 더 추가합니다.
  • 공간 대화상자 등 다른 공간 구성요소를 사용합니다.

참조 문서