Android DataBinding Basic Sample

devvace ㅣ 2021. 1. 27. 18:41

DataBinding은 JetPack 아키텍처에 포함된 라이브러리이다.

DataBinding을 사용하면 선언적 형식으로 레이아웃의 UI구성 요소를 앱의 데이터와 결합할 수 있다.

대표적인 장점으로는 findViewById 메소드를 호출할 필요가 없어 보일러플레이트 코드를 방지할 수 있고, 앱 성능이 향상되며, 메모리 누수 및 NullPointerException을 방지할 수 있다.

간단한 샘플 앱 구현을 통해 DataBinding 을 살펴보자.

샘플 앱은 Add 버튼을 누르면 Count 값이 올라가 텍스트뷰에 실시간으로 표시되고, Reset 버튼을 누르면 Count 값이 초기화되는 예제이다.

DataBinding 설정하기

DataBinding은 Android Studio 버전은 1.5.0 이상이어야 하고,

Android 4.0(API 14 Level) 이상 안드로이드 기기에서 지원한다.

plugins {
	...
	id 'kotlin-kapt'
}

android {
	...
	dataBinding {
    enabled = true
	}
}
...

☝️ Kotlin을 사용하는 경우 kotlin-kapt 플러그인을 추가해야 한다.

Binding class 생성하기

레이아웃의 변수와 뷰를 참조할 수 있는 Binding class를 생성해야 한다.

생성하는 방법은, XML 레이아웃 파일에서 가장 상위 레이아웃을 <layout> 태그로 감싸면 클래스가 자동으로 생성된다.

생성되는 클래스 이름은 XML 레아아웃의 파일명을 파스칼 케이스로 변경한 뒤 접미어 Binding을 붙인다.

ex) activity_main.xml 을 표현하는 Binding class 이름은 ActivityMainBinding 이다.

<?xml version="1.0" encoding="utf-8"?>
<layout>
	<LinearLayout
			xmlns:android="http://schemas.android.com/apk/res/android"
			android:layout_width="match_parent"
			android:layout_height="match_parent">
			...
	</LinearLayout>
</layout>

Binding class로 Binding 객체 생성하기

바인딩 객체를 생성하자.

여러 방법이 있는데, 그 중 DataBindingUtil 클래스를 활용하면 setContentView 메소드를 대체할 수 있다.

class MainActivity : AppCompatActivity() {

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

☝️ 아까전에 <layout>으로 XML 레이아웃 파일을 감쌌기 때문에, 자동으로 ActivityMainBinding 이라는 Binding class가 생성되어 있다.

ViewModel 생성하기

버튼을 누르면 카운트 값이 갱신되고 리셋되는 기능을 구현해보자.

class MyViewModel {
    private var count: Int = 0
    private val txtCount = ObservableField<String>()

    init {
        updateTxtCount()
    }

    fun getTxtCount() : ObservableField<String> = txtCount

    private fun updateTxtCount() {
        txtCount.set("$count clicked!!")
    }

		// Reset 버튼을 눌렀을 때 동작한다.
    fun resetCount() {
        count = 0
        updateTxtCount()
    }

		// Add 버튼을 눌렀을 때 동작한다.
    fun addCount() {
        ++count
        updateTxtCount()
    }
}

여기서 짚고 넘어가야할 점은 텍스트뷰에 찍을 데이터를 ObservableField<String> 으로 선언했다는 것이다.

Observable 데이터 객체는 데이터의 변경 사항을 감지하고 알려주는 객체이다.

데이터 바인딩 라이브러리에는 Observable을 구현한 여러가지 클래스들을 제공하므로 직접 구현할 필요는 없을 것이다.

ViewModel binding

이제 생성한 ViewModel을 Binding class에 binding(?) 해야한다.

class MainActivity : AppCompatActivity() {

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

				binding.viewmodel = MyViewModel()
    }
}
 

XML 레이아웃에서 변수 선언하기

XML 레이아웃에 변수를 선언하고, 이 변수에 값을 대입하는 것으로 뷰 상태를 변경할 수 있다.

우리는 앞서 생성한 ViewModel의 필드를 사용할 것이기 때문에, ViewModel 변수를 선언한다.

<layout ...>
	<data>
	    <variable
	        name="viewmodel"
	        type="com.example.databindingsample.viewmodel.MyViewModel" />
	</data>
	...
</layout>

이벤트 처리하기

이제 각 View widget의 이벤트를 처리한다.

다음은 activity_main.xml의 소스코드이다.

<?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.databindingsample.viewmodel.MyViewModel" />
    </data>

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

        <TextView
            android:id="@+id/txt_count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="@{viewmodel.txtCount}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />


        <Button
            android:id="@+id/bt_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="40dp"
            android:text="버튼"
            android:onClick="@{() -> viewmodel.addCount()}"
            app:layout_constraintTop_toBottomOf="@+id/txt_count"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/bt_reset"/>

        <Button
            android:id="@+id/bt_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="40dp"
            android:text="리셋"
            android:onClick="@{() -> viewmodel.resetCount()}"
            app:layout_constraintTop_toBottomOf="@+id/txt_count"
            app:layout_constraintStart_toEndOf="@+id/bt_add"
            app:layout_constraintEnd_toEndOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

☝️ android:onClick에서 버튼 이벤트를 실행하는 방법을 리스너 바인딩이라고 한다.

이벤트를 다루는 방법에는 메소드 참조리스너 바인딩 두 가지 방법이 있다.

결과