chacha's

[ Android ] Data binding 본문

Android/Concept

[ Android ] Data binding

Cha_Cha 2021. 4. 6. 17:50

목차

     Android Kotlin Fundamentals - Data binding basics 
     를 참고하여 작성한 게시글입니다.

     

     Data Binding

    Android는 activity나 fragment 코드에서 view를 참조하기 위해서 보통 findViewById를 사용하여 Id를 찾아야합니다. 이 경우 런타임(runtime)에 view 계층을 탐색하여 찾는 것입니다. 따라서 뷰 계층이 깊어질수록 찾는데 더 오랜 시간이 걸리므로 유저가 앱을 사용할 때 반응을 느리게 할 수 있는 요소가 됩니다. ( findViewById를 사용하면 view가 호출될 때마다 runtime에 view를 찾습니다. )

    DataBinding을 사용하면 이를 해결할 수 있습니다!!!

    DataBinding을 사용하는 경우 컴파일 타임(compile tiem)에 activity나 fragment와 layout을 연결할 수 있습니다. 컴파일러는 activity가 생성될 때 binding class라고 불리는 helper class와 binding class의 인스턴스의 생성합니다. 생성된 binding class를 통해 추가적인 오버헤드 없이 view에 접근할 수 있습니다.

     

     Data Binding 사용하기

    ▶ Data Binding and findViewById

    Get started with data binding
    Generated binding classes
    위의 Android Docs와 더불어 salix97님 블로그를 참조하였습니다.

    1. app/build.gradle 파일에 아래와 같은 코드 추가하기

    android {
        ...
        buildFeatures {
            dataBinding true
        }
    }

    2. xml의 layout을 <layout></layout> tag로 감싸주기

    3. Activity에서 binding 객체를 생성해주기 + data binding class를 import 해주기

    이때, binding class의 이름은 layout 파일 이름에서 파생됩니다. ex. activity_main.xml 이라는 이름의 파일이면 binding 객체 이름은 AcitivityMainBinding 입니다.

    class MainActivity : AppCompatActivity() {
        // binding 객체 선언하기
        private lateinit var binding: ActivityMainBinding

    4. setContentView를 binding 객체로 바꾸기 + data binding util을 import 해주기

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            // setContentView(R.layout.activity_main)
            // setContentView 대신 binding 객체 사용하기
            binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    
            findViewById<Button>(R.id.done_button).setOnClickListener {
                addNickname(it)
            }
            ...
        }

    위의 과정만으로는 data binding을 사용하는 것이 view binding을 사용하는 것보다 더 좋은지 알 수 없습니다. Data binding을 이용할 때 좋은 점은 view와 data를 묶을 수 있다는 것입니다.


    ▶ Data Binding Views and Data

    Layouts and binding expressions

    데이터를 업데이트하고 데이터가 보이는 view를 업데이트하는 과정은 복잡하고 에러의 원인이 되는 경우가 많습니다. 또한 데이터를 view 내부에 유지하는 것은 view와 data의 분리하는 설계원칙을 위반합니다. Data binding을 이용하면 이러한 문제를 해결하는데 도움이 됩니다. 

    Data Binding을 사용하면 컴파일러가 compile time에 view와 data를 묶는 binding 객체를 생성하고 실행 중 data가 업데이트되어서 view에 변화를 주어야 할 때 binding 객체를 통해서 이를 해결합니다.

    1. data class를 생성하기

    2. xml 파일에 <data> 블록을 추가하기 ( <data> 블록 안에 사용하고자 하는 data class를 <variable> tag를 사용하여 추가하기 )

    3. 만약 TextView에서 이를 사용하고자 한다면, 기존에 string resource을 참조하던 text를 variable에 대한 참조로 바꿔주기 (  @{}  구문을 사용하여 속성에서 작성됩니다. )

    1,2,3 의 과정

    4. Activity에서 MyName 인스턴스 생성

    class MainActivity: AppCompatActivity() {
    	private lateinit var binding: ActivityMainBinding
    	private val myName: MyName = MyName("Aleks Haecky")
    	...
    }

    5. onCreate()에서 binding 객체의 data source를 set

    binding.myName = myName

    6. runtime에 받아오는 값으로 TextView의 값을 변화하고 이를 갱신해주기

    myName?.nickname = nicknameEdit.text.toString()
    // Invalidate all binding expressions and request a new rebind to refresh UI
    invalidateAll()

     

     Data Binding은 왜 사용할까요?

    1. 데이터를 UI 요소에 연결하기 위해 필요한 코드를 최소화 할 수 있습니다. ( findViewById를 사용하지 않기때문에 코드가 간결해집니다. )
    2. RecyclerView에 각각의 item을 set 해주는 작업도 자동으로 진행됩니다.
    3. data가 바뀌면 자동으로 view를 변경하게 할 수 있습니다.
    4. xml resource만 보고도 view에 어떤 데이터가 사용되는지 파악이 가능합니다.
    5. MVP / MVVM 패턴을 구현하기 위해 유용하게 사용됩니다.

    하지만 무분별하게 사용하기에는 class 파일이 많이 생긴다는 점, 빌드 속도가 느려진다는 점 등의 단점도 존재하므로 적절하게 사용하는 것이 중요합니다.

     

     Two-way data binding

     고급 데이터 바인딩 - Goolge 개발자 콘퍼런스 2016 Youtube
     양방향 데이터 결합 - Doc 

    @={} 표기법은 속성에 대한 데이터 변경사항을 수신함과 동시에 사용자에게 이를 알립니다.

            <com.google.android.material.textfield.TextInputLayout
                ...>
                <com.google.android.material.textfield.TextInputEditText
                    ...
                    android:text="@={shoe.name}" />
            </com.google.android.material.textfield.TextInputLayout>

    📌 간단하게 나타내면...

    one-way -> "@{}"
    two-way -> "@={}

     

     EditText에서 Two-way data binding

     Two-way databinding in EditText - stack overflow

    데이터 바인딩에서 string 값이 아닌 다른 값을 적용하려고 할 때 문제가 발생하였습니다. 

    해결 방법 1

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/tf_shoe_size"
                ... >
    
                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/ef_shoe_size"
                    ...
                    android:text="@={``+shoe.size}" />
            </com.google.android.material.textfield.TextInputLayout>

    해결 방법 2 converter 이용하기

    <!-- fragment_shoe_details.xml -->
    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
      ...
      <data>
        <import type="com.example.android.Converter" />
        ...
      </data>
      <androidx.constraintlayout.widget.ConstraintLayout
        ...>
       ...
       <!-- need to do this for all EditText -->
       <EditText
           android:id="@+id/name"
           ...
           android:text="@={newShoe.name}" />
       ...
       <!-- saving non-string data using converter -->
       <EditText
           android:id="@+id/shoe_size"
           ...
           android:text="@={Converter.doubleToString(newShoe.size}" />
     ...
    </layout>
    import androidx.databinding.InverseMethod
    
    object Converter {
        @InverseMethod("stringToDouble")
        @JvmStatic
        fun doubleToString(value: Double?): String {
            return value?.toString() ?: ""
        }
        @JvmStatic
        fun stringToDouble(value: String): Double? {
            return if (value.isNotEmpty()) {
                value.toDouble()
            } else {
                0.0
            }
        }
    }

     

     CheckBox에서 Two-way data binding

     android databinding: how to avoid onCheckedChanged triggered by programmatically
     식별 가능한 데이터 객체 작업 - Doc 
        <CheckBox
            android:id="@+id/rememberMeCheckBox"
            android:checked="@{viewmodel.rememberMe}"
            android:onCheckedChanged="@{viewmodel.rememberMeChanged}"
        />

    위와 같이 단방향 데이터 결합을 사용하면 속성에 값을 설정하고 이 속성의 변경에 대응하는 리스너를 설정할 수 있습니다. 하지만 이를 양방향 데이터 결합을 이용하여 하나의 속성만 선언하는 것으로 줄일 수 있습니다.

        <CheckBox
            android:id="@+id/rememberMeCheckBox"
            android:checked="@={viewmodel.rememberMe}"
        />
        
        // val rememberMe = ObservableBoolean(false)

    이때, viewmodel.rememberMe은 도큐먼트에 나온 것과 같이 선언해도 되지만, 저는 stack overflow를 참고하여 ObservableBoolean 타입의 변수로 직접 선언하였습니다.

    End

    'Android > Concept' 카테고리의 다른 글

    [ Android ] ADB 사용하기  (0) 2021.04.27
    [ Android ] Android Lifecycle  (0) 2021.04.15
    [ Android ] Navigation  (0) 2021.04.09
    [ Android ] Constraint layout  (0) 2021.04.08
    [ Android ] View Binding  (0) 2021.03.24
    Comments