스플래시 화면

Android 12부터 SplashScreen API를 사용하면 실행 시 인앱 모션, 앱 아이콘을 보여주는 스플래시 화면, 앱으로의 전환 등 애니메이션으로 앱을 실행할 수 있습니다. SplashScreenWindow이므로 Activity를 포함합니다.

그림 1. 스플래시 화면입니다.

스플래시 화면 환경에서는 모든 앱을 실행할 때 표준 디자인 요소를 사용할 수 있지만 맞춤설정도 가능하므로 앱의 고유한 브랜딩을 유지할 수 있습니다.

SplashScreen 플랫폼 API를 사용하는 것 외에 SplashScreen API를 래핑하는 SplashScreen compat 라이브러리를 사용할 수도 있습니다.

스플래시 화면 작동 방식

앱 프로세스가 실행되고 있지 않거나 (콜드 스타트) Activity가 생성되지 않은 경우 (웜 스타트) 사용자가 앱을 실행하면 다음과 같은 이벤트가 발생합니다.

  1. 시스템은 개발자가 정의한 테마 및 애니메이션을 사용하여 스플래시 화면을 표시합니다.

  2. 앱이 준비되면 스플래시 화면이 닫히고 앱이 표시됩니다.

핫 스타트 중에는 스플래시 화면이 표시되지 않습니다.

스플래시 화면의 요소와 메커니즘

스플래시 화면의 요소는 Android 매니페스트 파일의 XML 리소스 파일로 정의됩니다. 각 요소에는 밝은 모드와 어두운 모드 버전이 있습니다.

스플래시 화면의 맞춤설정 가능한 요소는 앱 아이콘, 아이콘 배경, 창 배경으로 구성됩니다.

스플래시 화면에 포함된 요소를 보여주는 이미지
그림 2. 맞춤설정 가능한 스플래시 화면 요소

그림 2에 표시된 다음 요소를 고려하세요.

1 앱 아이콘은 벡터 드로어블이어야 합니다. 정적 또는 애니메이션일 수 있습니다. 애니메이션의 지속 시간은 무제한일 수 있지만 1,000밀리초를 초과하지 않는 것이 좋습니다. 런처 아이콘이 기본값입니다.

2 아이콘 배경은 선택사항이며 아이콘과 창 배경 사이에 대비가 더 필요한 경우에 유용합니다. 적응형 아이콘을 사용하는 경우 창 배경과 충분히 대비되면 배경이 표시됩니다.

3 적응형 아이콘과 마찬가지로 포그라운드의 1/3이 마스킹됩니다.

4 창 배경은 단일 불투명 색상으로 구성됩니다. 창 배경이 설정되어 있고 단색인 경우 속성이 설정되지 않은 경우 기본적으로 사용됩니다.

스플래시 화면 크기

스플래시 화면 아이콘은 다음과 같이 적응형 아이콘과 동일한 사양을 사용합니다.

  • 브랜드 이미지: 200×80dp여야 합니다.
  • 아이콘 배경이 있는 앱 아이콘: 240×240dp이고 직경이 160dp인 원 안에 들어야 합니다.
  • 아이콘 배경이 없는 앱 아이콘: 288×288dp이고 지름이 192dp인 원 안에 들어야 합니다.

예를 들어 이미지의 전체 크기가 300×300dp인 경우 아이콘은 지름이 200dp인 원 안에 들어가야 합니다. 원 밖의 모든 것은 보이지 않게 됩니다 (마스크됨).

단색 및 투명한 배경의 다양한 아이콘 크기를 보여주는 이미지
그림 3. 단색 및 투명한 배경의 스플래시 화면 아이콘 크기

스플래시 화면 애니메이션 및 시작 시퀀스

추가 지연 시간은 보통 콜드 스타트로 앱을 실행할 때 발생합니다. 스플래시 화면에 애니메이션 아이콘을 추가하면 시각적으로 눈에 띄게 매력적이며 더 프리미엄 환경을 제공합니다. 사용자 연구에 따르면 애니메이션을 볼 때 인지되는 시작 시간이 더 짧습니다.

그림 4와 같이 스플래시 화면 애니메이션이 시작 시퀀스 구성요소 내에 삽입됩니다.

