본 포스팅은 꾸준히 수정됩니다~
findViewById<TextView>(R.id.sample_text).apply {
text = viewModel.userName
}
//또는
sample_text.text = viewModel.userName
전체 프로젝트의 코드 중 UI의 데이터를 변경해주는 코드의 양이 적지 않다. DataBinding을 사용하면 불필요한 UI 변경 코드를 줄이면서 데이터를 변경할수 있고 메모리 누수 방지와 NPE 방지 등 좋은 점들이 많다.
예를 들어, 부모 레이아웃안에서 TextView를 찾아 viewModel의 userName 값을 대입해주는 코드도 DataBinding을 사용하면 xml에서 똑같은 기능을 구현할 수있다.
변경 후 .. activity_main.xml
<TextView
android:text="@{viewmodel.userName}" />
DataBinding
추가 방법
추가 방법은 ViewBinding과 유사하다. 프로젝트 gradle의 DataBinding 종속성을 추가하면 된다.
bulid.gradle
android {
...
dataBinding {
enabled = true
}
}
레이아웃 컴파일러 지원
DataBinding은 레이아웃에서 Data를 표현식을 사용해 대입한다. 표현식이란 Data 그 자체로 대입하는 경우도 있지만 그 데이터를 가공해서 어떠한 기능을 구현하기도 할 때 사용하는 식이다.
ex) age가 13 이상이라면 Gone
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
따라서 표현식의 오류를 방지하기 위해서 표현식을 작성할 때 오류가 있다면 오류 경고를 제공해준다.
기본 구현
DataBinding은 표현식에 사용될 Data를 등록하는 것부터 시작된다.
Account.kt
data class Account(val site:String,var id:String, var pwd:String)
activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="account"
type="com.mut_jaeryo.saveaccount.model.Account" />
</data>
...
<TextView
...
android:text = "@{account.pwd}"
레이아웃의 구성은 layout 태그로 시작하게 되며 data 태그로 표현식에서 사용될 Data를 등록한다. 그러면 표현식에서 name에 해당하는 data에 접근하여 해당 클래스의 필드에 접근할 수 있다. 이후 Activity에서 account에 실제로 값을 가진 Account 객체를 넣어주어야 한다.
Activity에서도 변경이 필요하다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(
this, R.layout.activity_main)
binding.account = Account("naver", "User","1234")
}
DataBinding을 사용하여 변경한 레이아웃은 컴파일되어 파스칼 표기법으로 변경한 이름을 가진 새로운 클래스를 생성한다.
해당 클래스는 표현식에 사용될 Data와 레이아웃 안에 id가 부여된 컴포넌트를 접근할 수 있다.setContentView 함수의 기능과 동일하게 Activity의 화면에 보일 뷰를 구성함과 동시에 데이터를 결합하는 방법이다.
Fragment 또는 RecyclerView에서 DataBinding 레이아웃을 사용하고 있다면 inflate 함수를 사용하면 된다.
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
지속적인 UI 갱신
어쩌면 이 부분이 DataBinding의 기본 구현일 수도 있다. 포스팅의 기본구현에서 사용된 Account data는 String 과 같이 기본 클래스로만 필드가 구성되어 있다. 이 경우 처음 뷰가 구성될 때 UI가 변경되고 이후 Account의 필드 값이 변경되어도 UI는 자동으로 갱신되지 않는다.
Observable
위 인터페이스로 구현한 객체들은 값의 변경이 관찰된다. DataBinding에서 Data로 사용한다면 값이 변경될 때 자동으로 UI가 갱신되게 된다.
class User {
val firstName = ObservableField<String>()
val lastName = ObservableField<String>()
val age = ObservableInt()
}
최신 예제에서는 ViewModel을 결합한 LiveData를 사용해서 자동 UI 갱신을 구현한다.
LiveData
LiveData는 Observable과 같이 값의 변경을 관찰할 수 있다. 다른 점은 관찰하는 대상의 LifeCycle을 고려한다는 것이다.
LifeCycleOwner를 등록하여 등록된 관찰자만 변경을 관찰하고 관찰자의 수명주기를 고려하여 활성화 되어 있을때만 관찰하고 관찰자가 Destroy되면 즉시 삭제되어 메모리 누수를 방지한다.
class NameViewModel : ViewModel() {
// Create a LiveData with a String
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
// Rest of the ViewModel...
}
표현식의 확장 BindingAdapter
표현식으로는 복잡한 기능을 구현하기 힘들 수 있다. 그럴때 제공해주는 것이 BindingAdapter이다.
Picasso.get().load(url).error(error).into(view)
예를 들어,이미지 처리 라이브러리는 함수의 호출로 기능이 구현된다. 따라서 표현식에서 위의 코드를 구현하는 것은 쉽지 않다.
@BindingAdapter("imageUrl", "error")
fun loadImage(view: ImageView, url: String, error: Drawable) {
Picasso.get().load(url).error(error).into(view)
}
BindingAdapter 어노테이션으로 작성한 함수는 레이아웃에서 속성값으로 사용될 수 있다.
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
구조
@BindingAdapter(속성 이름 ... )
fun 함수이름 ( 속성이 사용될 View 객체 , 표현식에서 표현될 값 ... )
참고
'Android > Common' 카테고리의 다른 글
[Android] Google Calendar API 사용법 (4) | 2020.05.02 |
---|---|
[Android] 바인딩 서비스로 음악 재생 (0) | 2020.04.29 |
[Android] 로또 정보 크롤링해보기 by Kotlin (4) | 2020.04.08 |
[Android] Firebase ML Kit - Translate에 대한 소개 (6) | 2020.03.29 |
[Android]SharedPreferences by Kotlin (0) | 2020.03.27 |