color.xml과 Color객체
color.xml은 Jetpack Compose가 출시되기 이전 color을 정의하는 방법이었다. 하지만, Compose에서 Color객체와 Color객체를 이용한 Theming을 지원하게 되면서 코드 상에 Color 객체를 정의하는 방식이 표준이 되어, 기존 color.xml을 이용한 색상 리소스 로딩 방법을 더이상 사용하지 않게 되었다.
하지만 기존의 프로젝트들에서 모든 리소스를 한 번에 이동하기는 번거롭다. 따라서 Compose에서는 colorResource메서드와 color.xml을 이용한 Color객체 로딩 방법 또한 제공한다.
새로운 앱에서는 color.xml을 사용하지 말고 Color 객체를 사용하도록 하자.
기존 color.xml의 color를 로딩하기 위해 getColor을 이용한 방식과 한계점
기존에는 Application Context나 Activity Context에서 getColor을 통해 해당 color의 리소스에 접근해야 했다.
baseContext.getColor(R.color.black) // Activity
applicationContext.getColor(R.color.black) // App
ContextCompat.getColor(applicationContext, R.color.black) // ContextCompat 이용
하지만 이 context을 직접 사용해야지 Color를 받을 수 있다는 단점이 있었다. context를 다른 클래스로 넘기는 순간 memory leak의 우려가 생기고, 이 자체를 프레임웍 단에서 막지 않아 직접 WeakReference 처리를 해주거나해야 했다.
물론 Activity나 Fragment같은 applicationcontext를 직접 접근할 수 있는 곳에서는 사용할 수 있었지만, 그렇지 않아도 Activity나 Fragment의 의존성이 높던 안드로이드에서는 Activity와 Fragment의 의존성이 더 강해져 Activity와 Fragment 클래스에서 너무 많은 역할을 하게 된다. 이는 객체 지향의 원리에 반하며 코드의 유연성을 떨어트린다.
colorResource이용해 color.xml에서 Color 객체 만들기
colorResource를 이용하면 위의 memory leak 우려가 완전히 사라진다. 안드로이드 프레임웍단에서 Context를 관리해주기 때문이다. 내부를 보면 Composable에서 관리하는 Context가 따로 있고 해당 Context에서 color 객체에 접근하는 것을 볼 수 있다. 즉 기존에는 getColor를 쓸 때 우리가 직접 Context를 관리했다면 Compose 프레임웍단으로 Context의 관리 책임이 넘어간 것이다.
@Composable
@ReadOnlyComposable
fun colorResource(@ColorRes id: Int): Color {
val context = LocalContext.current
return if (Build.VERSION.SDK_INT >= 23) {
ColorResourceHelper.getColor(context, id)
} else {
@Suppress("DEPRECATION")
Color(context.resources.getColor(id))
}
}
colorResource를 이용해 기존 color.xml 파일에서 Color 객체를 로딩하는 방법은 간단하다. 단순히 colorResource뒤에 color.xml 상의 color의 id를 적어주면 된다.
fun colorResource(@ColorRes id: Int): Color
예를 들어 color.xml에 black이라는 id를 지정해놓고 해당 color값을 로딩하고 싶다면 다음과 같이 쓰면 된다.
val blackColor = colorResource(id = R.color.black)
단, 눈치가 빠른 사람은 눈치 챘겠지만 colorResouce위에는 @Composable이 붙어 있기 때문에 colorResource를 이용해 color.xml에서 Color객체를 로딩하는 것은 @Composable에서만 가능하다.(Context가 Composable에서 관리되고 있기 때문) 만약 Composable 외부에서 Color 객체를 로딩하고 싶으면 기존의 getColor 방식을 써야 한다.
Composable 외부에서 color.xml에서 Color 객체 만들기
Composable외부에서 color.xml에서 Color 객체를 만들기 위해서는 기존의 방식을 사용해야 한다. Context에서 직접 ColorResource에 접근해 Color의 Int값(4byte에 해당하는 Int값)을 얻은 후 해당 int값을 Color 객체 내부에 세팅해주면 된다.
val blackColorFromActivity = Color(baseContext.getColor(R.color.black))
val blackColorFromApplication = Color(applicationContext.getColor(R.color.black))
*조금 더 자세히 보고 싶은 사람은 접은글을 펼치자.
여기서 사용하는 Color 함수는 Int값을 생성자로 받는다.
fun Color(/*@ColorInt*/ color: Int): Color {
return Color(value = color.toULong() shl 32)
}
앞선 글에서 Compose의 기본 Color객체는 Hex(16진수)값으로 만드는데 왜 Int로 Color을 생성하는 생성자가 있나 했는데 여기서 이렇게 사용된다.
예를 들어 다음과 같이 color객체를 Activity에서 접근해서 Color 객체를 만들어 사용할 수 있다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val blackColorFromActivity = Color(baseContext.getColor(R.color.black))
val blackColorFromApplication = Color(applicationContext.getColor(R.color.black))
setContent {
ComposeView(backgroundColor = blackColorFromActivity)
}
}
}
@Composable
fun ComposeView(backgroundColor: Color){
Box(modifier = Modifier.fillMaxSize().background(color = backgroundColor))
}