12개의 연속된 프레임에서 시작 시퀀스를 보여주는 이미지(런처 아이콘을 탭한 후 확대됨에 따라 화면을 채우는 것부터 시작)
그림 4. 실행 시퀀스
  1. 애니메이션 시작: 스플래시 화면의 시스템 뷰로 구성됩니다. 이는 시스템에서 제어하며 맞춤설정할 수 없습니다.

  2. 스플래시 화면 (시퀀스의 '대기' 부분에 표시됨): 스플래시 화면을 맞춤설정할 수 있으므로 자체 로고 애니메이션과 브랜딩을 제공할 수 있습니다. 제대로 작동하려면 이 페이지에 설명된 요구사항을 충족해야 합니다.

  3. 종료 애니메이션: 스플래시 화면을 숨기는 애니메이션으로 구성됩니다. 맞춤설정하려면 SplashScreenView 및 아이콘을 사용합니다. 변환, 불투명도, 색상 설정을 사용하여 모든 애니메이션을 실행할 수 있습니다. 이 경우 애니메이션이 완료될 때 스플래시 화면을 수동으로 삭제합니다.

아이콘 애니메이션을 실행할 때 앱 시작 시 앱이 더 일찍 준비된 경우 시퀀스를 건너뛸 수 있는 옵션이 제공됩니다. 앱이 onResume()를 트리거하거나 스플래시 화면이 자동으로 타임아웃되므로 모션을 편안하게 건너뛸 수 있어야 합니다. 스플래시 화면은 앱이 시각적 관점에서 안정적일 때만 onResume()를 사용하여 닫아야 하므로 추가 스피너가 필요하지 않습니다. 불완전한 인터페이스를 도입하면 사용자가 부자연스럽게 느껴질 수 있으며 예측하기 어렵거나 세련되지 않은 듯한 인상을 줄 수 있습니다.

스플래시 화면 애니메이션 요구사항

스플래시 화면은 다음 사양을 준수해야 합니다.

  • 단일 창 배경 색상을 투명하지 않게 설정합니다. 주간 및 야간 모드는 SplashScreen compat 라이브러리로 지원됩니다.

  • 애니메이션 아이콘이 다음 사양을 충족하는지 확인하세요.

    • 형식: 아이콘은 AnimationVectorDrawable (AVD) XML이어야 합니다.
    • 크기: AVD 아이콘은 다음과 같이 적응형 아이콘 크기의 네 배여야 합니다.
      • 아이콘 영역은 432dp, 즉 마스킹되지 않은 적응형 아이콘의 108dp 영역의 4배여야 합니다.
      • 이미지의 안쪽 3분의 2는 런처 아이콘에 표시되며 288dp여야 합니다. 즉, 적응형 아이콘의 내부 마스크 영역을 구성하는 72dp의 4배입니다.
    • 시간: 휴대전화에서는 1,000ms를 초과하지 않는 것이 좋습니다. 지연된 시작을 사용할 수 있지만 166ms를 초과할 수 없습니다. 앱 시작 시간이 1,000ms보다 길면 연속 재생 애니메이션을 사용하는 것이 좋습니다.
  • 스플래시 화면을 닫을 적절한 시간을 설정합니다. 이 시간은 앱이 첫 번째 프레임을 그릴 때 발생합니다. 스플래시 화면을 화면에 더 오래 표시하는 방법에 관한 섹션에 설명된 대로 이를 추가로 맞춤설정할 수 있습니다.

스플래시 화면 리소스

그림 5. AVD 예

애니메이션을 만들고, 형식을 지정하고, AVD로 내보내는 방법을 보여주는 스타터 키트 예를 다운로드합니다. 이 패키지에는 다음 도구가 포함되어 있습니다.

  • 애니메이션의 Adobe After Effects 프로젝트 파일입니다.
  • 마지막으로 내보낸 AVD XML 파일입니다.
  • 애니메이션 GIF 예시

이 파일을 다운로드하면 Google 서비스 약관에 동의하는 것으로 간주됩니다.

Google 개인정보처리방침에 이 서비스의 데이터 처리 방식이 설명되어 있습니다.

앱의 스플래시 화면 맞춤설정

