Data Binding
의 개념은 안드로이드 디자인 패턴 중 MVVM
패턴에서 같이 언급 되었다.
View에서 ViewModel을 관찰하면서 변경된 데이터를 UI에 결합해 줄 때 사용되는 것이 데이터 바인딩 이다.
findViewById<TextView>(R.id.sample_text).apply {
text = viewModel.userName
}
안드로이드에서는 위 코드처럼 Activiy
, Fragment
와 같은 UI컴포넌트에서 레이아웃을 연결해주고 레이아웃 안의 View의 id
값을 찾아주어야 했다.
하지만 dataBinding을 사용하면 다른 선언 없이 layout에서 바로 데이터를 결합하여 사용할 수 있기 때문에 코드의 수가 확! 줄어든다.
puls, minus , reset 버튼을 눌렀을 때 text의 값이 변하는 예제이며
DataBinding
과 함께ViewModel
,LiveData
를 함께 쓰고 있다.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt' // 추가!
}
android {
...
dataBinding {
enabled = true
}
}
앱 모듈의 build.gradle
에 pluginandroid-kapt
추가,
dataBinding
요소 추가하기
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_reset"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_minus"
app:layout_constraintTop_toTopOf="@+id/btn_minus"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/btn_minus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/btn_minus"
app:layout_constraintEnd_toStartOf="@+id/btn_reset"
app:layout_constraintStart_toEndOf="@+id/btn_plus"
app:layout_constraintTop_toBottomOf="@+id/tv_number" />
<Button
android:id="@+id/btn_plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_plus"
app:layout_constraintEnd_toStartOf="@+id/btn_minus"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/btn_minus" />
</androidx.constraintlayout.widget.ConstraintLayout>
ConstraintLayout
이 최상위 레이아웃이고 그 안에 Button
3개랑 TextView
가 있다.
<layout>
<data>
<variable
name="viewmodel"
type="com.example.mvvm2.viewmodel.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout > ...
</>
</layout>
우선 첫번째로 최상위 레이아웃을 Layout
으로 변경해준다.
그 아래 <data>
안에 <variable>
을 넣어주고 이 xml에서 사용할 변수가 있는 class를
타입과 함께 선언해준다.
LiveData와 함께 사용할 예정이기 때문에 ViewModel 클래스 안에 있는 변수들을 사용하기 위해 ViewModel을 선언해주었다.
<TextView
android:id="@+id/tv_number"
android:text="@{viewmodel.number.toString()}"
... />
<Button
android:id="@+id/btn_reset"
android:onClick="@{() -> viewmodel.resetNumber()}"
.../>
각각의 컴포넌트에는 들어갈 값을 명시해주어야 하는데 Databinding의 레이아웃 및 결합 표현식을 통해 레이아웃의 View와 데이터를 결합시켜준다.
@{}
구문을 사용하여 속성에 적절하게 사용되면 된다!
class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var mainViewModel : MainViewModel
private val btnPlus : Button by lazy { findViewById<Button>(R.id.btn_plus)}
private val btnMinus : Button by lazy { findViewById(R.id.btn_minus)}
private val btnReset : Button by lazy { findViewById(R.id.btn_reset) }
private val tvNumber : TextView by lazy { findViewById(R.id.tv_number)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var binding : ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
//ViewModel 객체 생성
mainViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MainViewModel::class.java)
//LiveData(number)에 ViewModel 연결
mainViewModel.number.observe(this, Observer {
tvNumber.text = it.toString()
})
btnPlus.setOnClickListener(this)
btnMinus.setOnClickListener(this)
btnReset.setOnClickListener(this)
}
override fun onClick(view: View?) {
when(view){
btnPlus -> mainViewModel.plusNumber()
btnMinus-> mainViewModel.minusNumber()
btnReset -> mainViewModel.resetNumber()
}
}
}
수정 전 코드를 살펴보면 findViewByID
로 xml의 레이아웃을 찾아서
Onclick
리스너를 상속받고 viewmodel안에 있는 함수를 호출하여 클릭 이벤트 처리를 해 주었다.
TextView를 업데이트 해주기 위해 TextView의 데이터를 viewModel의 LiveData를 관찰 하고 있는 코드가 필요했다.
class MainActivity : AppCompatActivity(){
private lateinit var mainViewModel : MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var binding : ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
//ViewModel 객체 생성
mainViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MainViewModel::class.java)
binding.viewmodel = mainViewModel
binding.lifecycleOwner = this
}
}
전보다 코드 수가 확! 줄었다.
findViewById
를 통해 컴포넌트를 연결할 필요가 없어졌고 viewModel안의 LiveData를 관찰할 필요도 없어졌다. 2번에서 LiveData를 바로 View에 결합시켜줬기 때문!!!!!!
binding.viewmodel = mainViewModel
2번 코드에서 <variable>
안에 설정해둔 name과 실제 class를 연결해줌으로써
viewModel을 직접 관찰할 필요도 없어졌다.
전체 코드는 여기에!
다음에는 BindingAdapter
와 RecyclerView
를 사용하는 방법을 정리해 볼 예정이다!