Compose 다크 모드 적용 후 Navigation 사용 시 화면이 깜빡이는 현상 | 삽질 노트

hoya·2023년 4월 24일
3

삽질 노트

목록 보기
8/8
post-thumbnail

😡 문제 상황

위의 GIF와 같이, 다크 모드 적용 후 Navigation 으로 화면을 이동할 때마다, 하얀 배경으로 깜빡이는듯한 오류가 발생했다.


🤔 원인 파악

우선, 근본적인 원인은 Navigation 을 사용하게 되면 자동적으로 애니메이션이 재생된다는 점이다.

기존에 있던 Composable 은 파괴되는 애니메이션과 새로 이동할 Composable 이 새롭게 생성되는 애니메이션이 재생되며 중간에 Composable 을 담고있는 액티비티의 배경색이 보여지게 된다.

즉, 파괴와 생성의 과정에서 아주 찰나의 순간에 액티비티의 배경색이 보여지게 되는 것이다.


😎 해결

원인을 알았으니 해결할 수 있는 방안으로 다음 두 가지를 생각할 수 있다.

자동으로 제공되는 애니메이션 자체를 없애버리거나, 액티비티의 배경색을 다크 모드에 걸맞게 바꾸는 것이다.


자동으로 제공되는 애니메이션 자체를 없애기

애니메이션 제거는 기본 라이브러리로는 불가능하고, 구글에서 제공하는 Accompanist 를 사용해야만 구현할 수 있다.

implementation "com.google.accompanist:accompanist-navigation-animation:0.30.0"

먼저, accompanist-navigation-animation 의 의존성을 추가해준다.

import com.google.accompanist.navigation.animation.AnimatedNavHost
import com.google.accompanist.navigation.animation.composable
import com.google.accompanist.navigation.animation.rememberAnimatedNavController

fun SampleScreen(
    navController: NavHostController = rememberAnimatedNavController()
) {
    AnimatedNavHost(
        navController = navController,
        startDestination = SampleScreenRoute.FIRST.route,
        enterTransition = { EnterTransition.None },
        exitTransition = { ExitTransition.None },
        popEnterTransition = { EnterTransition.None },
        popExitTransition = { ExitTransition.None },
    ) {
		composable(SampleScreenRoute.FIRST.route) { }
	}
}

그리고 위와 같이 NavHost 에서 AnimatedNavHost 로 변경하고, 모든 매개 변수에 None 을 설정해줌으로써 애니메이션 자체를 삭제해버린다.

이 때 꼭 유의해야 할 점은, NavControllercomposable 을 기존에 있던 것으로 사용하지 않고, 위와 같이 Accompanist 에서 제공하는 AnimatedNavController, composable 을 사용해야 한다는 점이다.


Activity 의 배경색 변경하기

이 방법은 애니메이션 자체를 없애는건 아니고, 최소한 다크 모드와 대비되는 색상으로 "어색하게 깜빡이는 것처럼 보이는" 모션을 없애기 위해 사용할 수 있다.

여기서 또 디테일이 나뉘는데, 모든 액티비티의 배경색을 한 번에 변경할 수 있는 방법과, 컴포저블 하나마다 해당 액티비티의 색상을 변경할 수 있는 방법이 있다.

먼저, 모든 액티비티의 배경색을 바꾸는 방법은 다음과 같다.

<style name="Theme.Composenavigationblinking" parent="android:Theme.Material.Light.NoActionBar">

    <item name="android:statusBarColor">@color/purple_700</item>
    <item name="android:windowBackground">@color/black</item>
</style>

Theme.xml 에서 android:windowBackground 의 속성을 변경해준다. android:windowBackground 는 액티비티 혹은 다이얼로그의 배경색이 지정되어 있지 않은 경우 자동으로 사용할 색상을 지정해주는 속성이다.

Compose 로 UI 작업을 할 때 액티비티의 배경색을 따로 지정할 일은 거의 없으므로, 위 속성을 지정해주면 모든 액티비티의 배경색이 변경될 것이다.