windowBackground가 단색이면 기본적으로 SplashScreen는 테마의 windowBackground를 사용합니다. 스플래시 화면을 맞춤설정하려면 앱 테마에 속성을 추가하세요.

다음 중 한 가지 방법으로 앱의 스플래시 화면을 맞춤설정할 수 있습니다.

  • 테마 속성을 설정하여 모양을 변경합니다.

  • 화면에 더 오랫동안 표시합니다.

  • 스플래시 화면 닫기 애니메이션을 맞춤설정합니다.

시작하기

핵심 SplashScreen 라이브러리는 API 23부터 모든 기기에 Android 12 스플래시 화면을 제공합니다. 이를 프로젝트에 추가하려면 build.gradle 파일에 다음 스니펫을 추가합니다.

Groovy

dependencies {
    implementation "androidx.core:core-splashscreen:1.0.0"
}

Kotlin

dependencies {
    implementation("androidx.core:core-splashscreen:1.0.0")
}

스플래시 화면 테마를 설정하여 모양 변경

Activity 테마에 다음 속성을 지정하여 앱의 스플래시 화면을 맞춤설정할 수 있습니다. android:windowBackground와 같은 속성을 사용하는 기존 스플래시 화면 구현이 이미 있다면 Android 12 이상용 대체 리소스 파일을 제공하는 것이 좋습니다.

  1. windowSplashScreenBackground를 사용하여 배경을 특정 단색으로 채웁니다.

    <item name="android:windowSplashScreenBackground">@color/...</item>
    
  2. windowSplashScreenAnimatedIcon를 사용하여 시작 창 중앙의 아이콘을 바꿉니다.

    Android 12 (API 수준 32)만 타겟팅하는 앱의 경우 다음을 실행합니다.

    객체가 AnimationDrawableAnimatedVectorDrawable를 통해 애니메이션 가능하고 드로어블이면 시작 창을 표시하는 동안 애니메이션을 재생하도록 windowSplashScreenAnimationDuration를 설정합니다. Android 13의 경우 기간이 AnimatedVectorDrawable에서 직접 추론되므로 필요하지 않습니다.

    <item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
    
  3. windowSplashScreenAnimationDuration를 사용하여 스플래시 화면 아이콘 애니메이션의 지속 시간을 나타냅니다. 이를 설정해도 스플래시 화면이 표시되는 실제 시간에는 영향을 미치지 않지만 SplashScreenView.getIconAnimationDuration를 사용하여 스플래시 화면 종료 애니메이션을 맞춤설정할 때 검색할 수 있습니다. 자세한 내용은 스플래시 화면을 화면에 더 오래 표시 섹션을 참고하세요.

    <item name="android:windowSplashScreenAnimationDuration">1000</item>
    
  4. windowSplashScreenIconBackgroundColor를 사용하여 스플래시 화면 아이콘 뒤에 배경을 설정합니다. 창 배경과 아이콘 사이의 대비가 충분하지 않은 경우에 유용합니다.

    <item name="android:windowSplashScreenIconBackgroundColor">@color/...</item>
    
  5. windowSplashScreenBrandingImage를 사용하여 스플래시 화면 하단에 표시할 이미지를 설정할 수 있습니다. 그러나 디자인 가이드라인에서는 브랜드 이미지를 사용하지 않는 것이 좋습니다.

    <item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
    
  6. windowSplashScreenBehavior를 사용하여 Android 13 이상에서 앱이 항상 스플래시 화면에 아이콘을 표시할지 지정할 수 있습니다. 기본값은 0으로, 시작 활동이 splashScreenStyleSPLASH_SCREEN_STYLE_ICON로 설정하면 스플래시 화면에 아이콘을 표시하고, 시작 활동이 스타일을 지정하지 않으면 시스템 동작을 따릅니다. 빈 스플래시 화면을 표시하지 않고 항상 애니메이션 아이콘을 표시하려면 icon_preferred 값으로 설정하세요.

    <item name="android:windowSplashScreenBehavior">icon_preferred</item>
    

스플래시 화면을 화면에 더 오래 표시

스플래시 화면은 앱이 첫 프레임을 그리는 즉시 닫힙니다. 로컬 디스크에서 인앱 설정을 비동기식으로 로드하는 등 소량의 데이터를 로드해야 한다면 ViewTreeObserver.OnPreDrawListener를 사용하여 앱을 정지하고 첫 번째 프레임을 그릴 수 있습니다.

