과거 Android를 개발할 때 view에 접근하는 가장 보편적인 방식은 'findViewById'를 사용하는 것이었습니다.
val mTextView = findViewById<TextView>(R.id.tv_hello)
'findViewById'의 문제점?
그 이외에도 'Butter Knife'나 'Kotlin Synthtic' 방식으로도 view 접근을 했다고 하는데 현재는 여러 문제로 인해 deprecated 되었다고 합니다. (상세한 내용은 아래 참조문서 ㄱㄱ)
그래서 google에서는 저 위의 문제를 해소하고자 view 항목에 접근하고 바인딩하여 보다 손쉽게 개발이 가능하도록 'dataBinding'과 'viewBinding'을 제공하게 되었습니다.
이번에 이 둘의 차이점을 알아보고 사용법을 익혀보겠습니다.
두 바인딩 기능을 사용하기 위해선 build.gradle(:app) 프로젝트 설정이 선행되어야 하는데, 둘다 사용하려고 하니 한꺼번에 설정을 잡습니다.
android {
...
// Android Studio 4.0 이상
buildFeatures {
viewBinding true
dataBinding true
}
// 안드로이드 스튜디오 3.6 ~ 4.0
viewBinding {
enabled true
}
dataBinding {
enabled true
}
}
안드로이드 스튜디오가 4.0 이상부터는 위의 buildFeatures {} 부분만 선언하면 되고 이전 스튜디오 버전(3.6 부터 4.0 이전)은 아래 처럼 하나하나 바인딩 선언을 하면 됩니다.
(안드로이드 스튜디오 3.6 미만 버전에서는 binding 을 사용하지 못하겠죠? 설정이 안되니...)
ViewBinding에 대해 먼저 알아보겠습니다.
아래 소스는 기본 레이아웃 소스와 Activity 클래스 입니다.
<activity_main.xml>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="test" />
</androidx.constraintlayout.widget.ConstraintLayout>
<MainActivity.kt>
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mTextView = findViewById<TextView>(R.id.tv_hello)
mTextView.setText("hello world!")
}
}
기본적으로 R.layout.id 의 레이아웃 정보를 setContentView에 세팅하고 this.findViewById()를 통해 view에 접근하고 있습니다.
<MainActivity.kt>
class MainActivity : AppCompatActivity() {
// binding 객체 선언
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// binding 정의
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// textView 'tv_hello'->'tvHello' 자동으로 camel 표기법으로 인식됨.
binding.tvHello.text = "hello world!"
}
}
binding 객체를 선언 할 때 ActivityMainBinding 이라는 것은 activity_main(.xml)_binding <- 이 layout resource를 바인딩하여 사용하게 해주는 자동완성? 클래스 입니다.
(예를 들어 SplashActivity.kt 에서 사용하는 layout 리소스의 이름이 activity_splash.xml 이라면 바인딩해서 사용할 때는 ActivitySplashBinding이라는 바인딩 클래스가 자동적으로 생기는 것입니다. camel 표기법)
onCreate() 에서 binding 객체를 inflate()를 통해 정의하여 바로 camel 표기법으로 id 정보를 자동으로 호출할 수 있게 되었습니다.
이처럼 별도로 view id 값을 찾아 TextView 객체에 넣고 접근을 할 필요 없이 binding.viewId 값을 통해 바로 사용할 수 있게 하는 것이 viewBinding입니다.
view 항목을 바인딩하여 코딩을 깔끔하게, null safety하게, 그리고 build speedy하게 하는 것이 ViewBinding을 사용하는 목적이라 할 수 있겠습니다.
그렇다면 이번에는 DataBinding에 대해 알아보겠습니다.
Databinding은 기본적으로 viewBinding의 기능을 포함하고 있다고 합니다.이미지 검색을 해보면 이런 이미지들이 많이 보임.
DataBinding의 경우는 ViewBinding보다 몇개의 세팅을 더 해주어야 하는데 바로 코드 작성해 보겠습니다.
<activity_main.xml>
<?xml version="1.0" encoding="utf-8"?>
<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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="test" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
아까 위의 기본 activity_main.xml 에서 <layout></layout>
항목을 한번 둘러 싼 형태로 바꿔줘야 합니다. DataBinding이 해당 레이아웃을 인식하기 위해선 꼭 저 layout으로 둘러쌓여 있어야 합니다.
<MainActivity.kt>
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 1. viewBinding 처럼 바인딩 클래스 명시적으로 선언
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 2. DataBindingUtil을 이용한 layout명 선언
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.tvHello.text = "hello world!"
}
}
binding 객체를 정의 할 때는 두가지 방법이 있습니다.
맨 아랫줄 바인딩 객체를 통해 viewBinding과 똑같이 view 정보를 가져와서 text값을 변경하는 작업이 가능합니다.
하지만 dataBinding은 viewBinding 보다 더 추가적인 기능이 있는데, xml에 view 정보값을 변경하는 기능입니다. liveData를 같이 사용하는 내용이 있지만 우선 그 부분은 제외하고 dataBinding에 집중해 보도록 하겠습니다.
아까 <laout></layout>
으로 감싼 레이아웃에 <data></data>
항목을 추가합니다.
이 data 필드는 여러 import문을 쓸 수도 있고 사용할 가변변수 값도 미리 지정할 수 있습니다.
저는 여기에 String 변수를 추가 했습니다. (activity도 올 수 있고 viewModel도 올 수 있음)
<activity_main.xml>
<?xml version="1.0" encoding="utf-8"?>
<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="customStr"
type="String" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{customStr.toString()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="test" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
그리고 textView 정의 값 중에 android:text="@{data.toString()}"
항목을 새로 추가하였는데 이 내용이 dataBinding의 추가적인 기능입니다. 클래스 코드 상에서 추가적으로 setText를 할 필요 없이 값이 바뀌면 자동적으로 String 값을 TextView에 넣어주게 하는 기능입니다. (LiveData와 같이 사용 필요)
<MainActivity.kt>
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.customStr = "hello world"
}
}
위 코드 동작은 binding.customStr 변수에 값을 넣었고 값을 넣는 순간 참조하고 있던 TextView가 해당 값을 바로 받아서 뿌려주게 됩니다. 따로 view id 호출이 없었는데도 말이죠.
즉, 클래스 코드 상에 view id를 참조해서 사용하지 않아도 view 값을 변경하게 해주는 것이 dataBinding이라고 할 수 있습니다.
view 정보를 참조할 내용이 많을 때 xml에서 값이 변경되도록 되어 있다면, veiw Class에서의 간결함이 더 빛날 것 입니다.
이 글을 읽으면서 그럼 dataBinding에서 다 되는데 dataBinding만 쓰면 되는 것 아니냐 하는 의문이 들겁니다.
구글링을 해보면 아시겠지만, dataBinding은 viewBinding 보다 build 속도가 더 오래걸리기도 하고 layout에 추가적인 작업도 해주어야 하니 적절하게 맞는 방법을 찾아 두가지 방법 다 사용하는게 좋을 것으로 보입니다.
[결론]
참고사이트
https://yoon-dailylife.tistory.com/54
https://todaycode.tistory.com/29
https://todaycode.tistory.com/31?category=979455