그리고 컴포저블 하나마다 액티비티의 배경색을 바꾸는 방법은 다음과 같다.

@Composable
fun FirstScreen() {
    val activity = LocalContext.current as Activity
    
    val color = android.graphics.Color.BLACK
    val bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
    bitmap.eraseColor(color)

    val bitmapDrawable = BitmapDrawable(LocalContext.current.resources, bitmap)

    LaunchedEffect(activity) {
        activity.window.setBackgroundDrawable(bitmapDrawable)
    }
}

BitmapDrwable 을 사용하여 현재 컴포저블을 담고있는 액티비티의 배경색을 변경하는 방법이다. 이렇게 하면 단일 컴포저블마다 액티비티의 배경색을 변경할 수 있어 특수한 상황에는 고려해볼 수 있는 방법이다.


이 방법도 비슷한 원리로, 액티비티가 컴포저블을 감싸는 대신, Scaffold 가 컴포저블을 감싸버리게 하는 방법이다.

Scaffold(backgroundColor = Color.Black) { innerPadding ->
    NavHost(
        navController = navController,
        startDestination = SampleScreenRoute.FIRST.route,
        modifier = Modifier.padding(innerPadding)
    ) {
        composable(SampleScreenRoute.FIRST.route) {
            FirstScreen()
        }
    }

위와 같이 설정하면, 이동 시 찰나의 순간에 액티비티가 아닌 Scaffold 의 배경색이 보여지게 된다.


혹시, Scaffold 배경색을 잘못 설정하진 않으셨나요?

이건 위의 언급한 문제 원인과 완전히 다른 원인인데, Scaffold 의 배경색을 아래와 같이 잘못 설정하면 마찬가지로 "깜빡이는 것처럼" 보일 수 있다.

Scaffold(
	modifier = Modifier.background(Color.DarkGray)
    // backgroundColor = Color.DarkGray
)

Scaffold 에는 기본적으로 backgroundColor 매개변수가 있는데, 여기엔 기본값으로 MaterialTheme.color.background 가 설정되어 있다. 하지만 만약 유저 커스텀으로 다크 모드 설정 시 따로 사용하는 배경색이 있다면, 꼭 이 backgroundColor 를 지정해주어야만 한다.

하지만 잘못된 사용 방법으로 modifier.backgroundScaffold 의 배경색을 사용하는 케이스가 있는데, 이렇게 되면 내부 코드 구조 상 modifier.background 가 적용되고, 다시 backgroundColor 매개변수에 설정한 배경색이 적용되게 된다.

아마 개발자 입장에서는 modifier.background 에 입력한 색상만이 적용되길 바라겠지만, 사실 backgroundColor 의 매개변수의 기본값인 MaterialTheme.color.background 도 순서대로 적용되며 깜빡이는 것 같은 현상이 발생할 수 있다.

꼭, Scaffold 의 배경색을 설정할 때에는 아래와 같이 설정하도록 하자.

Scaffold(
    // modifier = Modifier.background(Color.DarkGray) -> DO NOT!!
    backgroundColor = Color.DarkGray
) 

AnimatedNavHost 를 사용하는 방법이 아닌 다른 방법을 채택한 경우, 위와 같이 작동하게 된다.

하얀색으로 깜빡이는 것처럼 보이지 않고, 자연스럽게 애니메이션되는 모습을 확인할 수 있다.


참고 및 출처

https://stackoverflow.com/questions/68633717/topappbar-flashing-when-navigating-with-compose-navigation
https://stackoverflow.com/questions/71108210/screens-are-flashing-when-i-navigate-to-them-in-dark-theme-using-navigation-comp
https://google.github.io/accompanist/navigation-animation/
https://betterprogramming.pub/realize-jetpack-compose-navigation-2889401f52b

profile
즐겁게 하자 🤭

1개의 댓글

comment-user-thumbnail
2023년 4월 24일

좋은 글 감사합니다~

답글 달기