반응형

 

※ findViewById()메소드를 이용한 View참조 방법이 가진 문제를 해결하기 위한 기법들[코드의 번거로움과 메소드의 동작속도 문제]

1. ButterKnife 외부 라이브러리 사용하기 (버터 나이프 라이브러리)
2. ViewBinding 안드로이드 아키텍처 구성요소
3. DataBinding (view binding + data binding) : 뷰결합 + 데이터 결합 기능까지 가진 아키텍처 구성요소

 

이번글에서는 findViewById()를 대체하는 기술의 마지막 3번째 기법인 데이터바인딩에 대해 소개하겠습니다.

이 데이터 바인딩기법은 요즘 안드로이드 앱 개발시에 많이 사용되는 프로젝트 아키텍처 패턴 중 MVVM( Medel- View - View Model)을 이해하는데 선행되어야 할 기법 중 하나여서 이 글에서 학습이 끝나지 않는 기법이니 주의깊게 봐 주시기 바랍니다.

 

코드를 보기전에 간략하게 데이터바인딩의 주요특징을 알아보겠습니다.

 

데이터바인딩은 기존에 하던 뷰와의 상호작용 방법과는 완전 그 메카니즘이 다릅니다.  즉, 기존 방법대로 이해하려고 하면 오히려 코드를 보실때 더 알아보기 어려우실 것이라는 겁니다. 아주 큰 개념의 차이점만 먼저 인지하실 필요가 있습니다.

기존에 하던 findViewById()나 ButterKnife 라이브러리, 또는 뷰바인딩은 기본적으로 데이터를 보여주는 뷰(텍스트뷰 또는 이미지뷰 등)들을 xml에서 만들고 이를 .java 언어나 .kt 코틀린언어의 프로그래밍 코드에서 참조하여 참조변수를 이용하여 뷰에게 원하는 데이터를 설정하는 방법으로 뷰의 데이터를 보여줍니다. TextView의 .setText("Hello") 나 ImageView의 .setImageResource() 같은 뷰들의 기능메소드를 이용하는 방법 말이죠. 다시말해, 데이터를 보여주는 View객체들을 참조하여 그 객체들을 제어하는 방식입니다.

 

이에 반해, 데이터바인딩은 뷰객체를 참조하는 것이 아니라 뷰객체가 보여주는 데이터를 가진 변수를 제어합니다. 즉, 뷰들에게 데이터를 가지고 있는 변수를 처음에 설정해주면 그 변수의 값이 보여질 것이고, 그런다음 버튼을 클릭하는 등의 이벤트가 발생하였을때 뷰들을 참조하여 제어하지 말고 뷰에 설정되어 있는 변수의 값을 변경하면 그 뷰가 보여주는 데이터가 바뀌니까 화면도 바뀌도록 하는 방식입니다. 뷰객체를 제어할 필요가 없기에 findViewById()를 할 필요가 없는 것이죠. 사실 기존에 하던 방법과는 완전히 다른 뷰와의 상호작용 기법이라 조금 당황스러울 수 있겠지만 이해가 어려운 것은 아닙니다.  오히려 쉽다면 쉬운겁니다. 그냥 변수값만 바꾸면 화면이 갱신되는 겁니다. 특히, 리스트뷰나 리사이클러뷰 등을 다루어 보신 분들은 더 강하게 와 닿을 겁니다. 리스트뷰나 리사이클러뷰가 보여주는 대량의 데이터에 변경이 있을때 화면을 갱신하기 위해 아답터객체의 notifyDataSetChange()나 notifityItemInsert()등의 화면갱신 코드를 매번 호출해야 하지만 데이터 바인딩은 그 코드를 일일이 신경쓰지 않아도 데이터가 바뀌면 화면이 자동 갱신됩니다. 익숙해지면 코딩이 아주 수월해 지실겁니다. 물론 아무 변수나 값이 바뀌었다고 뷰들이 무조건 갱신되지는 않습니다. 만약, 그렇게 되면 뷰의 갱신이 너무 자주 발생하여 오히려 앱의 성능에 악영향을 미칠테니까요. 그래서 관찰이 가능한(Observable) 자료형의 변수들이 바뀌었을때만 연결된 뷰의 화면이 자동갱신되어 있도록 되어 있어서 원하는 시점에만 화면의 갱신이 되도록 조절할 수 있습니다.

 

