BindingAdapter는 일반적인 View 에서 제공하는 속성이 아닌, 커스텀이나 개발 코드가 필요한 경우에 사용을 합니다.
예를 들어, ImageView에서 url 로 이미지를 로딩을 하여 표시해야 한다면,
기존에는 Activity에서 이미지를 로드하여 imageview에 표시하도록 구현을 해야했습니다.
그리고, 이런 로직이 화면마다 필요하다면 모든 Activity에 동일하게 구현을 해야 했죠.
이런 번거로움과 코드량을 줄여주는 것이 BindingAdapter 기능입니다.
위의 예를 코드상으로 구현해 보겠습니다.
<androidx.appcompat.widget.AppCompatImageView
...
app:profileUrl="@{viewModel.user.photoUrl}"/>
layout xml의 ImageView에 app:profileUrl 이라는 속성을 추가하고 이미지 url을 할당하였습니다.
이제 이 profileUrl 속성의 값을 받아서 이미지를 로드하여 ImageView 에 표시하는 코드가 필요합니다.
object BindingAdapterHelper {
@BindingAdapter("profileUrl")
@JvmStatic
fun setProfileImage(view: ImageView, url: String?) {
url?.let {
Glide.with(view.context).load(it).circleCrop().into(view)
}
}
}
setProfileImage()라는 함수를 만들고, @BindingAdapter 라는 Annotation을 추가했습니다.
@BindingAdapter의 파라메터 ("profileUrl") 은 layout xml 에서 추가해주었던 속성의 이름과 동일하게 작성해줍니다.
그리고, BindingAdapter로 사용되는 함수는 static이거나 전역함수여야 하기때문에 @JvmStatic 를 추가했습니다.
(object 클래스 안에 함수를 만들었기때문에 @JvmStatic 를 추가한 것이구요, 전역 함수로 만든다면 추가할 필요는 없습니다.)
setProfileImage()함수의 첫번째 파라메터는 연결되는 view를 받습니다. 두번째 파라메터부터 @BindingAdapter에 등록한 속성의 값을 받게 됩니다.
따라서, profileUrl의 값이 setProfileImage() 함수의 url 파라메터로 전달되게 됩니다. (이때 입력받는 값을 타입은 동일하게 맞추어야 하며, null 값이 들어올수 있기때문에 필히 ? 를 추가해주어야 합니다.)
위와같이 구현을 하였다면, 앱에서 사용하는 모든 ImageView에서 app:profileUrl 속성을 사용한다면 setProfileImage()로 연결이 되어 이미지가 로드되어 표시됩니다.
재사용도 가능하고, 코드도 깔끔해졌습니다.
전달해야하는 값이 1개 이상이면 어떻게 하나요?
물론 가능합니다.
이 부분도 샘플 코드로 설명드립니다.
이름과 점수를 받아서 "OOO님의 등급은 OO입니다." 라고 표시하는 코드입니다.
<TextView
...
app:userName="@{viewModel.user.name}"
app:score="@{viewModel.user.etc.score}" />
이름과 점수를 받아야하기 때문에 app:userName, app:score 를 추가하고 값을 할당합니다.
특이사항으로 viewModel.user.etc 는 HashMap 입니다.
Map에 "score" 라는 key로 값이 저장되어 있습니다.
DataBinding에서는 Map 데이터도 위와같이 사용이 가능합니다.
object BindingAdapterHelper {
@BindingAdapter("userName", "score")
@JvmStatic
fun setUserInfo(view: TextView, userName: String?, score: Int?) {
score?.let {
val grade = when {
it >= 80 -> "A+"
it >= 60 -> "A"
it >= 40 -> "B+"
it >= 20 -> "B"
else -> "C"
}
view.text = "${userName}님의 등급은 ${grade}입니다."
}
}
}
앞서 설명했던 내용에서 @BindingAdapter에 userName, score 를 추가하고, 함수에도 이를 받는 파라메터를 추가해줍니다.
그리고, 등급을 구분하는 코드와 TextView에 완료된 텍스트를 설정하는 코드를 추가해주면 됩니다.
생각보다 간단하죠.

추가기능으로 Button 에서 DataBinding으로 android:onClick 을 연동하는 방법을 알려드립니다.
<androidx.appcompat.widget.AppCompatButton
...
android:onClick="@{() -> viewModel.fetchUser(UUID.randomUUID().toString())}"/>
android:onClick에서 직접 함수 호출이 가능하고, 파라메터 데이터도 전달할수 있습니다.
다만, 파라메터로 null 이 오는 경우에는 연동이 안되게 됩니다.
두번째 방법으로는 handler class를 이용하는 방법입니다.
class BindingAdapterExampleActivity :
BaseActivity<ActivityBindingAdapterExampleBinding>(R.layout.activity_binding_adapter_example) {
inner class DataBindingHandler {
fun onHandlerClicked(view: View) {
Toast.makeText(view.context, "handler를 이용한 click!!", Toast.LENGTH_SHORT).show()
}
}
}
Activity에 inner class를 만들고, 이벤트를 받을 함수를 구현합니다.
그리고, layout xml에서 호출!!
<data>
...
<variable
name="handler"
type="com.remind.sampleapp.binding_adapter_example.BindingAdapterExampleActivity.DataBindingHandler" />
</data>
<androidx.appcompat.widget.AppCompatButton
...
android:onClick="@{(view) -> handler.onHandlerClicked(view)}"/>
파라메터로 view를 받기때문에 context 이용도 가능합니다.
전체 개발 코드는 아래 Github 링크를 확인해주세요.
GitHub - rcbuilders/RemindSampleApp: https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함.
https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함. - GitHub - rcbuilders/RemindSampleApp: https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함.
github.com
'Android + Kotlin' 카테고리의 다른 글
adb: more than one device/emulator 문제 해결하기 (0) | 2022.03.14 |
---|---|
[Android + Kotlin] 아이폰의 스와이프하여 뒤로가기 기능을 구현해보자. (1) | 2022.02.18 |
[Android Kotlin] TransactionTooLargeException 해결 방법 (0) | 2022.01.25 |
[Android Kotlin] 안전하게 Enum 타입 사용하기 (0) | 2022.01.24 |
[Android Kotlin] Fragment arguments 를 쾌적하게(Extension) 해보자. (0) | 2022.01.21 |