4 분 소요

이전의 포스팅은 ViewModel에 LiveData를 추가하였었다.😀 안드로이드 Jetpack의 LiveData 적용하기 이번에는 ACC 구성요소의 DataBinding 을 LiveData와 함께 사용하여 프로젝트를 더 개선된 형태로 만들어 보려고 한다.


DataBinding(데이터 바인딩)

데이터 바인딩 의 주목적은 UI 레이아웃의 뷰(xml)와 앱 코드에 저장된 데이터(주요사용->ViewModel 인스턴스)와 연결 하는 간단한 방법 을 제공하는 것이다.

📍 데이터 바인딩을 통하여 불필요한 코드를 줄여줄수 있다.

데이터 바인딩은 LiveData 컴포넌트와 같이 사용될 때 특히 좋다. LiveData 컴포넌트는 실시간으로 데이터의 변경을 감시하기 때문에 데이터 바인딩 을 사용하여 UI와 앱 코드와 연결을 하게되면 데이터가 자동으로 LiveData 값을 변경할 수도 있다. 데이터 바인딩은 초기의 설정 외에 개발자가 추가로 작성할 코드가 필요 없다는 것도 큰 장점이다.

데이터 바인딩 사용하기

데이터 바인딩을 사용하기 위하 초기 작업은 다음과 같다.

  • 프로젝트 빌드 구성
  • 데이터 바인딩 레이아웃 파일 변환
  • 레이아웃 파일 Data 요소 추가
  • 바인딩 클래스 구성
  • 바인딩 표현식 사용

간단한 예시를 데이터 바인딩을 사용하도록 바꾸면서 각각의 사용법을 알아갈 것이다.

1. 프로젝트 빌드 구성

데이터 바인딩을 사용하려면 우선 build.gradle(Module: 앱이름.app)에 다음을 추가해야 한다.

android {
    .
    .
    .
    buildFeatures {
        dataBinding true
    }
}

전의 포스팅까지의 예시에 데이터 바인딩을 추가하기에 앞서 필요없진 코드를 삭제해 준다. 또한 사용자가 Convert 버튼을 클릭하였을때 ViewModel의 함수를 호출하여 환전된 값을 LiveData 변수에 넣어주도록 추가한다.

class MainViewModel: ViewModel() {
    private val usd_to_eu_rate = 0.74f
    var dollarValue: MutableLiveData<String> = MutableLiveData()
    private var _result: MutableLiveData<Float> = MutableLiveData()
    val result: LiveData<Float> get() = _result

    fun convertValue() {
        dollarValue.let {
            if (!it.value.equals("")) {
                _result.value = it.value?.toFloat()?.times(usd_to_eu_rate)
            } else {
                _result.value = 0f
            }
        }
    }
}

2. 데이터 바인딩 레이아웃 파일 변환

앱을 구성하는 UI는 XML 레이아웃 파일에 포함된다. 데이터 바인딩을 사용하기 위해서는 기존 XML 레이아웃 파일을 데이터 바인딩 레이아웃 파일로 변환 해야 한다.

📍 루트 뷰를 Layout 컴포넌트 로 교체

<?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">

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


        <EditText
            android:id="@+id/dollarText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            android:ems="10"
            android:inputType="numberDecimal"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
            .
            .
            .
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3. 레이아웃 파일 Data 요소 추가

데이터 바인딩 레이아웃 파일로 바꿨다면 이제 프로젝트의 UI 컨트롤러(ViewModel이나 Activity 및 Fragment)와 바인딩 되어야 한다.

클래스의 이름데이터 바인딩 레이아웃 파일에 선언 되어야 하고, 클래스의 인스턴스를 참조하는 변수 이름 을 설정해야 한다. 이때 data 요소를 추가한다.

📍 data 요소에는 variable, import 가 포함된다.

<?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="myViewModel"
            type="com.kyonggi.viewmodelexample.MainViewModel" />
    </data>
    .
    .
    .
</layout>

예시의 코드를 보면 MainViewModel 클래스(이때 클래스는 전체 패키지 이름으로 지정해야한다.) 타입의 변수이름 myViewModel 을 사용한다고 지정하였다고 해석하면 된다.

data 요소중 두 번째는 다른 클래스를 import 할 수 있으며, 이 클래스는 레이아웃 파일의 어디서든지 바인딩 표현식에서 참조가 가능하다.

