! 기존에 xml에 작성했던 뷰들을 코틀린 파일에서 조작할 때는 세미나에서 배웠던 viewBinding을 사용했었다. 이것도 findviewById에 비해 편했지만 MVVM 아키텍처를 위해 저번에 ViewModel에 이어서 dataBinding을 정리했다
지금까지 내가 사용해왔던 ViewBinding의 경우 뷰에 텍스트를 띄우기 위해서 코틀린 파일에서 아래와 같이 작성해야 했다.
binding.tvHello = viewModel.hello
하지만 DataBinding의 경우에는 코틀린 파일이 아닌 xml에서 다음과 같이 바로 데이터를 연결시켜 뷰에 띄울 수 있다.
<TextView
android:text = "@{viewmodel.hello}"
즉, DataBinding이란 데이터와 뷰를 연결하는 작업을 XML에서 처리하는 기술
이런 방식으로 xml에 코드를 넣을 경우 액티비티에는 로직을 위한 코드만 남게 되어 더욱 코드가 간결해질 수 있다.
stackoverflow - Android : Difference between DataBinding and ViewBinding
에 있는 내용을 참고해서 작성했다.
그렇다면 ViewBinding 하고 DataBinding 중에 어떤 상황에 뭘 쓰면 좋을까? 무조건 DataBinding이 좋을까?
No!!!
왜 그럴까? 밑에서 알아보도록 하자
ViewBinding과 DataBinding의 관계는 위의 그림과 같다.
ViewBinding이 DataBinding에 속해 있다는 것은 DataBinding이 ViewBinding의 역할을 그대로 수행할 수 있음을 보여준다.
추가로 양방향 결합도 가능하고 xml 파일의 변수도 사용한다.
ViewBinding의 경우 DataBinding과 비교했을 때 간단하고 용량이 작기 때문에 빌드 속도에서 DataBinding에 비해 빠르기 때문에 상황에 맞게 적절히 사용하는 것이 중요하다.
DataBinding을 사용하기 앞서 ViewBinding을 사용했던 것처럼 build.gradle파일에 추가해줘야할 것이 있다.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
위와 같이 plugin에 'kotlin-kapt'를 넣어줘야 하고,
android {
buildFeatures {
dataBinding = true
}
}
다음과 같이 dataBinding = true를 넣어줘야 한다.
DataBinding을 사용할 때는 기존에 XML코드를 작성할 때와는 다르게 작성해 줘야 한다.
아래 코드와 같이 < layout > 태그를 가장 바깥에 작성하고, 그 안에 < data > 태그를 작성하고, 그 밑에 원래 우리가 적었던 코드를 작성한다.
<?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="vm"
type="com.htk.practicemvvm.NumberViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{Integer.toString(vm.currentValue)}"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="더하기"
android:onClick="@{() -> vm.plusValue()}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.195"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_num" />
<Button
android:id="@+id/btn_minus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginEnd="96dp"
android:text="뺴기"
android:onClick="@{() -> vm.minusValue()}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_num" />
</androidx.constraintlayout.widget.ConstraintLayout>
< data > 태그 안에 있는 < variable > 태그 안에는 name과 type이 있는데 type의 경우 데이터가 있는 경로 여기서는 NumberViewModel의 경로를 작성해줬고, name에는 내가 사용할 이름을 작성했다.
우리는 vm을 통해 NumberViewModel의 데이터에 접근이 가능하게 된다.
변수를 사용할 시에는 "@{ }"안에 넣어주면 되는데, 이 코드에서는 정수 값을 문자로 변환하기 위해 "@{Integer.toString(vm.currentValue)}"로 표시했다.
Button의 onClick에 "@{() -> vm.plusValue()}"와 같이 람다식이 있는데 이는 NumberViewModel에 fun plusValue()를 호출한다고 생각하면 된다.
다음과 같이 NumberViewModel 코드를 작성했다.
class NumberViewModel : ViewModel() {
// Mutable 라이브 데이터 - 수정 가능한 데이터
// 라이브 데이터 - 값이 변경 안됨
// 내부에서 설정하는 자료형은 뮤터블로
// 변경가능하도록 설정
private val _currentValue = MutableLiveData<Int>()
val currentValue: LiveData<Int>
get() = _currentValue
//초기값 설정
init {
Log.d(TAG, "myNumberViewModel - 생성자 호출")
_currentValue.value = 0
}
fun plusValue() {
_currentValue.value = _currentValue.value?.plus(1)
}
fun minusValue() {
_currentValue.value = _currentValue.value?.minus(1)
}
}
class MainActivity : AppCompatActivity() {
private lateinit var numberViewModel : NumberViewModel
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
// 뷰 모델 프로바이더를 통해 뷰모델 가져오기
// 라이프사이클을 가지고 있는 녀석을 넣어줄 즉 자기 자신
// 우리가 가져오고 싶은 뷰모델 클래스를 넣어서 뷰모델을 가져오기
numberViewModel = ViewModelProvider(this).get(NumberViewModel::class.java)
binding.vm = numberViewModel
binding.lifecycleOwner = this
binding.tvNum
}
}
XML 및 ViewModel을 작성 했으면 이제 MainActivity와 XML을 연결시키기만 하면 된다.
여기서는 ViewBinding과 다르게 binding class를 인스턴스화 하는 과정에서 DataBindingUtil 클래스 안에 setContetView() 메소드를 activity_main XML 파일로 설정한다.
다음으로 ViewModel을 XML에 바인딩 시키고 lifecycleOwner를 통해 수명주기 관리를 설정해주면 알아서 UI의 변경된 값을 관찰하여 실시간으로 변동이 일어나게 된다.
끝!!