시작 활동이 그리기 전에 종료되는 경우(예: 콘텐츠 뷰를 설정하지 않고 onResume 전에 종료) 미리 그리기 리스너가 필요하지 않습니다.

Kotlin

// Create a new event for the activity.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Set the layout for the content view.
    setContentView(R.layout.main_activity)

    // Set up an OnPreDrawListener to the root view.
    val content: View = findViewById(android.R.id.content)
    content.viewTreeObserver.addOnPreDrawListener(
        object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                // Check whether the initial data is ready.
                return if (viewModel.isReady) {
                    // The content is ready. Start drawing.
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                } else {
                    // The content isn't ready. Suspend.
                    false
                }
            }
        }
    )
}

Java

// Create a new event for the activity.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set the layout for the content view.
    setContentView(R.layout.main_activity);

    // Set up an OnPreDrawListener to the root view.
    final View content = findViewById(android.R.id.content);
    content.getViewTreeObserver().addOnPreDrawListener(
            new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    // Check whether the initial data is ready.
                    if (mViewModel.isReady()) {
                        // The content is ready. Start drawing.
                        content.getViewTreeObserver().removeOnPreDrawListener(this);
                        return true;
                    } else {
                        // The content isn't ready. Suspend.
                        return false;
                    }
                }
            });
}

스플래시 화면 닫기 애니메이션 맞춤설정

Activity.getSplashScreen()을 통해 스플래시 화면의 애니메이션을 추가로 맞춤설정할 수 있습니다.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...

    // Add a callback that's called when the splash screen is animating to the
    // app content.
    splashScreen.setOnExitAnimationListener { splashScreenView ->
        // Create your custom animation.
        val slideUp = ObjectAnimator.ofFloat(
            splashScreenView,
            View.TRANSLATION_Y,
            0f,
            -splashScreenView.height.toFloat()
        )
        slideUp.interpolator = AnticipateInterpolator()
        slideUp.duration = 200L

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.doOnEnd { splashScreenView.remove() }

        // Run your animation.
        slideUp.start()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...

    // Add a callback that's called when the splash screen is animating to the
    // app content.
    getSplashScreen().setOnExitAnimationListener(splashScreenView -> {
        final ObjectAnimator slideUp = ObjectAnimator.ofFloat(
                splashScreenView,
                View.TRANSLATION_Y,
                0f,
                -splashScreenView.getHeight()
        );
        slideUp.setInterpolator(new AnticipateInterpolator());
        slideUp.setDuration(200L);

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                splashScreenView.remove();
            }
        });

        // Run your animation.
        slideUp.start();
    });
}

이 콜백이 시작되면 스플래시 화면의 애니메이션 벡터 드로어블이 시작됩니다. 앱 실행 시간에 따라 드로어블은 애니메이션 중간에 있을 수 있습니다. SplashScreenView.getIconAnimationStart를 사용하여 애니메이션이 시작된 시점을 알 수 있습니다. 다음과 같이 아이콘 애니메이션의 남은 시간을 계산할 수 있습니다.

Kotlin

// Get the duration of the animated vector drawable.
val animationDuration = splashScreenView.iconAnimationDuration
// Get the start time of the animation.
val animationStart = splashScreenView.iconAnimationStart
// Calculate the remaining duration of the animation.
val remainingDuration = if (animationDuration != null && animationStart != null) {
    (animationDuration - Duration.between(animationStart, Instant.now()))
        .toMillis()
        .coerceAtLeast(0L)
} else {
    0L
}

Java

// Get the duration of the animated vector drawable.
Duration animationDuration = splashScreenView.getIconAnimationDuration();
// Get the start time of the animation.
Instant animationStart = splashScreenView.getIconAnimationStart();
// Calculate the remaining duration of the animation.
long remainingDuration;
if (animationDuration != null && animationStart != null) {
    remainingDuration = animationDuration.minus(
            Duration.between(animationStart, Instant.now())
    ).toMillis();
    remainingDuration = Math.max(remainingDuration, 0L);
} else {
    remainingDuration = 0L;
}

추가 리소스