데이터 바인딩은 뷰바인딩 기능이 기본적으로 내포되어 있기에 데이터바인딩 사용 설정만으로도 원한다면 뷰바인딩으로 뷰와의 상호작용이 가능합니다. 때문에 프로젝트의 상황에 따라 개발자가 뷰바인딩과 데이터바인딩을 적절히 선택하여 개발하신다면 보다 효율적이고 성능이 개선된 앱을 만드실 수 있으실 겁니다. 

뷰바인딩에 대한 내용을 알지못한다면 이전 뷰바인딩에 대한 글을 먼저 읽어보시고 오시길 권합니다. 조금더 데이터 바인딩의 초기설정을 이해하는 데 도움이 될겁니다.

 

2021.09.15 - [소소한 소스코드] - [안드로이드 Android] 뷰 바인딩 View Binding #1

 

 

너무 서론이 길면 오히려 학습에 방해가 되니 우선 이정도만 소개하고 추후 필요한 시점에 추가적인 특성들을 더 소개하도록 하겠습니다.

 

 

이제 여러분들의 앱에 데이터 바인딩을 적용해보는 실습을 진행해보겠습니다.

 

참고로 데이터 바인딩 역시 뷰바인딩 처럼 안드로이드 아케텍처 구성요소이기에 별도의 라이브러리를 추가하는 것이 아니라 데이터바인딩 기능을 사용설정 해줌으로서 준비가 끝나기에 매우 편하게 적용가능합니다.

 

먼저, 앱 모듈수준의 build.gradle 파일에 데이터바인딩 사용 설정을 하도록 하겠습니다. 안드로이드 스튜디오의 버전에 따라 방법이 다르니 참고하시어 따라하시기 바랍니다. 

※주의. 안드로이드 개발자 공식문서에는 아직 예전 버전의 방법으로 소개되어 있습니다. 이 글을 보고있는 대부분의 분들은 본인의 안드로이드 스튜디오 버전이 최신버전이기에 4.0버전 이상일 겁니다.

# build.gradle (Module)
android {
    
    .....


    //Android Studio 4.0 버전 이상일때,
    buildFeatures {
        dataBinding = true
    }
    
    //Android Studio 4.0 버전 미만일때,
    dataBinding {
    	enabled= true
    }
}

 

데이터 바인딩의 사용설정을 해주면 뷰바인딩과 마찬가지로 레이아웃 xml 파일과 연결되어 자동으로 설계되는 바인딩 클래스가 만들어 집니다. 다만, 뷰바인딩과 다르게 무조건 생기는 것이 아닙니다. 여기서 주의 해야 합니다. 

데이터 바인딩에 의해 자동 연결되고 싶은 레이아웃 xml 파일의 root(최상위) View는 반드시 <layout> 이어야 합니다. 기존의 xml 레이아웃파일을 만들듯이 그냥 RelativeLayout아니 LinearLayout으로 시작하면 바인딩클래스는 만들어 지지 않습니다.

 

즉, 데이터바인딩으로 제어하고 싶은 레이아웃 xml은 기존 코드를 반드시 수정해야 합니다.

참고로 미리 소개하자면, 자동으로 설계되어 만들어지는 바인딩클래스의 이름은 뷰바인딩 명명규칙과 동일합니다.

activity_main.xml 과 연결되는 바인딩 클래스는 ActivityMainBinding 인 것은 뷰바인딩과 똑같습니다. 단, 뷰 바인딩과 다르게 이 바인딩클래스 객체의 뷰참조 멤버변수들을 직접 제어하지 않는 다는 것이 조금 다릅니다.

 

우선 데이터바인딩에서의 레이아웃 xml 은 최상위 root로 <layout>으로 시작하고 그 안에 크게 2개의 요소를 배치합니다.

