Vector Drawable과 하위호환

Vector Drawable

VectorDrawable은 API 21이상에서만 지원

getDrawable(R.drawable.vector)는 VectorDrawable을 반환한다.

API 21 이전 버전에서 Vector 지원

  • 하위 호환을 위한 PNG 생성

    • build.gradle에 android { defaulConfig { vectorDrawables.useSupportLibrary = true }} 옵션을 주지 않는 경우 빌드 중 하위 호환을 위한 PNG를 생성하며 pre-21에서 getDrawable(R.drawable.vector)는 BitmapDrawable를 반환한다.
  • vectorDrawables.useSupportLibrary = true

    • pre-21에서 getDrawable(R.drawable.vector)는 VectorDrawableCompat을 반환한다.
    • 모든 theme가 AppCompat를 상속해야 한다.
    • XML에서 vector 참조시 ImageView와 ImageButton은 app:srcCompat 속성을 사용해야 한다.

21 이전 버전에서 Issue

  • pre-21에서 벡터 이미지와 동일한 이름으로 png 파일을 등록하면 실행시 이미지 리소스를 제대로 찾지 못해서 crash 발생

  • ImageView에 android:tint 사용하시면 pre-21에서 크래시 발생. 대안으로

    • AppCompatImageView에 app:tint와 app:tintMode 사용
    • Vector xml 파일에 android:tint, android:tintMode 사용
    • java 코드에서 적용
  • Context의 Resources로 Drawable를 얻으려면
    • AppCompatResources.getDrawable 사용해야 함.

    • ContextCompat.getDrawable 사용시 pre-21에서 crash

svg

그리는 옵션은 두 가지 non-zero, even-odd

even-odd는 API 24이상만 지원. 이하에서는 의도와 다르게 표시될 가능성이 있음.

AppCompatDelegate.setCompatVectorFromResourcesEnabled 옵션 적용

  • SDK 21 버전 부터 true, 이전은 false

  • 21 이하 버전에서 selector에서 vector 이미지 사용 가능하도록 처리하기 위하여 옵션을 true로 해야함.​

하위 호환을 위해 주의할 점

  • 옵션을 true로 설정하고 selector에 vector 이미지를 사용하는 경우에는 compat 속성를 사용하지 않아도 되지만 되도록이면 모두 compat속성을 사용을 하는 것이 좋음.

  • AppCompatImageView, VectorDrawableTextView, VectorDrawableCheckedTextView 등에서 제공되는 compat 속성 사용

  • 만일 compat 속성이 없는 경우에는 selector를 사용하여 회피하거나 compat 속성을 추가한 커스텀 뷰 개발

  • 일반적인 background 속성인 경우에는 selector를 사용하여 회피 가능

AppCompatDelegate.setCompatVectorFromResourcesEnabled 옵션 참고 사항

  • 작동 방식

  • TypedArray에서 drawable을 로딩할때는 Resources.loadDrawable을 사용함. 이 메서드는 package scope이기 때문에 재정의가 불가능함.

  • StateListDrawable은 Resources.getDrawable을 사용함. 이 메서드는 public이기 때문에 재정의가 가능

  • setCompatVectorFromResourcesEnabled을 true로 켜도 TypedArray에서 drawable을 가져올 경우에는 21버전 이하에서 VectorEnabledTintResources.getDrawble을 사용 할 수 없기 때문에 android 기본 속성에서는 백터 이미지를 사용할 수 없지만 selector는 사용 가능

  • 즉, selector에 벡터 이미지를 쓰고 싶어도 true로 설정해야 하고 이를 역 이용하여 selector에 무조건 백터 이미지를 포함시켜서 srcCompat을 안 써도 사용할 수 있는 것임.

  • 메모리 이슈

  • api 문서에서 언급하는 메모리 이슈는 모든 벡터 이미지에 대해서 selector를 사용해야 하기 때문에 메모리 이슈가 있을 수 있다는 것으로 해석됨.

  • selector를 사용 안하는 경우에 대해서는 compat 속성을 사용하면 되기 때문에 옵션을 적용해서 생기는 메모리 이슈는 없을 것으로 예상됨.​

