본문 바로가기
ANDROID/Android Jetpack

[Android/Jetpack] AAC - Data Binding

by 주 녕 2021. 8. 11.
728x90

https://developer.android.com/

View Binding에 이어 Data Binding에 대한 포스팅이다. 

View Binding은 여러 번 사용해보았는데, 아키텍처를 공부한지 얼마 되지 않아서인지 Data Binding은 아직 생소한 개념이다. View Binding과 Data Binding을 비교해보고 이번 포스팅을 통해 Data Binding에 대한 실습 또한 진행해보려고 한다.

 

Data Binding

레이아웃의 UI 구성 요소를 앱의 데이터 소스와 결합할 수 있는 지원 라이브러리

→ xml 파일에 데이터를 연결(binding)하여 사용할 수 있게 도와주는 라이브러리 

 

 BEFORE    findViewById()를 사용하여 특정 위젯을 찾는 방식이 아닌 (프로그래밍적으로 연결)

 NOW          레이아웃 파일에서 직접 위젯에 할당 (선언적 방식으로 연결)

 

Why Data Binding?

데이터를 UI 위젯에 연결하기 위한 코드를 최소화 할 수 있다

  • findViewById()를 사용하지 않고도 자동으로 xml에 있는 view를 생성
  • 레이아웃 파일에서도 View에 어떤 데이터가 들어가는지 파악할 수 있음
  • 데이터의 변화에 따라 자동으로 View를 변경하게 할 수 있음(옵저버 사용 시)
  • 파일이 단순화되어 유지관리가 쉬워지고 메모리 누수 방지, null 위험을 방지할 수 있음

 

View Binding과 Data Binding의 차이점

View Binding과 Data Binding은 모두 View를 직접 참조하여 사용할 수 있는 binding 클래스를 제공한다.또한 두 가지 방식 모두 Null Safety, Compile Type Safety라는 장점이 있다.

  • Data Binding은 빌드 속도가 느려지는 등의 단점이 있음
  • Data Binding은 단독으로 사용하는 것 보다는 MVVM이나 MVP 아키텍처와 함께 사용하는 것이 좋음
  • View Binding은 컴파일 속도가 빠르지만 동적 UI 컨텐츠를 생성할 수 없어 비교적 단순한 처리에 적합함

View Binding이 궁금하다면 아래 포스팅 참고!

 

Data Binding의 사용

build.gradle

android {
    ...
    buildFeatures {  // 안드로이드 스튜디오 4.0 이상
        dataBinding true
    }
}

공식문서에서는 아래와 같은 코드로 라이브러리를 가져오라고 되어 있었지만 빌드해보니 'android.dataBinding.enabled'는 쓸모없다(?)고 하며 'android.buildFeatures.dataBinding'으로 교체되었다고 한다. 찾아보니 안드로이드 4.0 부터는 위의 방식으로 사용한다고 한다. 

 

레이아웃 파일

최상위 레이아웃에서 ALT+ENTER를 누르고 'Convert to data binding layout'을 누르면 <layout> 태그가 생성된다.

  • <import> : xml파일에서 클래스를 쉽게 참조할 수 있도록 함
  • <variable> : xml의 바인딩식에 사용할 수 있는 프로퍼티 선언
  • <include> : 전달된 variable 프로퍼티로 include 된 레이아웃에 바인딩할 수 있는 기능
<?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="user"
            type="com.june.databinding.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity"
        android:gravity="center">

        <TextView
            android:id="@+id/first_name_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{firstNameText.text}" />

    </LinearLayout>
</layout>

 

데이터 바인딩

각 레이아웃 파일의 바인딩 클래스가 생성된다.

해당 클래스 이름은 레이아웃 파일이름을 파스칼 표기법으로 바꾸고 Binding 추가 ( → ActivityMainBinding )

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.user = User("June", "Lee")
    }
}

data class User(val firstName: String, val lastName: String) {}

