Data Binding은 2018년도 Jetpack 라이브러리의 일부로 소개되었다. 일단, 그 이전에 UI를 다루는 방식을 알아보자. 이전에는 코드내에서(보통 onCreate()나 onCreateView()) LayoutInflater를 이용하여 XML을 파싱 후 View 오브젝트로 변환하는 작업을 해야했다. UI를 구성하는 변환된 View 오브젝트는 트리형태로 되어있으며, 원하는 View를 얻어오려면 findViewById()등을 이용해 이 트리구조를 검색해야 가능했다. XML파싱도 검색도 모두 부하가 상당한 작업들이었다.
이를 개선하고자, Data Binding 라이브러리가 소개되었다. Data binding을 사용하면, 컴파일 단계에서 XML을 변환한 코드를 자동생성해준다. View 하나를 얻어오기 위해 findViewById()도 사용할 필요가 없이 생성된 객체의 멤버로 존재한다.
이제, 실제 사용하기 위한 방법을 하나씩 알아보자.
build.gradle
Data Binding을 사용하려면, 첫번째로 gradle 파일의 수정이 필요하다. 모듈 build.gradle 에 다음과 같이 들어간다.
android {
...
buildFeatures {
dataBinding true
}
}
dataBinding { enabled = true } 와 같이 사용되다가 buildFeatures 항목으로 변경되었으므로 알아두자.
위 내용을 추가하면 kotlin-kapt 플러그인이 필요하다고 경고가 뜰 것이다. 사용중이 아니라면 플러그인을 추가하자.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt' // <-- 요거
}
Layout XML
Layout XML 파일들도 조금 다른 형태가 되어야 한다. 우선 root tag가 <layout>이 되어야 한다. 그 안에, <data> tag로 코드관련 항목이 추가되었으며, 그 아래로 기존에 사용하던 layout 폼을 사용하게 된다. 가이드에 있는 XML 파일을 보면 다음과 같다.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>
최상위 태그가 <layout>인 것이 보이며, <data> 태그안에 실제 코드와 결합된 변수가 정의되어 있음을 볼 수 있다. 또한, 아래쪽 TextView의 text를 보면 @{user.firstName} 처럼 이 변수의 멤버를 직접 연결하고 있는게 보인다. 이 표현식에서 사용가능한 표현은 공식 문서의 해당항목을 참고하자.
android studio에서 기본으로 생성되는 xml파일들은 예전 형태이다. 하나씩 손으로 고칠 필요는 없고, 최상위 xml 태그에서 alt+enter를 눌러보면, “convert to data binding layout”항목이 뜬다. 이를 이용해 쉽게 변경 가능하다. 이 기능은 앞서 설명한 gradle파일 변경사항이 적용되어야 사용가능 할 것이다.
우선, 최상위 <layout> 태그로 둘러쌓이면, 이 레이아웃에 해당하는 클래스가 컴파일러에 의해 생성된다. android studio안에선 확인이 안되며, 소스 디렉토리에서 app>build>generated>data_binding_base_class_source_out 에서 확인된다.
<data> 부분은 사용자가 구현한 코드와 연동이 되고 있다. 예제의 User 클래스는 다음과같이 정의되어 있다.
data class User(val firstName: String, val lastName: String)
이걸 참조하여, XML에서 binding class가 생성될 때, “user” 변수의 타입을 정할 수 있다. 뒤에 나오는 text에서 @{user.firstName} 과 같은 표현도 가능해진다. text의 표현식은 보면 알겠지만, Kotlin에서 변수나 코드를 사용하는 문자열 포맷을 사용하고 있다.
Binding Data
이로서 사용할 클래스는 모두 준비가 되었다. 실제 코드내에서 인스턴스를 생성하고 실제 데이터를 연결해보자. 일반적으로 다음과 같은 방법을 사용한다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(
this, R.layout.activity_main)
binding.user = User("Test", "User")
}
DataBindingUtil 클래스를 사용하고 있으며, 리턴값으로 binding 객체를 받아오고 있다. 이것은 앞서 정의한 XML에 기반하여 자동으로 생성된 클래스의 객체이다. XML에서 ‘User class’ 타입의 ‘user’라는 변수를 선언했는데, 그 변수에 생성한 인스턴스를 할당해주고 있다. 이를 참조하는 XML의 text 값들은 이 인스턴스의 값들을 표시해주게 된다.
일반적으로, Fragment나 기타 view의 사용시에는 다음과 같이 직접 inflate()를 불러준다.
val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)
// or
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
위와같이 얻어온 binding 객체에는 id를 갖는 ui view들이 멤버로 포함되어 있어 바로 사용가능하다.
Data binding은 XML을 하나의 클래스로 생각할 수 있게 해준다. 위 내용은 시작일 뿐이고, LiveData와 연결하여 값의 변경을 바로 적용해주는 observer pattern도 쉽게 사용가능하고, onClick과 같은 이벤트 핸들러나 데이터를 가공해 할당해주는 Adapter도 사용이 가능해진다. 사실 이 모든게 MVVM Design pattern을 구성하는 큰 그림의 일부다. 이 후, 포스팅에서 추가로 다뤄보도록 하겠다.
1 thought on “Android: Data binding basics”