본문 바로가기
Programando/Android

[Android/Kotlin] 양방향 바인딩(Two-way DataBinding)

0. 종속성 추가

모듈에서 dataBinding을 enable하기 위해서 app 모듈의 build.gradle의 build option을 true로 설정합니다. 그리고 @Bindable을 사용하기 위해 pluginskotlin-kapt를 추가합니다.

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

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

 

1. DataBinding에 대한 기본적인 설명

- [Android/Kotlin] DataBinding

 

2. Binding Expressions

@={}와 같이 사용하면 Two-way Binding, @{}와 같이 사용하면 One-way Binding이라고 합니다. 여기서는 EditText의 text 속성을 Two-way Binding으로 설정하고, Button의 onClick 속성을 리스너 결합 방식으로 이벤트를 처리하도록 합니다.

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="model"
            type="com.naram.weatherproject.ui.main.TextModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout 
        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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginVertical="5dp"
        android:background="@drawable/item_background"
        android:padding="10dp">

        <LinearLayout
            android:id="@+id/llSearchBar"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:orientation="horizontal"
            android:weightSum="5"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0">

            <EditText
                android:id="@+id/etSearchCity"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginEnd="5dp"
                android:layout_weight="1.2"
                android:fontFamily="@font/nsr_regular"
                android:hint="원하는 도시를 검색해보세요."
                android:singleLine="true"
                android:text="@={model.etSearchCity}"
                android:textSize="15sp" />

            <Button
                android:id="@+id/btnSearch"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="5dp"
                android:layout_weight="3.8"
                android:fontFamily="@font/nsr_bold"
                android:onClick="@{() -> model.clickSearchButton()}"
                android:text="검색"
                android:textSize="15sp" />

        </LinearLayout>

        <TextView
            android:id="@+id/tvCity"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:fontFamily="@font/nsr_extrabold"
            android:text="@={model.tvCity}"
            android:textSize="30sp"
            app:layout_constraintBottom_toTopOf="@id/tvTemperature"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            tools:text="CITY" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

반응형

 

3-1. Observable POJO Class 생성

먼저, BaseObservable을 상속받아야 합니다. 그 후에 Two-way Binding을 할 변수의 Getter와 Setter를 선언합니다. 이때 Getter의 위에 @Bindable을 추가하고, Build를 하면, BR이라는 클래스 안에 값들이 들어가게 됩니다. 그리고 Setter에는 값이 변경되었을 때 알려줄 수 있도록 notifyPropertyChanged()를 추가하고, 매개변수로 BR.text와 같이 넘겨줍니다.

그리고 버튼이 클릭되면 text가 변경되도록 아래 onClickButton에서 setText()로 String을 넘겨줍니다.

  • TextModel.kt
class TextModel : BaseObservable() {

    private var etSearchCity: String? = ""
    private var tvCity: String? = ""

    fun setEtSearchCity(etSearchCity: String) {
        this.etSearchCity = etSearchCity
        notifyPropertyChanged(BR.etSearchCity)
    }

    @Bindable
    fun getEtSearchCity(): String? {
        return etSearchCity
    }
    
    fun setTvCity(tvCity: String) {
        this.tvCity = tvCity
        notifyPropertyChanged(BR.tvCity)
    }
    
    @Bindable
    fun getTvCity(): String? {
        return TvCity
    }
    
    fun onClickButton() {
        getEtSearchCity()?.let {
            setTvCity(it)
        }
    }

}
  • MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.setVariable(BR.model, TextModel())

    }

}

 

3-2. ObservableField 사용

ObservableField는 Observable 인터페이스를 구현하지 않고도 속성을 notify할 수 있는 수단을 제공합니다. set(), get() 접근자 메서드를 사용하며, 값이 변경되면 자동으로 View에 notify하기 때문에 UI를 업데이트할 수 있습니다.

  • TextModel.kt
class TextModel {
    var etSearchCity: ObservableField<String> = ObservableField("")
    var tvCity: ObservableField<String> = ObservableField("")
    
    fun onClickButton() {
        etSearchCity.get()?.let {
            tvCity.set(it)
        }
    }
}
  • MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.setVariable(BR.model, TextModel())

    }

}
반응형