추가적으로 발견된 이슈

  • sdk 21 이전 버전에서 벡터 이미지와 동일한 이름으로 png 파일을 등록하면 실행시 이미지 리소스를 제대로 찾지 못해서 crash 발생
  • ImageView에 android:tint 사용하시면 하위버전(21 미만)에서 크래시 발생합니다.

    확인하시고 수정 부탁드립니다.

    • 참고 : 아래 방법으로 해결 하시면 될 듯 합니다.

    • AppCompatImageView에 app:tint와 app:tintMode 사용

    • Vector xml 파일에 android:tint, android:tintMode 사용

    • java 코드에서 적용

  • minSdk 17로 설정하기

호환 안 되는 drawable 관련 api 사용으로 하위 버전에서 크래시가 발생합니다. 17로 켜서 테스트 하시는게 좋을 것 같습니다.

  1. DrawableCompat.setTintMode, setTint 사용하기

drawable은 하위 api에서 setTint가 동작 하지 않습니다. DrawableCompat.setTint나 ApiCompatUtils.setTint를 사용하시고요.

Mode를 바꿔서 쓰시려면 DrawableCompat.setTintMode, setTint 조합을 사용하십시오. 이때는 mutate()를 사용해서 drawable 상태 변수를 복사하여 사용하세요.

  1. ContextCompat.getDrawable 사용하지 마세요.

ContextCompat.getDrawable을 사용하지 마십시오. AppCompatResources.getDrawable을 사용하세요. 테스트 해보니 하위 버전에서 크래시 납니다.

  1. srcCompat 적용 안된 부분 확인하기

벡터 이미지 적용만 하고 srcCompat을 적용 안한 곳이 있습니다. 수정 바랍니다.

  1. VectorDrawable로 형변환을 하지 마세요.

하위 버전에는 해당 클래스가 없습니다. 형변환은 하지 마세요.

  1. TypeArray에서 getDrawable을 사용하지 마세요.

커스텀 뷰에서 사용할 수 있는데요. 이미지를 벡터로 바꿨다면 AppCompatResources를 사용하세요.

  1. background에 vector를 사용할 경우에는 아래와 같이 변경하십시오.

background에 벡터를 사용하시면 다음과 같이 대응하시면 됩니다.

  • TextView_background를 사용할 경우에는 VectorDrawableTextView에 backgroundCompat 속성을 추가하였습니다. 이것을 사용하십시오.

  • Button_background 속성을 사용할 경우에는 ImageView를 사용하시고 srcCompat을 사용하시면 될것 같습니다.

기타 다른 상황에서 크래시가 발생하면 공유 부탁드립니다.

* Vector 사용 옵션 변경 테스트 상황 공유

해외 포럼에나 국내 블로그에서 찾아 봤을때는 StateListDrawable을 사용하면 안전하게 vector를 사용할 수 있다고 했으나...

sdk

<

= 19 이하에서 state list drawable 생성시에 벡터 이미지를 제대로 로딩하지 못하고 크래시가 발생해서.. 결국에는...

if 
(Build.VERSION.
SDK_INT 
<
= Build.VERSION_CODES.
KITKAT
) {


   AppCompatDelegate.
setCompatVectorFromResourcesEnabled
(
true
)
;


}

이 옵션을 켜고 테스트 하려고 합니다. AppCompatActivity를 context로 사용하게 되면 Resources를 Vector를 사용가능하게 만들어주는 옵션인데요.

@Override


public 
Resources 
getResources
() {


if 
(
mResources 
== 
null 
&
&
 VectorEnabledTintResources.shouldBeUsed()) {


mResources 
= 
new 
VectorEnabledTintResources(
this, super
.getResources())
;


}


return 
mResources 
== 
null 
? 
super
.getResources() : 
mResources
;


}

이렇게 하면 AppCompatActivity 내부의 뷰는 모두 VectorEnabledTintResources를 사용해야 하는데 문제는 TypedArray만 기본 Resources를 사용하는 것으로 보입니다.

결국에는 위 옵셥을 켜면 일단 state list drawable을 사용하는데는 무리는 없으나 srcCompat을 적용한 곳에서만 크래시 없이 사용할 수 있습니다.

19 버전 이하에만 옵션을 켜놨고 위에 공유해드린 수정 사항을 17 버전에서 수정하고 테스트 하시면 됩니다. 더 자세한 내용은 정확하게 파악하고 나서 추가 공유드릴게요.

각 서비스에서도 하위 버전에서 위와 같이 테스트 하시고 메모리나 성능 이슈가 없는 확인 부탁드립니다.

설날이네요... ^^;; 연휴 잘 보내시고 새해 복많이 받으세요~

감사합니다.

results matching ""

    No results matching ""