1. <data>요소 - 뷰들과 연결되어 보여줄 데이터를 가진 데이터 클래스를 지정해주고 레이아웃파일안에서 사용할 식별명을 정해주는 요소입니다. 

2. 레이아웃의 root 뷰 요소 - 기존에 레이아웃 xml 파일에 하던 RelativieLayout 이나 LinearLayout 등이 뷰를 배치합니다.

 

주석으로 해당 요소들에 대한 소개를 하였으니 참고 바랍니다.

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Data Binding의 root는 <layout> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- 1. 레이아웃뷰와 바인딩할 데이터들 명칭과 클래스지정   -->
    <data>
        
    </data>


    <!-- 2. 레이아웃 뷰 : 기존에 root로 만들었던 뷰   -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="16dp">

    </LinearLayout>

</layout>

 

코드에서 보듯이 <layout>태그가 최상위 이고 그 안에 1. 과 2. 의 자식요소가 배치됩니다. 2. 레이아웃뷰는 기존에 레이아웃 xml 파일의 최상위 였던 뷰그룹입니다.

1. <data> 요소가 중요합니다. 데이터바인딩의 핵심이지요. 여기에 뷰들에 보여질 데이터를 가지고 있는 클래스를 지정해주고 이 레이아웃 파일안에서 인식할 명칭을 지정해주는 작업을 합니다.

 

먼저 간단하게 TextView를 하나 추가한 후 그 뷰가 보여줄 데이터를 멤버로 가지는 클래스를 만들어 지정해 주겠습니다.

 

앱에 사용될 데이터를 저장할 변수를 가진 클래스를 설계하는 겁니다. 데이터 바인딩의 연결 특성을 확인해보는 실습이라서 우선 간단히 만들고 다음 실습에서 다시 수정할 겁니다. TextView에 보여줄 String 멤버변수를 하나 가지는 클래스 입니다.

# User.java
public class User{
	
    //TextView에서 보여줄 데이터를 저장하고 있을 변수
    String name;
    
    //생성자메소드
    public User(String name){
    	this.name = name;
    }
    
}

 

이제 위 데이터 클래스를 activity_main.xml 에서 데이터바인딩으로 연결 설정해주기 위해 <data> 태그안에 변수값을 의미하는 태그인 <variable>로 지정해 주겠습니다. 주석으로 부연설명을 하였으니 확인바랍니다.

 

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Data Binding의 root는 <layout> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- 1. 레이아웃뷰와 바인딩할 데이터들 명칭과 클래스지정   -->
    <data>
        <!-- 데이터바인딩으로 연결할 User클래스 객체를 이 레이아웃에서는 user 라는 이름으로 참조하여 사용하겠다는 설정  -->
        <variable
            name="user"
            type="com.kitesoft.databindingjava.User" />

    </data>


    <!-- 2. 레이아웃 뷰 : 기존에 root로 만들었던 뷰   -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="16dp">

    </LinearLayout>

</layout>

 

<variable>태그 속성

 - name속성 :  "user" 라는 이름은 개발자가 임의로 지정하는 값입니다. 이 activity_main.xml에서 이 "user"라는 이름으로 type에 지정한 자료형(User클래스)의 객체를 인식하겠다는 의미입니다. 일종의 xml에서 사용하는 객체 참조변수명 같은 것이라고 쉽게 이해하시면 됩니다. 

- type속성 : 이 activity_main.xml과 연결될 데이터를 가지고 있을 클래스 입니다. 저 위에서 만들었던 User 클래스를 지정해 줍니다.

 

추후 MainActivity.java 에서 이 User타입의 객체를 만들어서 바인딩 해주는 코드를 작성해주게 됩니다. 

 

