Custom Splash Screen 만드는 방법(Splash Screen API을 사용하는 경우)

이지훈·2023년 10월 20일
2

서두

일단 먼저 언급할 것이 이 방법은 꼼수이다.
하지만 다른 더 나은 방법은 찾지 못했기에 공유해보도록 하겠다.

TL;DR

SplashScreen Theme 의 Icon을 SplashScreen 배경 색상과 같게 맞춰서 빈 화면으로 보이게 만들고, Custom Splash Screen을 직접 만들어 시작 화면으로 설정하자.

문제의 발단

디자이너님께서 스플래시 화면을 다음과 같이 만들어 주셨다.

문제 발생

SplashScreen API를 적용할 경우엔 SplashScreen Theme 내에서 스플래시 화면의 배경 색상이나, 스플래시 화면 지속 시간 등은 custom 하게 지정할 수 있지만, 아이콘 자체는 app icon이 위치할 화면 중앙의 원 영역 내로 그 규격이 한정 되기 때문에 위와 같이 app의 icon의 규격과는 다른 icon을 넣을 경우 의도치 않는 결과가 나오는 것을 확인할 수 있었다.

<?xml version="1.0" encoding="utf-8"?>
<resources>

  <style name="Theme.Bandalart.Splash" parent="Theme.SplashScreen">
    <item name="windowSplashScreenBackground">#FFF9FAFB</item>
    <!--    적용 하고 싶은 이미지-->
    <item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
    <item name="postSplashScreenTheme">@style/Theme.Bandalart</item>
  </style>

</resources>

실행 결과)

문제 해결

SplashScreen Theme 내에 어떠한 설정 값을 수정하여 app icon 자리에 원하는 Image(혹은 lottie, gif)를 넣고 이를 짤리지 않게 정상적으로 반영 시키는 방법은 존재 하지 않는 것 같아, 여러 곳에 질문을 올려보았고, 선배 개발자 분들의 내공이 느껴지는 대처 방법을 알 수 있었다.

무조건 app icon이 출력되는 화면 중앙에, 그것도 원형의 규격 내에서만 app icon을 설정해야 한다면, splash 화면에 들어갈 app icon의 전체 색상을 Splash Screen 의 배경 색상과 같게 맞춰 배경 색상만 보이는 빈 화면으로 보이게 하면 되지 않을까?...

이게 무슨 소리냐면 그냥 Splash Screen API 로 만들어지는 splash 화면을 빈 껍데기 화면으로 만들겠다는 것이다. app icon이 없는, 빈 splash 화면(색상은 요구사항에 따라 변경 가능)을 빠르게 지나가게 한 뒤에, 그때 내가 만든 커스텀한 splash screen을 띄우자는 의미이다.

splash 가 두번 뜨는거 아니냐고 생각할텐데 그게 맞다. 하지만 빈 splash screen이 0.5초도 되지 않는 시간내에 사라지고 내가 만든 splash screen이 뜬다면. splash가 연속으로 두번 발생하는 것을 알아차리긴 쉽지 않을 것이라 예상된다.(다만, splash screen의 배경색상은 반드시 일치시켜줘야 한다.)

이 방법을 적용한 코드는 다음과 같다.

Themes.xml

<resources>

  <style name="Theme.Bandalart.Splash" parent="Theme.SplashScreen">
    <item name="windowSplashScreenBackground">#FFF9FAFB</item>
    <item name="windowSplashScreenAnimatedIcon">@drawable/ic_dummy_splash</item>
    <item name="postSplashScreenTheme">@style/Theme.Bandalart</item>

  </style>

  <style name="Theme.Bandalart" parent="Theme.Material3.Light.NoActionBar">
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:windowLightStatusBar">true</item>

  </style>

</resources>

ic_dummy_splash

<vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="108dp"
  android:height="108dp"
  android:viewportWidth="192"
  android:viewportHeight="192">

  <path
    android:fillColor="@color/white"
    android:pathData="M0,0h1200v1200h-1200z" />

</vector>

참고로 이런 vector 이미지이다.
fillColor 는 원하는 Splash Screen의 배경색상과 같도록 맞춰주면 된다.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools">

  <uses-permission android:name="android.permission.INTERNET" />

  <application
    android:name=".BandalartApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="${appName}"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.Bandalart"
    android:usesCleartextTraffic="true">

    <activity
      android:name="com.nexters.bandalart.android.MainActivity"
      android:exported="true"
      android:theme="@style/Theme.Bandalart.Splash">

      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>

    </activity>

  </application>

</manifest>

이 이후로 Multi-Activity 구조라면 직접 만든 Splash Activity를 진입 화면으로 설정한 뒤에 기존의 진입화면으로 이동하도록 코드를 작성해주면 될 것이고.

IntroActivity

@AndroidEntryPoint
class IntroActivity : AppCompatActivity() {

  private val SPLASH_DISPLAY_LENGTH = 500L

  private val binding by lazy { ActivityIntroBinding.inflate(layoutInflater) }
  override fun onCreate(savedInstanceState: Bundle?) {
    installSplashScreen()
    super.onCreate(savedInstanceState)
    setContentView(binding.root)

    Handler(Looper.getMainLooper()).postDelayed(
      { startActivityWithAnimation<MainActivity>() },
      SPLASH_DISPLAY_LENGTH,
    )
  }
}

activitiy_intro.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <ImageView
    android:id="@+id/iv_logo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="@string/app_logo"
    android:src="@drawable/ic_splash"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Single-Activity 기반의 프로젝트라면 navigation의 start destination을 splash fragment or splash screen composable 로 설정한 뒤에 navigate 함수로 기존 진입화면으로 이동하면 될 것이다.

왜 네이밍을 갑자기 Intro 라고 했는지, 의아해할 수 있는데, SplashScreen API 가 세상에 나타난(?) 이후 Android Studio IDE 측에서 SplashActivity라고 Activity 이름을 지정하면 사용하지 말라는 lint가 뜨기 때문에 Intro 라는 네이밍을 대신 사용했다.
이를 따로 feature 모듈로 빼줄 경우, 내부에 Activity 네이밍은 Intro~ 인데 모듈 네이밍이 splash 라면, 깔맞춤이 되지 않는 것 같아 모듈 네이밍 또한 Intro 로 해주는 편이다.

결과

실행 결과를 gif 를 올릴때 프레임이 감소해서 좀 끊기는 듯한 느낌이 없지 않아 있지만, 생각보다 스무스하게 흰 바탕의 빈화면이 빠르게 지나가고 내가 지정한 icon이 나오는 custom splash screen 이 나오는 것을 확인할 수 있다. 실기기에서 보면 눈치 못챔 ㄹㅇ

animation을 추가해서 custom image 를 fade in, fade out 효과를 적용 하는 것도 좋은 방법일 것이다.

위에 코드가 포함된 전체 코드는 아래 깃허브 링크를 통해 확인할 수 있습니다.
https://github.com/Nexters/BandalArt-Android

profile
실력은 고통의 총합이다. Android Developer

0개의 댓글