View Binding 과 Data Binding

chance-up·2022년 1월 21일

Android Kotlin에서
1. 일반적으로 View를 제어하는 방법과,
2. 이를 보다 편리하게 해주는 View Binding을 사용하는 방법
3. 그리고 ViewModel과 함께 사용하면 효율이 좋은 DataBinding을 사용하는 방법에 대해 정리

0. 준비

아래와 같이 Button을 클릭할 때 EditText에 있는 내용을 TextView로 출력하는 기능을 3가지 방법으로 만들어보고자 한다.

1. 기초적인 제어 방법

아래와 같이 findViewById 메서드를 이용하여 View객체를 얻어온 후, 해당 View의 get/set메서드를 호출하여 제어하는 방법이다.

var textView = findViewById<TextView>(R.id.textView1)
textView.text = "this is TextView"

findViewById<Button>(R.id.button1).setOnClickListener{
    val editText : String = findViewById<EditText>(R.id.editText1).text.toString()
    findViewById<TextView>(R.id.textView1).text = editText
}
    

여기서 이렇게 매 번 findViewById 메서드를 호출하는 것이
1. 매 번 작성해야 하는 번거로움이 있고,
2. View 내부를 전체 순회하여 해당 View를 Find하기 때문에 속도가 느리며,
3. Null Safe 하지 못하다는 단점이 있다.

kotlin-android-extension(synthetic)

이후 kotlin-android-extension 이라는 기능이 추가되어, 아래와 같이 gradle에 plugin을 추가하고, MainActivity에서 이를 import 하면 findViewById를 사용하지 않아도 되었다. 내부 캐싱을 통한 재사용성도 높아졌다고 한다.

build.gradle

plugins {
    id 'kotlin-android-extensions'
}

Activity

import kotlinx.android.synthetic.main.activity_main.*
...
textView1.text = "this is TextView"

하지만 이 또한 아래와 같은 단점으로 코틀린 버전 1.4.20부터 지원이 중단되었다.

  1. null-safe가 제한적으로 적용되며,
  2. 같은 ID를 가진 다른 레이아웃을 참조할 수도 있고,
  3. kotlin에서만 사용할 수 있다.

위 단점들을 보완하고자 나온것들이 ViewBinding과 DataBinding이다.
구글에서도 이 2가지를 활용하여 개발하도록 권장하고 있다.
각각의 특징을 알아보자.

2. ViewBinding을 통해 제어하기

ViewBinding은 Android Studio 3.6부터 사용 가능

build.gradle

android {
    ...
    
    // Binding 설정
    buildFeatures{
        viewBinding = true
    }
}

이렇게 적용할 경우, XML파일에 대한 객체가 생성된다.
(ex. activity_main.xml -> ActivityMainBinding객체)

만약 XML 파일에 tools:viewBindingIgnore="true" 을 사용하면 해당 XML의 바인딩 클래스가 생성되지 않는다.

MainActivity


private lateinit var binding : ActivityMainBinding
...

override fun onCreate(savedInstanceState: Bundle?) {
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
        
    binding.textView1.text = "this is TextView"

    binding.button1.setOnClickListener{
        binding.textView1.text =binding.editText1.text
        }
    }
}

위의 [기초적인 제어 방법] 코드와 비교했을 때, 사용 방법이 보다 명확해지고 간결해진 것을 볼 수 있다.

현재 코드는 3개 정도의 View만 있기 때문에 코드량의 차이가 많이 없어보일 수 있으나, 더 많아졌을 경우 차이를 체감할 수 있다.

또한 xml과 binding객체가 1:1매칭되므로 kotlin-android-extension 에서 우려되었던 같은 ID를 가진 다른 레이아웃을 참조할 가능성이 없어진다.

3. DataBinding을 통해 제어하기(MVVM패턴 사용)

DataBinding은 Android Studio 3.6부터 사용 가능

build.gradle

android {
    ...
    
    // Binding 설정
    buildFeatures{
        dataBinding = true
    }
}

DataBinding또한 ViewBindig과 동일하게 xml에 대한 Binding객체가 생성된다.
따라서 ViewBinding처럼 사용할 수도 있지만, 이렇게 사용한다면 DataBinding의 특징을 사용한 것이라고 볼 수 없다.

DataBinding의 특징을 좀 더 잘 살려보자.
activity_main.xml을 아래와 같이 layout태그로 감싸주고, data 태그 선언 후 안에 variable태그를 선언한다.

이후 ViewModel객체를 variable로 넣어준다.

주의해서 봐야 할 점은, EditText에는 @={activity.text2} 와 같이 적혀있는 것을 볼 수 있다.
이는 Fragment -> XML 방향으로만 바인딩 하는 것이 아니라 Fragment <-> XML 과 같이 양방향 바인딩을 한다는 말이다.(DataBinding의 장점)

fragment_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="vm"
            type="com.example.viewcontrolbindingtest.viewmodel.MainViewModel" />
    </data>

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

        <TextView
            ...
            android:text="@{vm.text1}"
            ... />

        <EditText
            ...
            android:text="@={vm.text2}"
            ... />
            
        <Button
            ...
            android:onClick="@{() -> vm.onSaveClick()}"
            ... />
           
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

ViewModel

class MainViewModel : ViewModel() {
    private val _text1 = MutableLiveData<String>()
    val text1 : MutableLiveData<String> = _text1
    ...
    
    fun onSaveClick(){
    ...
    }
}

Fragment를 Create하는 부분에서 binding객체에 ViewModel을 연결해준다. 이를 통해 MVVM구조를 만들 수 있다.

연결 된 이후에는 ViewModel에 있는 변수,메서드를 XML에서 자유롭게 사용할 수 있다. 또한 lifecycleOwner를 해당 Fragment로 등록해줌으로써 ViewModel의 데이터가 변경될 때 View를 갱신시켜준다.

Fragment

private val viewModel: MainViewModel by viewModels()
private lateinit var binding : MainFragmentBinding

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    binding = MainFragmentBinding.inflate(inflater,container,false).apply {
        vm = viewModel
        lifecycleOwner = this@MainFragment
    }
    return binding.root
}

여기까지 왔다면 View를 제어하는 3가지 방법에 대한 감이 올 것이다.
DataBinding은 위와 같이 단독으로 사용될 때 보다는
Data class, ViewModel등과 같이 사용되어 MVVM패턴을 구축할 때 효율적이다.

profile
FullStack Developer

0개의 댓글