<data> 태그안에 데이터를 가진 변수 <variable>을 지정해 주었다면 이제 TextView를 하나 추가 하겠습니다. 특별한 점은 텍스트뷰가 보여줄 글씨값을 설정한는 android:text="" 속성안에 {}를 쓰고 위 <data>안에 만든 <variable>을 마치 객체인듯 이 곳에서 직접 연결한다는 겁니다. 주석으로 부연설명했습니다.

 

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Data Binding의 root는 <layout> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- 1. 레이아웃뷰와 바인딩할 데이터들 명칭과 클래스지정   -->
    <data>
        <!-- 데이터바인딩으로 연결할 User클래스 객체를 이 레이아웃에서는 user 라는 이름으로 참조하여 사용하겠다는 설정  -->
        <variable
            name="user"
            type="com.kitesoft.databindingjava.User" />

    </data>


    <!-- 2. 레이아웃 뷰 : 기존에 root로 만들었던 뷰   -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="16dp">

        <!-- 텍스트뷰의 글씨를 <data>태에서 지정한 <variable>그 요소의 User객체의 멤버값 사용 -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="@{user.name}"/>

    </LinearLayout>

</layout>

@{user.name} 이 데이터 바인딩의 가장 핵심코드입니다. user라는 이름은 <variable>에서 name 속성안에 개발자가 임의로 지정한 이름입니다.  그리고 그 user는 type에 지정한 User클래스의 참조변수명이기에 그 안에 있는 멤버변수 String name 변수의 값을 텍스트뷰에서 보여주겠다고 설정한 것입니다. 평소에 해오던 안드로이드 코드와 달라서 조금 어색해 보일 수 있으나 생각보다 간단히 이해되는 코드입니다. 

 

근데. 여기까지만 보면 User객체를 만든적도 없고, 그 User 객체의 name 변수에 어떤 값을 저장한 적도 없었죠. 그렇기에 도데체 어떤 글씨가 보여질 것인지 전혀 예측하기 어려울 겁니다. 그 작업은 MainActivity.java 에서 하시는 겁니다.

 

곧바로 자바코드를 확인해 보겠습니다. 주석으로 주요설명을 표시하였으니 잘 살펴보시기 바랍니다.

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;

import com.kitesoft.databindingjava.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);

        //DataBindingUtil클래스를 통해 이 MainActivity에게 activit_main.xml 레이아웃을 ContentView()로 설정하고 연결하여 제어하는 클래스객체를 리턴
        //클래스의 이름의 xml레이아웃 파일명을 기준으로 자동으로 설계되어 만들어짐. [ex. activity_main.xml 이면 ActivityMainBinding,  activity_intro.xml 이면 ActivityIntroActivityBinding ]
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        //xml문서의 <data>요소 안에 있는 <variable>의 type에 지정한 클래스 객체를 만들어 set()해주면 레이아웃파일의 뷰들에 설정한 객체의 값들이 반영됨
        binding.setUser(new User("sam")); //초기값으로 지정할 User객체 생성 및 바인딩객체에게 설정

    }
}

 

이제 실행해 보시면 텍스트뷰에는 bind.setUser( new User("sam") ); 에 의해 설정된 데이터객체인  User의 멤버값 "sam"이 보여지게 될 겁니다. 

당연히 객체가 name 말고 여러개의 데이터를 추가로 더 가지고 있다면 각각의 값을 보여주는 뷰와 연결하는 작업도 같은 방식으로 할 수 있습니다. 연습을 위해 일부러 String 이 아닌 int나 boolean 같은 다른 자료형을 가지고 있도록 하고 이 값들을 적절한 뷰에 연결하도록 하겠습니다.

 

먼저 User클래스를 조금 수정하겠습니다.

#  User.java
public class User{
	
    //TextView에서 보여줄 데이터를 저장하고 있을 변수
    String name;  //이름 
    int age;      //나이
    boolean fav;  //좋아요 체크박스 여부 [true/false]
    
    //생성자메소드
    public User(String name, int age, boolean fav){
    	this.name = name;
        this.age = age;
        this.fav = fav;
    }
    
}

 

