View 결합 기능을 사용하면 View와 상호작용하는 코드를 쉽게 작성할 수 있다. 모듈에서 사용 설정된 View 결합은 모듈에 있는 각 XML 레이아웃 파일의 결합 클래스를 생성한다. 바인딩 클래스의 인스턴스에는 상응하는 레이아웃에 ID가 있는 모든 View의 직접 참조가 포함된다.
대부분의 경우 뷰 결합이 findViewById를 대체합니다.
구글에선 이렇게 정의하고 있다.
findViewById를 어떻게 대체하는지 예제 코드를 통해 살펴보자
간단하게 버튼1~버튼3 중 하나를 터치했을 시 터치한 버튼 번호가 나오게 했다.
Toast 메시지를 띄우기 위한 코드를 보면
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button1 = findViewById<Button>(R.id.button1)
val button2 = findViewById<Button>(R.id.button2)
val button3 = findViewById<Button>(R.id.button3)
button1.setOnClickListener{
Toast.makeText(this, "버튼1", Toast.LENGTH_SHORT).show()
}
button2.setOnClickListener{
Toast.makeText(this, "버튼2", Toast.LENGTH_SHORT).show()
}
button3.setOnClickListener{
Toast.makeText(this, "버튼3", Toast.LENGTH_SHORT).show()
}
}
}
findViewById를 이용하면 버튼 하나하나 변수를 선언해 주어야 한다.
예제 코드는 세 개의 버튼이지만 xml 내에 여러 View 들이 추가될 수록 비효율적인 작업을 수행해야 하고 아마 onCreate() 함수는 findViewById로 도배될 것이다.
또한 같은 ID를 여러 레이아웃에 사용하면 엉뚱한 레이아웃이 임포트 되거나, 완전히 Null Safe 하지 못한 문제가 발생했다.
findViewById 같이 꼭 필요하고 간단하지만 반복적인 코드를 보일러 플레이트라고 한다.
이러한 보일러 플레이트를 없애기 위한 대안이 바로 안드로이드 젯팩에서 제공하는 ViewBinding이다.
build.gradle에 코드를 추가해서 설정을 적용함
android {
viewBinding {
enabled = true
}
}
작성 후 sync now 할 것
뷰 바인딩이 활성화되면, 우리가 작성하는 모든 xml 파일은 자동으로 각각 바인딩 클래스가 생성된다.
ex) activity_main.xml 파일을 생성하면 ActivityMainBinding 클래스가 생성
따라서 activity_main.xml 뷰를 활용할 때 ActivityMainBinding 객체를 이용하면 된다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 바인딩 클래스의 객체 생성
binding = ActivityMainBinding.inflate(layoutInflater)
// 바인딩 객체의 root view 설정
setContentView(binding.root)
binding.button1.setOnClickListener{
Toast.makeText(this, "버튼1", Toast.LENGTH_SHORT).show()
}
binding.button2.setOnClickListener{
Toast.makeText(this, "버튼2", Toast.LENGTH_SHORT).show()
}
binding.button3.setOnClickListener{
Toast.makeText(this, "버튼3", Toast.LENGTH_SHORT).show()
}
}
}
Root View는 모든 View들을 포함하는 View이다.
모든 바인딩 클래스는 Root View를 반환하는 getRoot() 함수를 가지고 있다.
바인딩 클래스에서 자동으로 Button형 멤버 변수로 선언되었기 때문에 액티비티에서 사용만 하면 된다.
위 코드와 같이 binding.View_ID 형식으로 본인이 설정한 View의 ID를 입력해주면 된다.
결과적으로 ViewBinding을 사용할 경우 불필요한 findViewById() 함수 호출이 없어지고 코드의 직관성이 더 좋아지게 된다.
※ TextView, Button, EditText 등 수십개의 View를 변수에 일일이 선언한다고 생각해보자
UI 요소와 데이터를 프로그램적 방식으로 연결하지 않고, 선언적 형식으로 결합할 수 있게 도와주는 라이브러리... 까지가 검색하면 많이 나오는 DataBinding의 정의이고
간단히 말하면 xml에 코드를 직접 집어 넣어서(선언) 해결하는 방법이다.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="first name"
android:textSize="20sp"/>
<TextView
android:id="@+id/last_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="last name"
android:textSize="20sp"/>
</LinearLayout>
기존에 우린 xml을 이렇게 작성하고
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var firstName = findViewById<TextView>(R.id.first_name)
var lastName = findViewById<TextView>(R.id.last_name)
val name = UserProfile("홍", "길동")
firstName.text = name.firstName
lastName.text = name.lastName
}
}
로 매인 액티비티를 구성하면 아래와 같은 결과값을 받을 수 있다.
하지만 이러한 과정 없이 DataBinding을 사용하면 TextView에 하나 하나씩 넣어줄 필요가 없다.
우선 build.gradle 파일에
android {
buildFeatures {
dataBinding = true
}
}
을 추가하고 sync now 해준다.
UserProfile 파일을 아래와 같이 정의해주고
data class UserProfile(
val firstName: String,
val lastName: String
)
xml파일을 아래와 같이 작성해준다.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="com.example.databindnigexam.UserProfile" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.firstName}"
android:textSize="20sp"/>
<TextView
android:id="@+id/last_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.lastName}"
android:textSize="20sp"/>
</LinearLayout>
</layout>
DataBindig을 사용하는 xml 리소스는 layout 태그로 시작하여야 한다.
그리고 data 태그에 사용할 변수를 정의한다. 위 경우는 user
TextView는 @{} 구문을 사용하여 표현식을 작성한다.
마지막으로 매인 액티비티는 아래와 같이 작성해준다.
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.user = UserProfile("김", "흰돌")
}
}
UserProfile 객체를 초기화하고 binding 객체에 넣어주기만 하면 아래와 같은 화면이 나온다.
이처럼 DataBinding을 사용하면 데이터를 UI에 띄우기 위해 필요한 코드를 최소화할 수 있다.
findViewById를 호출하지 않아도 되고, data가 바뀔 시 자동으로 View를 변경하게 할 수 있다.
또 xml 파일만으로도 View에 어떠한 데이터가 들어가는지 파악이 가능하고 결과적으로 코드의 가독성과 코드량에서 이득을 볼 수 있다.
DataBinding에서 ViewBinding의 기능도 가지고 있다.
하지만 무턱대고 DataBinding을 사용하기엔 DataBinding에도 단점이 존재한다.
클래스 파일이 많이 생길 수 밖에 없고, 빌드 속도 또한 느려지기 때문이다.
보통 DataBinding은 MVVM 패턴과 함께 사용을 많이 하고 있기 때문에
findViewById를 대체하기 위해서 사용하는 것이라면 ViewBinding을 사용하는게 효율이 좋다고 생각된다.