[Android] Data Binding - Part.1

Hwichan Ji·2021년 9월 6일
0

Android

목록 보기
6/7
post-thumbnail

📌 Data Binding

Data Binding은 UI 구성요소와 앱의 데이터 소스를 선언적으로 연결할 수 있게 하는 라이브러리입니다.

레이아웃 파일에서 UI 구성요소를 앱 데이터와 연결하면 액티비티에서 UI 프레임워크의 호출을 줄일 수 있어서 코드가 간결해지고 유지관리가 쉬워진다는 장점이 있습니다. 또한 앱 성능이 향상되며, 메모리 누수 및 Null Pointer 예외를 방지할 수 있습니다.

레이아웃 파일

Data Binding 라이브러리는 레이아웃의 뷰를 데이터 객체와 결합하는데 필요한 클래스를 자동으로 생성합니다.

레이아웃 파일 작성

Data Binding을 사용하는 레이아웃 파일은 layout이라는 루트 태그로 시작하고 그 안에 해당 레이아웃에서 사용할 데이터를 data 태그를 통해 명시합니다. 그런 다음 레이아웃을 구성할 뷰들을 배치합니다.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView 
           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}"/>
   </LinearLayout>
</layout>
    

앱의 데이터는 @{} 구문을 통해 뷰의 특정 속성에 지정됩니다.

표현식

@{} 표현식에는 this, super, new, 명시적 제네릭 호출을 제외한 여러 연산자와 키워드를 사용할 수 있습니다.

<TextView
    android:text="@{String.valueOf(index + 1)}"
    android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}" />
          

또한 표현식에는 Null 병합 연산자를 사용할 수 있습니다.

<TextView
    android:text="@{user.displayName ?? user.lastName}" />

Null 병합 연산자
왼쪽 피연산자가 NULL이 아니면 왼쪽 피연산자를 선택, NULL이면 오른쪽 피연산자를 선택

표현식은 ID를 통해 레이아웃의 다른 뷰를 참조할 수도 있습니다.

<EditText
    android:id="@+id/example_text"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"/>
<TextView
    android:id="@+id/example_output"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{exampleText.text}"/> 

데이터 결합

Data Binding도 각 레이아웃 파일의 바인딩 클래스를 생성하는데요, 이 클래스는 View Binding처럼 레이아웃 파일 이름을 파스칼 표기법으로 변환한 뒤, Binding이라는 접미사를 추가한 이름을 갖습니다.

이 바인딩 클래스에는 레이아웃 속성(데이터 변수 등)에서부터 레이아웃 뷰까지 모든 바인딩을 갖고 있으며, 어떻게 바인딩 표현식의 값을 할당할지도 알고 있습니다.

바인딩 객체 생성 방법

권장되는 결합 생성 방법은 레이아웃이 inflating을 하는 동안에 생성하는 것입니다.

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

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

    binding.user = User("Test", "User")
}

다른 방법으로는 LayoutInflater를 이용하는 것이 있습니다.

val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())    

Fragment, ListView, RecyclerView 어댑터 내에서 Data Binding을 사용한다면 inflate() 메서드를 사용하여 바인딩 객체를 생성할 수 있습니다.

val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)
// or
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)

이벤트 처리

Data Binding을 사용하면 뷰에서 전달되는 표현식 처리 이벤트를 작성할 수 있습니다. 이벤트 속성의 이름은 대부분 리스너 메서드의 이름에 따라 결정됩니다.

메서드 참조

Data Binding에서 표현식이 메서드 참조로 계산되면 리스너에서 메서드 참조 및 소유자 객체를 래핑하고, 타겟 뷰에서 이 리스너를 설정합니다.

이벤트는 android:onClick이 액티비티의 메서드에 할당되는 방식과 유사하게 핸들러 메서드에 직접 결합될 수 있는데요, Data Binding의 메서드 참조는 표현식이 컴파일 타임에 처리된다는 장점이 있습니다.

class MyHandlers {
    fun onClickFriend(view: View) { ... }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.MyHandlers" />
       <variable name="user" type="com.example.User" />
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{handlers::onClickFriend}" />
   </LinearLayout>
</layout>

메서드의 매개변수는 이벤트 리스너의 매개변수와 일치해야 합니다.

메서드 참조이 리스너 결합과 다른 점은 실제 리스너 구현이 이벤트가 트리거될 때가 아닌, 데이터가 결합될 때 생성된다는 것입니다. 따라서 이벤트가 발생할 때 표현식을 계산하려면 리스너 결합을 사용해야 합니다.

리스너 결합

리스너 결합은 메서드 참조와 달리 이벤트가 발생할 때 실행되는 결합 표현식이며, 메서드와 이벤트 리스너의 반환 값만 일치하면 됩니다.

class Presenter {
    fun onSaveClick(task: Task){}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="task" type="com.android.example.Task" />
        <variable name="presenter" type="com.android.example.Presenter" />
    </data>
    <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="match_parent">
        <Button
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:onClick="@{() -> presenter.onSaveClick(task)}" />
    </LinearLayout>
</layout>

표현식에 콜백을 사용하면 데이터 결합은 필요한 리스너를 자동으로 생성하여 이벤트에 등록합니다.

리스너 결합에서는 모든 매개변수를 무시하거나, 모든 매개변수의 이름을 지정하여 매개변수를 선택할 수 있습니다. 매개변수 이름을 지정하면 표현식에 매개변수를 사용할 수 있습니다.

<Button
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}" />
class Presenter {
    fun onSaveClick(view: View, task: Task){}
}

import, variable, include

  • import를 사용하면 레이아웃 파일 내에서 클래스를 참조할 수 있습니다.
  • variable을 사용하면 결합 표현식에 사용할 수 있는 속성을 설명할 수 있습니다.
  • include를 사용하면 앱 전체에서 복잡한 레이아웃을 재사용할 수 있습니다.

import

data 태그 내에 0개 이상의 import 요소를 사용할 수 있습니다.

<data>
    <import type="android.view.View" />
</data>

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}" />

이처럼 import를 통해 클래스를 가져오면 표현식에서 해당 클래스를 참조할 수 있습니다.

별칭을 사용하여 클래스 이름 충돌을 해결할 수 있습니다.

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>

가져온 클래스는 변수 및 표현식에서 유형 참조로 사용할 수도 있습니다

<data>
    <import type="com.example.User" />
    <import type="java.util.List" />
    <variable name="user" type="User" />
    <variable name="userList" type="List&lt;User>" />
</data>

형변환 또한 가능합니다.

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content" />

variable

data 태그 내에 여러 variable 요소를 사용할 수 있습니다.

<data>
    <import type="android.graphics.drawable.Drawable"/>
    <variable name="user" type="com.example.User"/>
    <variable name="image" type="Drawable"/>
    <variable name="note" type="String"/>
</data>

include

속성에 앱 네임스페이스 및 변수 이름을 사용함으로써 포함하는 레이아웃에서 포함된 레이아웃의 결합으로 변수를 전달할 수 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <include 
            layout="@layout/name"
            bind:user="@{user}"/>
       <include 
            layout="@layout/contact"
            bind:user="@{user}"/>
   </LinearLayout>
</layout>
    

Reference

profile
안드로이드 개발자를 꿈꾸는 사람

0개의 댓글