이제 age와 fav 데이터값을 보여줄 TextView와 CheckBox 뷰를 추가하여 데이터와 연결해 놓겠습니다.

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Data Binding의 root는 <layout> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- 1. 레이아웃뷰와 바인딩할 데이터들 명칭과 클래스지정   -->
    <data>
        <!-- 데이터바인딩으로 연결할 User클래스 객체를 이 레이아웃에서는 user 라는 이름으로 참조하여 사용하겠다는 설정  -->
        <variable
            name="user"
            type="com.kitesoft.databindingjava.User" />

    </data>


    <!-- 2. 레이아웃 뷰 : 기존에 root로 만들었던 뷰   -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="16dp">

        <!-- 텍스트뷰의 글씨를 <data>태에서 지정한 <variable>그 요소의 User객체의 멤버값 사용 : Observarble 멤버이기에 값변경이 관찰되어 자동 반영됨 -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="@{user.name}"/>

        <!-- int형 값인 user.age는 TextView에 text로 직접 설정이 안되기에 String.valueOf()메소드를 이용하여 문자열로 변환하여 설정 [문자열 결합 +""는 안됨]   -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="@{String.valueOf(user.age)}"/>

        <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="좋아요"
            android:checked="@{user.fav}"/>

    </LinearLayout>

</layout>

 

age와 fav 값을 추가로 설정해주도록 자바코드를 수정하겠습니다.

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;

import com.kitesoft.databindingjava.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);

        //DataBindingUtil클래스를 통해 이 MainActivity에게 activit_main.xml 레이아웃을 ContentView()로 설정하고 연결하여 제어하는 클래스객체를 리턴
        //클래스의 이름의 xml레이아웃 파일명을 기준으로 자동으로 설계되어 만들어짐. [ex. activity_main.xml 이면 ActivityMainBinding,  activity_intro.xml 이면 ActivityIntroActivityBinding ]
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        //xml문서의 <data>요소 안에 있는 <variable>의 type에 지정한 클래스 객체를 만들어 set()해주면 레이아웃파일의 뷰들에 설정한 객체의 값들이 반영됨
        binding.setUser(new User("sam", 20, true)); //초기값으로 지정할 User객체 생성 및 바인딩객체에게 설정

        //참고로 뷰들에 id가 지정되어 있다면 binding객체의 멤버변수로 id명과 같은 뷰의 참조변수들이 자동으로 만들어져 있음 [ ViewBinding 기능 ]
    }
}

실행해 보았을때 2개의 텍스트뷰에 "sam", 20 이 보이고 체크박스는 true 값에 의해 체크된 상태로 보이면 데이터 바인딩이 잘 동작하고 있는 것입니다.

 

잠시 정리하면, 자바에서 뷰들을 findViewById()와 같은 방법으로 참조하여 값을 설정하는 것이 아니라 데이터를 가진 클래스(User) 객체를 만들고 xml 레이아웃에서 미리 연결만 해 놓으면 알아서 UI에 적용되도록 하는 기법입니다. 글로 쓴 것보다 코드를 보면 더 쉽게 이해가 되실 거라고 생각합니다.

 

대충 데이터 바인딩에 대해 알아보았습니다. 하지만 지금까지 소개한 데이터 바인딩은 큰 개념만 소개하기 위해 실습입니다. 애석하게 지금 만든것처럼 User 클래스를 만들면 클릭같은 이벤트가 발생했을때 User객체의 값을 변경해도 이 객체의 데이터를 보여주도록 설정되어 있는 뷰들의 화면을 자동 갱신되지 않습니다. 

 

다음 포스트에서 데이터를 변경했을때 자동으로 UI 가 갱신되어 뷰들에 변경된 값이 보이도록 하는 코드를 소개하겠습니다. 진짜 데이터바인딩의 코드인 것이지요.

잠시 예고를 해드린다면, User 클래스 멤버들의 자료형을 그냥 String, int, boolean으로 선언하면 값의 변경을 관찰(Observe) 하고 있지 않아 화면이 자동 갱신되지 않기에 관찰가능한 자료형으로 선언해야 합니다. 다음 포스트에서 코드로 소개해 드리겠습니다.

반응형

+ Recent posts