DataBinding & LiveData

김명진·2021년 1월 3일
1

안드로이드

목록 보기
4/25
post-custom-banner

💡DataBinding


DataBinding(데이터바인딩)은 간단하게 xml파일에 Data를 연결(binding)해서 사용할 수 있게 도와주며 Android JetPack 라이브러리의 하나의 기능 입니다.

즉, 데이터바인딩은 애플리케이션 로직과 레이아웃을 binding하는 데 필요한 글루 코드를 최소화합니다.

  • findViewById를 사용하지 않아도 되며 보통 MVVM 패턴을 구현 할 때 "LiveData"와 함께 거의 필수적으로 사용합니다.

세팅방법:

MVVM AAC Databinding 사용법(1.세팅편)

데이터 바인딩을 위해서는 최소 3 영역에 대한 수정이 필요하다
1. (뷰 모델을 이해하도록) 레이아웃
2. (데이터 바인딩 레이아웃을 사용하도록) View에서 레이아웃을 초기화하는 코드
(내부 데이터와 동기화될 수 있는 객체인) 뷰 모델

📌 레이아웃

이 최상위 요소로 추가되어야한다. (gradle에서 databinding을 활성화 해야한다)

<layout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto">
        <data>
            <variable
                name="viewmodel"
                type="com.myapp.data.ViewModel" />
        </data>
        <ConstraintLayout... /> <!-- UI layout's root element -->
    </layout>

📌 뷰 모델 객체

엑티비티에서 데이터 바인딩을 할 레이아웃을 사용하기 위해 초기화하는(inflate) 방식도 바꿔야한다.

//기존은 setContentView 메서드를 onCreate에서 호출하는 형태입니다.
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
}

//데이터 바인딩을 사용할때는 다른 형식으로 바꿔줘야한다. 
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding = DatabindingUtil.setContentView(this,
																						R.layout.activity_main)
}

ActivityMainBinding은 R.layout.activity_main으로부터 자동 생성이 되는 파일이다. activity_main이었던 이름을 ActivityMain의 형태로 바꾸고 뒤에 Binding을 붙여 객체를 생성한다.

  • ( _ ) 언더바 형식은 스네이크 케이스
  • 첫 글자와 중간 글자들이 대문자인 경우를 파스칼 케이스

뷰 모델 객체 생성

inner class ViewModel{
	var title = ObservableField<String>()
}

ObservableField는 값이 바뀌면 연동된 DataBinding에게 정보를 전달하고 이를 이용해 UI를 업데이트할 수 있다.

레이아웃에서 뷰 모델을 참조하기 위해서는 변수를 만들어야한다. 아래 를 넣고 그 아리 변수들을 의 형태로 넣는다. variable은 두 가지 속성을 가지는데 name 과 type을 갖고있다.(위 레이아웃 코드 참조)

그리고 레이아웃이 참조하는 변수는 자동으로 대입되지 않기 때문에 MainActivity (데이터바인딩을 할 Activity)의 onCreate으로 돌아와 연결시켜야한다.

override fun onCreate(savedInstanceState: Bundle?) {
  	super.onCreate(savedInstanceState)
  	val binding: ActivityMainBinding = DatabindingUtil.setContentView(this,R.layout.activity_main)
	binding.viewModel = ViewModel()

}

뷰 모델 데이터를 조금 더 자유자재로 변경하기 위해서는 뷰 모델을 액티비니틔 필드로 바꿀 수 있다.

private val viewModel: ViewModel by lazy{
	ViewModel()
}

override fun onCreate(savedInstanceState: Bundle?) {
  	super.onCreate(savedInstanceState)
  	val binding: ActivityMainBinding = DatabindingUtil.setContentView(this,R.layout.activity_main)
  	binding.viewModel = viewModel

}

여기서 lazy {코드}은 해당 변수/필드가 실제로 사용될 때 코드 블록의 내용으로 초기화를 진행한다. viewModel이란 필드에 실제로 접근하게 될때 lazy {ViewModel()} 의 코드 블록인 viewModel()이 호출되어 그 값이 사용된다.

lazy 블록은 쵝화 시점과 상관없이 필드의 정의에 초기화 코드를 보여줄 수 있어 인식하기 쉽다.

뷰 모델에 값 설정하기

binding.viewModel = viewModel
viewModel.title.set("Hello World")

직관적으로 문자열 viewModel.title = "Hello World"로 대입할 것처럼 보이지만 String 타입이 아닌 ObservableField 타입이기 때문에 set 메서드로 설정해야 한다.

📌 뷰 모델 필드를 뷰에 연동하기