4. 바인딩 클래스 구성

위의 바인딩 레이아웃 파일의 data 요소에서 참조하는 각 클래스는 레이아웃 파일의 CamelCase로 변환후 Binding이 붙은 형태 로 생성된다.(예시에서 MainViewModelBinding)

이 클래스는 레이아웃 파일에 지정된 바인딩 정보 를 포함하며 이것을 바인딩되는 객체의 변수와 함수 에 연결한다.

바인딩 클래스는 자동으로 생성되지만 대응되는 데이터 바인딩 레이아웃 파일에 대해 바인딩 클래스의 인스턴스를 생성 하는 코드는 생성해 줘야한다. 이때 DataBindingUtil 클래스 를 사용한다.

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.lifecycleOwner = this
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        // viewModel과 data 요소와 결합
        binding.myViewModel = viewModel

    }
}

DataBindingUtil.setContentView(this, R.layout.activity_main) 을 통하여 바인딩 클래스를 생성해준다. ViewModel과 LiveData를 함께 사용하기 위해 binding.lifecycleOwner = this 를 통하여 바인딩 객체가 Activity가 존재하는 동안에 메모리에 남아있고 소멸될때 바인딩 객체도 소멸되도록 생명주기 소유자(lifecycleOwner)를 현재 Activity로 설정해 준다. 마지막으로 binding.myViewModel = viewModel 을 통하여 레이아웃 data 요소의 변수 myViewModel을 ViewModel과 바인딩해준다.

5. 바인딩 표현식 사용

바인딩 표현식은 단방향양방향 이 있다. 또한 바인딩의 표현식은 산술 표현식, 함수 호출, 문자열 결합, 배열 요소 사용, 비교 연산 등을 수행 할 수 있다.

단방향 표현식은 @로 사작하며 중괄호({}) 안에 표현식을 넣는다. 예를 들어 data 요소의 변수이름이 viewmodel이고 result 변수를 포함하는 ViewModel 인스턴스라면 다음과 같이 바인딩 표현식을 사용할 수 있다.

android:text="@{viewmodel.result}"

단방향 바인딩은 대응 되는 데이터 모델 값이 변경되면 레이아웃 뷰의 값도 변경되지만 그 반대의 경우는 안된다.

양방향 표현식은 @= 으로 선언하면 된다. 예시로 EditText 뷰 같은경우는 사용자가 다른 값을 입력하면 이것과 대응되는 데이터 모델의 값이 입력 값으로 변경된다.

단향향 바인딩: @로 사작하며 중괄호({}) 안에 표현식을 넣는다.
양방향 바인딩: @=로 사작하며 중괄호({}) 안에 표현식을 넣는다.


이제 다시 예시로 가서 바인딩 표현식을 레이아웃 파일에 바인딩 해준다. 첫 번째로 TextView의 ViewModel 의 result 값을 바인딩 해준다.

        <TextView
            android:id="@+id/resultText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{safeUnbox(myViewModel.result) == 0.0 ? "Enter value" : String.valueOf(safeUnbox(myViewModel.result))}'
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

바인딩 표현식은 result값이 0이면 값을 입력하라고 “Enter value”로 알려주고 0이 아니면 환전된 값을 변환하여 사용자에게 보여준다.

여기서 값이 변경되는것은 ViewModel의 값은 변경될 필요가 없기 때문에 단방향 바인딩 을 사용하였다. 반면에 사용자가 입력을하는 EditText 값은경우 사용자가 직전에 입력한 값으로 ViewModel의 값이 변경될 수 있도록(장치의 회전이 일어났을때 현재 값을 보여줘야한다.) 양방향 바인딩을 사용한다.

        <EditText
            android:id="@+id/dollarText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            android:ems="10"
            android:text="@={myViewModel.dollarValue}"
            android:inputType="numberDecimal"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

마지막으로 Convert 버튼을 누르면 처음에 추가한 convertValue() 함수 를 호출하기 위해 바인딩 해준다.

        <Button
            android:id="@+id/convertButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:text="Convert"
            android:onClick="@{() -> myViewModel.convertValue()}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/resultText" />

앱을 실행해보면 “Enter value” 메시지와 올바르게 환전이 되는지를 확인할 수 있다.


References

맨 위로 이동 ↑

댓글남기기