본문 바로가기

Android

안드로이드 데이터 바인딩 in Java

반응형

Hello World!

TextView label = findViewById(R.id.textview);
label.setText("Hello World!");

findViewById(...) 안드로이드를 처음 배울 때 제일 많이 사용한 구문입니다. 그러나! 해당 구문은 레이아웃 파일이 커질수록, 찾는 요소가 많아질수록 비용이 커지기 때문에 사용하지 말아야 합니다...

내부적으로 findViewById(...) 구문은 다음과 같이 작용합니다. 안드로이드는 매번 findViewById(...) 구문이 호출될 때마다 레이아웃 파일 트리를 순회하며 해당하는 요소를 찾습니다. 그러니 레이아웃 파일이 많아지거나 찾는 요소가 많아질수록 시간이 늘어날 수 밖에 없는 구조이지요. 구글 엔지니어들은 Data Binding 기법을 통해 이러한 문제를 해결하였습니다.

Data Binding

Data Binding의 컨셉은 단순합니다. 레이아웃 파일을 중개해주는 오브젝트를 만들어 관리하는 방법입니다. 액티비티 입장에서는 해당 오브젝트의 속성에 접근하듯 bindObj.textView와 같은 방식으로 레이아웃의 요소에 접근할 수 있습니다.

Gradle Scripts

Data Binding을 사용하기 위해서는 먼저 Gradle Scripts에서 해당 기능을 활성화하여야 합니다. Gradle Scripts는 백그라운드에서 프로젝트 구성을 위해 사용되며, 이 중 build.gradle을 수정하여 JDK 버전을 설정하거나 라이브러리를 추가하는 등 프로젝트와 관련된 설정을 수행할 수 있습니다. build.gradle은 Project, Module 두 개의 파일로 나뉘는데, build.gradle(Project: ...) 파일은 모든 서브 프로젝트, 모듈에 적용되는 최상위 빌드 파일이며, common configs를 포함하여야 합니다. build.gradle(Module: ...)은 해당 모듈에만 적용되는 구성 파일입니다.

데이터바인딩은 기타 모듈을 제외한, 어플리케이션에만 적용되어야 하므로, build.gradle(Module: ...)에 적용하는 것이 맞습니다. build.gradle(Module: ...)에 다음과 같이 추가합니다.

android {
    ...
    buildFeatures {
        dataBinding true
    }
}

layout_file.xml

이제 다음과 같이 각 레이아웃 파일 내용을 <layout> 태그로 감싸줍니다. 이 과정에서 xml name space는 최상단 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.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <ImageView
            android:id="@+id/profileImage"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:contentDescription="@string/name"
            app:srcCompat="@drawable/ic_launcher_foreground" />

        <TextView
            android:id="@+id/name"
            style="@style/name_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="RooMedia" />

        <TextView
            android:id="@+id/hobbies"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Playing Guitar, Drink Water, ..., etc" />

        <Button
            android:id="@+id/toggleButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="HIDE" />

    </androidx.appcompat.widget.LinearLayoutCompat>
</layout>

LayoutActivity.java

위 두 과정을 마치면 이제 데이터 바인딩을 사용할 수 있습니다. 레이아웃 파일의 이름에 따라 데이터를 중개해주는 클래스가 생성됩니다. 레이아웃 파일 이름이 activity_main.xml이었다면 ActivityMainBinding과 같은 자료형이 생성되는 식으로, 모든 레이아웃 파일에 각각을 나타내는 클래스가 생성됩니다.

사용은 다음과 같이 할 수 있습니다. setContentView 부분에 변화가 생겼고, 이후에는 binding 멤버변수를 활용하여 이전과 동일하게 사용할 수 있습니다.

package com.roo_media_.helloandroidworld;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;
import android.view.View;

import com.roo_media_.helloandroidworld.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.toggleButton.setOnClickListener(this::toggleViews);
    }

    private void toggleViews(View view) {
        int visibility;
        String buttonText;

        switch (binding.profileImage.getVisibility()) {
        case View.VISIBLE:
            visibility = View.GONE;
            buttonText = "SHOW";
            break;

        default:
            visibility = View.VISIBLE;
            buttonText = "HIDE";
            break;
        }

        binding.profileImage.setVisibility(visibility);
        binding.name.setVisibility(visibility);
        binding.hobbies.setVisibility(visibility);
        binding.toggleButton.setText(buttonText);
    }
}

Insert Data

데이터 바인딩은 위와 같은 기능 뿐만 아니라, 레이아웃 파일을 데이터와 연결하는 역할을 하기도 합니다. 이를 이용하면 데이터를 수정하면 레이아웃 값을 따로 바꾸지 않아도 수정됩니다. 먼저 Bio.java 파일을 만들어 다음과 같은 클래스를 생성합니다. Bio 클래스는 name, hobbies를 멤버변수로 가지며, getter, setter를 통해 값을 조작합니다.

package com.roo_media_.helloandroidworld;

public class Bio {
    private String name;
    private String hobbies;

    public Bio() {
    }

    public Bio(String name, String hobbies) {
        this.name = name;
        this.hobbies = hobbies;
    }

    public String getName() {
        return name;
    }

    public String getHobbies() {
        return hobbies;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setHobbies(String hobbies) {
        this.hobbies = hobbies;
    }
}

xml 파일에서는 이런 식으로 표현합니다.

<?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="bio"
            type="com.roo_media_.helloandroidworld.Bio" />
    </data>

    ...

    <TextView
            android:id="@+id/name"
            style="@style/name_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{bio.name}"
            tools:text="RooMedia" />

        <TextView
            android:id="@+id/hobbies"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{bio.hobbies}"
            tools:text="Playing Guitar, Drink Water, ..., etc" />

        ....

</layout>

연결을 위해 binding에서 setter를 이용합니다.

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private Bio bio = new Bio();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.toggleButton.setOnClickListener(this::toggleViews);

        bio.setName("RooMedia");
        bio.setHobbies("Playing Guitar, Drink Water, ..., etc");
        binding.setBio(bio);
    }
}
반응형