Vector Asset을 이용하여 넣어두었던 icon들의 동적인 색상 변화를 원했고,
그에 따라 일반적으로 생각되어지는 코드를 작성했다.
view.setColorFilter(R.color.colorPrimary, PorterDuff.Mode.SRC_ATOP)
하지만 적용되지 않았다.
색의 변화는 있었지만, 원하는 색이 아닌 거무스름한 색이 나왔다.
아래와 같은 코드로 수정했더니 원하는 결과가 나왔다.
val drawable: Drawable = DrawableCompat.wrap(view.drawable) // getDrawable()
DrawableCompat.setTint(
drawable.mutate(),
ContextCompat.getColor(this, R.color.colorPrimary)
)
됐다고 끝낼 수는 있지만, 코드를 간단하게는 분석해 볼 필요가 있었다.
우선 궁금했던건, DrawableCompat.wrap(@params Drawable)
의 역할이었다.
함수를 파고 들어가면, 아래와 같은 코드를 볼 수 있다.
public static Drawable wrap(@NonNull Drawable drawable) {
if (Build.VERSION.SDK_INT >= 23) {
return drawable;
} else if (Build.VERSION.SDK_INT >= 21) {
if (!(drawable instanceof TintAwareDrawable)) {
return new WrappedDrawableApi21(drawable);
}
return drawable;
} else {
if (!(drawable instanceof TintAwareDrawable)) {
return new WrappedDrawableApi14(drawable);
}
return drawable;
}
}
매개값으로 Drawable 객체가 들어오는데, 리턴값 역시 Drawable이다.
또한, SDK 버전별로 Drawable 객체가 다른 방식으로 래핑됨을 알 수 있는데, 반환되는 각각의 객체 역시 Drawable을 상속받은 클래스로부터 생성된 인스턴스로 이해할 수 있다.
이렇게 버전별로 Drawable의 처리를 받음으로, 버전에 상관없이 내부의 Tint 변경 코드를 사용할 수 있게 된다.
DrawableCompat의 setTint 함수를 사용한다. 첫 매개변수로 Drawable 객체를, 두 번째 매개변수로 color값을 int로 받는다.
위의 코드를 보면 알 수 있겠지만, Drawable 객체를 지정한 색으로 바꿔주는 역할을 하는 직관적인 함수다.
Android에서는 자체의 성능 향상을 위해 공통된 Drawable 객체의 리소스 파일을 사용하는 여러 뷰들에 대해, 공통된 하나의 상태만을 공유하도록 되어있다. 즉, 하나의 tint값을 바꾸면, 이를 사용하는 다른 뷰에 대해서도 tint값이 바뀐 상태로 우리에게 보여진다는 의미다.
이에 대한 설정을 꺼주고, 각 뷰들이 다른 뷰에 영향을 받지 않도록 도와주는 함수가 mutate() 이다.
글을 쓰고 공부를 하다보니 본 글은 DrawableCompat을 활용한 Tint변경에 대한 글이 맞는 것 같다.
이는 버전별 대응을 통해 색 변화가 이루어지도록 구성된 API클래스 이므로,
서두에서 언급했던 setColorFilter()가 작동하지 않는 문제의 해결법으로서의 글로는 적합하지 않다. (물론 문제가 해결되긴 했지만..)
setColorFilter()가 먹히지 않는 문제에 대해서는 공부를 더 해본 뒤 정리를 해봐야겠다.
https://medium.com/@hanru.yeh/tips-for-drawablecompat-settint-under-api-21-1e62a32fc033