Databinding 특징 세가지
1) Remove findviewById
2) Custom Binding Adapter
3) Two-way DataBinding
- xml 에 데이터를 바인딩하여 불필요한 코드를 줄이는 방법으로, 보통 MVVM 패턴을 구현 할 때 사용된다.
<Databinding 과정>
1. 먼저 gradle에 아래와 같이 추가해준다.
buildFeatures.dataBinding true
2. xml으로 이동하여 ShowContextActions - convert to layout databinding을 눌러준다.
3. 이후 XML에 viewmodel을 구독하도록 data추가
4.MainViewXml
<?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="viewmodel"
type="com.example.databindingrepositoryapp.MyViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ProgressBar
android:id="@+id/progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="@{100}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.3"
app:progressScaled="@{viewmodel.liveCounter}" />
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:checked="@={viewmodel.hasChecked}"
android:text="CheckBox"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button"
app:layout_goneMarginTop="30dp" />
<TextView
android:id="@+id/hasCheckedCheckbox"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@id/checkbox"
android:layout_width="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="@{viewmodel.hasChecked.toString()}"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/numberTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewmodel.modifiedCounter.toString()}"
app:layout_constraintBottom_toTopOf="@id/button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/progressbar" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:backgroundTint="@color/teal_200"
android:text="클릭"
android:textColor="@color/black"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/numberTextView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
5. MainActivity
package com.example.databindingrepositoryapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.databinding.DataBindingUtil
import com.example.databindingrepositoryapp.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val factory = MyViewModelFactory(10, this)
val myViewModel : MyViewModel by viewModels<MyViewModel>() { factory }
binding.lifecycleOwner = this
binding.viewmodel = myViewModel
binding.button.setOnClickListener {
myViewModel.liveCounter.value = myViewModel.liveCounter.value?.plus(1)
}
myViewModel.liveCounter.observe(this) {
counter -> binding.numberTextView.text = counter.toString()
}
myViewModel.modifiedCounter.observe(this){
counter -> binding.numberTextView.text = counter
}
}
}
6.BindingAdapter (DataBinding을 위한 어댑터 설정)
package com.example.databindingrepositoryapp
import android.widget.ProgressBar
import androidx.databinding.BindingAdapter
@BindingAdapter(value =["app:progressScaled", "android:max"], requireAll = true)
fun setProgress(progressBar: ProgressBar, counter:Int, max: Int){
progressBar.progress = (counter * 2).coerceAtMost(max)
}
7. MainViewModel
package com.example.databindingrepositoryapp
import androidx.lifecycle.*
class MyViewModel(
_counter : Int,
private val savedStateHandle: SavedStateHandle,
) : ViewModel(){
var counter : Int = savedStateHandle.get<Int>(SAVE_STATE_KEY) ?: _counter
val liveCounter : MutableLiveData<Int> = MutableLiveData(_counter)
val modifiedCounter : LiveData<String> = Transformations.map(liveCounter) {
counter -> "${counter} 입니다"
}
val hasChecked : MutableLiveData<Boolean> = MutableLiveData<Boolean>(false)
fun saveState(){
savedStateHandle.set(SAVE_STATE_KEY, counter)
}
companion object{
private const val SAVE_STATE_KEY = "counter"
}
}
8. MainViewModelFactory
package com.example.databindingrepositoryapp
import android.os.Bundle
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.savedstate.SavedStateRegistryOwner
//
//@Suppress("UNCHECKED_CAST")
//class MyViewModelFactory(private val counter : Int) : ViewModelProvider.Factory {
// override fun <T : ViewModel> create(key:String, modelClass: Class<T>, handle : SavedStateHandle): T {
// }
//}
class MyViewModelFactory(
private val counter : Int,
owner : SavedStateRegistryOwner,
defaultArgs: Bundle?= null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs){
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
if(modelClass.isAssignableFrom(MyViewModel::class.java)){
return MyViewModel(counter, handle) as T
}
throw IllegalAccessException("ViewModel Class not Found")
}
}
결과
'안드로이드 > 정리(Android)' 카테고리의 다른 글
Android) WorkManager 워크매니저 (2) | 2022.09.11 |
---|---|
안드로이드 HTTP 통신 (2) | 2022.09.09 |
Android - Hilt(의존성주입) (0) | 2022.08.15 |
[Android] ViewModel 알아보기 (0) | 2022.08.04 |
Android - CustomView 만들기 (0) | 2022.05.12 |