Using Vector Drawable Safely on pre-lollipop version
Bài đăng này đã không được cập nhật trong 5 năm
Vector Drawable
- offically support trên android version 5.0 (API level 21) là vector graphic
định nghĩa trong một file XML bởi tập các points, lines, curves
cùng với thông tin về màu sắc tương ứng. Điểm lợi thế của nó là khả năng scalability
. Nó có thể scale
mà không bị mất chất lượng, điều đó có nghĩa là chỉ cần 1 file Vector Drawable
, bạn có thể resize cho các màn hình với density khác nhau mà không bị mất chất lượng hình ảnh. Rõ ràng điều này giúp cho kích thước của file APK
giảm đáng kể.
Bạn có thể tìm hiểu thêm nhiều thông tin về loại drawable này tại đây .
Vì lợi ích đặc biệt của nó nên dù ra đời từ android 5.0, nhưng google
vẫn cho phép các version android trước đó sử dụng thông qua các gói support. Tuy nhiên, trong lúc sử dụng cho các version trước đó, có một vài điểm cần lưu ý. Trong bài viêt này, mình xin tóm tắt vài tips để tránh những issues lúc sử dụng Vector Drawable
cho android 4.4 (pre-lollipop).
build.gradle
Để hỗ trợ cho android pre-lollipop
bạn cần enable vectorDrawable
config trong build.grale
của module app
. Lưu ý, đây là điều bắt buộc.
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
Setting in Application class
override fun onCreate() {
super.onCreate()
// App crashes when selector with using vector-drawable
// So, keep using this line + AppCompatImageView for android 4.4
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
// Your other logic code ...
}
Đây là option
không bắt buộc, mặc định sẽ là false
, bởi vì enable
nó có thể gây những issue về sử dụng memory, và khi update đối tượng Configuration
. Nếu bạn thay đổi configuration
bằng code, thì tốt nhất là không nên enable feature này.
Còn lý do để thêm dòng này là bạn có thể bị crash trên 4.4 nếu vô tình tạo 1 selector
có chứa reference
đến một Vector Drawable
VD:
<selector xmlns:android="...">
<item android:state_checked="true"
android:drawable="@drawable/vector_checked_icon" />
<item android:drawable="@drawable/vector_icon" />
</selector>
=> Vì thế, hãy cân nhắc trường hợp của ứng dụng trước khi sử dụng option này.
Way to getDrawable programmatically
Có nhiều cách để get và tạo Vector Drawable
thông qua gói support.
// using ContextCompat
ContextCompat.getDrawable(@NonNull Context context, @DrawableRes int resId)
// using ResourcesCompat
ResourcesCompat.getDrawable(@NonNull Resources res, @DrawableRes int id, @Nullable Theme theme)
// using AppCompatResources
AppCompatResources.getDrawable(@NonNull Context context, @DrawableRes int resId)
// using VectorDrawableCompat
VectorDrawableCompat.create(@NonNull Resources res, @DrawableRes int resId, @Nullable Theme theme)
Tuy nhiên, với mỗi cách khác nhau, trong các điều kiện khác nhau có thể gây ra những lỗi không mong muốn. Đặc biệt trong trường hợp làm custom view nếu bạn cho phép các attribute
reference đến resource là một drawable (normal drawable hoặc vector drawable). Hãy xem kết quả:
Normal image resource (non-vector)
Kind | Set setCompatVectorFromResourcesEnabled |
Not Set |
---|---|---|
ContextCompat | Good | Good |
ResourcesCompat | Good | Good |
AppCompatResources | Good | Good |
VectorDrawableCompat | Crashed | Crashed |
Vector drawable resource
Kind | Set setCompatVectorFromResourcesEnabled |
Not Set |
---|---|---|
ContextCompat | Good | Crash API < 21 |
ResourcesCompat | Good | Crash API < 21 |
AppCompatResources. | Good | Good |
VectorDrawableCompat | Good | Good |
Với cả Normal hay Vector Drawable resource, pre-lollipop hay không, chỉ có AppCompatResources.getDrawable(...)
là an toàn nhất để getDrawable()
.
Dưới đây là ví dụ về cách custom view có thể dùng cho bất kì drawable
nào:
const val INVALID_RES_ID = -1
private fun initAttrs(attrs: AttributeSet?) {
if (isInEditMode || attrs == null) return
var drawableStart: Drawable? = null
var ta: TypedArray? = null
try {
ta = context.obtainStyledAttributes(attrs, R.styleable.YourCustomView)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Directly call getDrawable is safely
drawableStart = typedArray.getDrawable(...)
} else {
val drawableStartId = typedArray.getResourceId(..., INVALID_RES_ID)
if (drawableStartId != INVALID_RES_ID)
drawableStart = AppCompatResources.getDrawable(context, drawableStartId)
}
// other logic
} finally {
ta?.recycle()
}
}
Ngoài ra, lúc tạo custom view, mình khuyên các bạn nên extends
các class
từ gói support như AppCompatImageView
, AppCompatTextView
,... Vì bản thân các component
trong gói support
có hỗ trợ tốt hơn trên những device pre-lollipop version.
Happy Coding !
All rights reserved