✌ 결합하는 방식 2가지

  • 공식 문서에서는 방법 1 을 권장하고 있다. (레이아웃 인플레이션 하는 동안 결합을 생성하는 방식)
  • 방법 2 또한 사용할 수 있고, Fragment, ListView, Recyclerview Adapter 내에서도 사용할 수 있다고 한다.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
        
    // 방법 1
    val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
                
    // 방법 2
    val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())
}

레이아웃 인플레이션은 아래 정리한 포스팅 참고!

 

 

이벤트 처리

  1. 메소드 참조(Method references)
    • 메소드 참조와 리스너의 owner 객체를 래핑하고 View에 그것을 설정
    • 메소드의 매개변수와 이벤트 리스터의 매개변수가 같아야 함
    • 컴파일 시에 체크할 수 있음
  2. 리스너 바인딩(Listener bindings)
    • 람다식으로 표현됨
    • 메소드의 리턴값과 이벤트 리스너의 예상된 리턴값만 같으면 됨
    • 이벤트가 실제로 발생했을 때 실행됨
<?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>
        <import type="android.view.View" />
        <variable
            name="user"
            type="com.june.databinding.User" />
        <variable
            name="main"
            type="com.june.databinding.MainActivity" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity"
        android:gravity="center">

        ...

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="click this"
            android:onClick="@{() -> main.clickButton()}"/>

    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.user = User("June", "Lee")
        binding.main = this  // 정의하지 않으면 버튼을 눌러도 반응 없음
    }

    fun clickButton() {
        Toast.makeText(this, "Thank you :)", Toast.LENGTH_SHORT).show()
    }
}

 

데이터 바인딩의 연산

💡 Null Pointer Exception 방지

  • 1. null 병합 연산자(??)를 사용하여 null인 경우의 연산 제공 → null이 아니면 왼쪽 피연산자, null이면 오른쪽 피연산자 선택
    android:text="@{user.displayName ?? user.lastName}"
    android:text="@{user.displayName != null ? user.displayName : user.lastName}"  // 동일한 코드
  • 2. 자동으로 null 값을 확인하고
    • 표현식의 값이 null인 경우, null이 기본 값으로 할당됨
    • 변수의 타입이 Int이며 null 값인 경우, 0의 기본 값을 사용함

 

View 참조

다른 위젯의 Id 값을 사용하여 View를 참조할 수 있음 (첫번째 레이아웃 파일의 세번째 TextView 참고)

 

컬렉션

  • Array, Map, List 등의 일반 컬렉션을 사용할 수 있음
  • [] 연산자를 통하여 참조할 수 있음
  • object.key 표기법을 사용하여 Map의 값을 참조할 수 있음
    <data>
        <import type="java.util.Map"/>
        <import type="java.util.List"/>
        <variable name="list" type="List&lt;String>"/>
        <variable name="map" type="Map&lt;String, String>"/>
        <variable name="index" type="int"/>
        <variable name="key" type="String"/>
    </data>
    …
    android:text="@{list[index]}"
    …
    android:text="@{map[key]}"
    android:text="@{map.key}"

 

 

 

 

참고할 수 있는 이전 포스팅

 

[Android] 뷰 바인딩(View Binding) in Activity, Fragment

View Binding에 대한 지식은 아래의 이전 포스팅을 참고하자! [지식] Kotlin Android Extensions deprecated 시간이 좀 지난 일이지만 개발 방식을 조금 바꾸게 되어 정리해본다. 안드로이드 4.1 버전에서 새로

junyoung-developer.tistory.com

 

[Android] 레이아웃 인플레이션 (layout inflation)

모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다. 레이아웃 인플레이션 안드로이드 앱을 개발할 때, 우리는 2가지 파일에 나누어서 개발함. 화면 배치를 알려주는 XML

junyoung-developer.tistory.com


reference >

 

728x90

댓글