뷰 모댈에 넣은 Hello World 글자를 화면에 출력하기

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{viewModel.title}"
        android:onClick="sayHello"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

뷰 모델 값을 대입하기 위해 @{}를 사용한다.

또한 onClick 핸들러에 등록하면 액티비니에 이벤트를 처리할 메서드를 추가해 사용할 수 있다.

fun sayHello(view: View){
	Toast.makeText(this,"Hello".Toast.LENGTH_SHORT).show()
}

📌 뷰 모델에서 이벤트 처리

이벤트 핸들러가 액티비티에 있을 필요는 없다. 즉 이벤트 핸들러를 뷰 모델로 옮길 수 있다.

inner class ViewModel{
	private var count = 0
	var title: ObservableField<String> = ObservableField()

	fun sayHello(){
		count++
		title.set("안녕하세요" + count + "번째 클릭입니다.")
	}
}
<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{viewModel.title}"
        android:onClick="@{()->viewModel.sayHello()}"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

💡LiveData


LiveData는 Data의 변경을 관찰 할 수 있는 Data Holder 클래스 입니다.

일반적인 Observable과는 다르게 LiveData는 안드로이드 생명주기(LifeCycle)를 알고 있습니다. (Lifecycle-Aware)

즉, 액티비티나, 프레그먼트, 서비스 등과 같은 안드로이드 컴포넌트의 생명주기(Lifecycle)를 인식하며 그에따라

LiveData는 활성상태(active)일때만 데이터를 업데이트(Update) 합니다.

활성상태란 STARTED 또는 RESUMED 를 의미합니다.

또한 LiveData 객체는 Observer 객체와 함께 사용됩니다. LiveData가 가지고 있는 데이터에 어떠한 변화가 일어날 경우, LiveData는 등록된 Observer 객체에 변화를 알려주고, Observer의 onChanged() 메소드가 실행되게 됩니다.

LiveData is an observable data holder.

This allows the components in your app to be able to observe LiveData objects for changes without creating explicit and rigid dependency paths between them.

//1
val liveData: MutableLiveData<Any>()

//2
liveData.value = "Hello"

//3
liveData.observe(this, Observer {
//UI update
})
  1. liveData 초기화 작업
  2. liveData는 val 변경이 안되는 변수로 선언이 되었지만 liveData는 MutableLiveData이기 때문에 value 는 변경이 가능하다. liveData의 value가 변경이 되면 모든 활성화 되어있는 해당 observer로 신호가 간다.
  3. liveData는 Activity 나 fragment안에서 항상 observed 되고있다. observer안에서 it parameter로 최근에 변경된 value를 받아올수있다.

예제:

class TestLiveDataViewModel : ViewModel() {
    // String 타입의 MutableLiveData 생성, by lazy로 초기화는 뒤에
    val textValue: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }
}
class MainActivity : AppCompatActivity() {
    // 전역 변수로 ViewModel lateinit 세팅
    private lateinit var model: TestLiveDataViewModel
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
        // ViewModel을 가져옵니다.
        model = ViewModelProvider(this).get(TestLiveDataViewModel::class.java)
 
        // Observer를 생성한 뒤 UI에 업데이트 시켜 줍니다.
        val testObserver = Observer<String> { textValue ->
            // 현재 MainActivity에는 TextView가 하나만 존재합니다.
            // 다른 데이터를 받는 UI 컴포넌트가 있다면 같이 세팅 해줍니다.
            tv_livedata_test.text = textValue
        }
 
        // LiveData를 Observer를 이용해 관찰하고
        // 현재 Activity 및 Observer를 LifecycleOwner로 전달합니다.
        model.textValue.observe(this, testObserver)
    }
}

📌 예제:

화면에서 버튼을 클릭하면 viewModel로 연결되어 counter의 값을 올려주고 그것을 MainActivity에서 observe해서 UI를 갱신하는 샘플

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        val viewModel = ViewModelProvider(this)[MainViewModel::class.java]
        binding.viewModel = viewModel
        binding.lifecycleOwner = this

        viewModel.counter.observe(this, Observer {
            counter -> binding.text.text = counter.toString()
        })
        
    }
}
class MainViewModel: ViewModel() {
    var counter : MutableLiveData<Int> = MutableLiveData()

		//counter 초기화
    init {counter.value = 0}
    
    fun clickBtn(){
        counter.value = counter.value?.plus(1)
        println(counter.value)
    }

}
<?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.mvvmlivedata.MainViewModel" />

    </data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="click"
        android:onClick="@{()->viewModel.clickBtn()}"/>

</androidx.constraintlayout.widget.ConstraintLayout>

</layout>
profile
꿈꾸는 개발자
post-custom-banner

0개의 댓글