그냥 가끔의 기록장

Android Data Binding 본문

Android

Android Data Binding

the phoenix 2021. 5. 23. 22:33

1. Data binding이란

1. 정의

데이터 바인딩이란, 데이터와 UI 요소를 프로그래매틱 방식이 아니라 선언적 형식으로 결합하게 해주는 라이브러리이다. 

참고: https://developer.android.com/topic/libraries/data-binding

 

개발 공식 문서의 정의를 예시로 살펴보자. TextView에 text를 정의할 때는 두 가지 방식이 있다.

1. 프로그래매틱 방식:

 java나 kt 파일의 onCreate 메서드 내에서 Textview.setText("~~"); 로 정의하는 방식

 

2. 선언적 방식:

xml의 android:text에 @{user.name} 이렇게 선언하는 방식

 

두 번째 경우는 첫 번째와 달리 자바나 코틀린 코드를 직접 호출하지 않아도 데이터와 UI요소를 직접 결합할 수 있다. 

 

2. 왜 사용하는걸까

데이터 바인딩을 사용했을 때 장점을 https://salix97.tistory.com/243 님이 잘 정리해주셨다.

1. findViewId() 를 호출하지 않아도, 자동으로 xml 에 있는 VIew 들을 만들어준다.
2. RecyclerView 에 각각의 item 을 set 해주는 작업도 자동으로 진행된다.
3. data 가 바뀌면 자동으로 View 를 변경하게 할 수 있다.
4. xml 리소스만 보고도 View 에 어떤 데이터가 들어가는지 파악이 가능하다.
5. 코드 가독성이 좋아지고, 상대적으로 코드량이 줄어든다.

 

3. 어떨 때 쓸까

 데이터 바인딩은 보통 혼자 쓰기보다는 MVVM 과 MVP 아키텍처와 같이 쓴다고 한다. 필자도 MVVM이 뭔지 알아보고 예제를 따라해보다가.. 데이터 바인딩부터 정리하게 되었다. 

 

2. Data binding 예제 코드

1. Databinding 기본 - Data에 대한 Class 생성 후, 활용하기 

(1) build.gradle에 dataBinding 추가 

module: app의 build.gradle에 dataBinding을 추가한다.

android{
	...
	dataBinding {
        enabled true
    }
}

 

(2) binding 하려는 xml의 최상위 태그를 <layout> 으로 한다. 

constraintLayout (바인딩 하려는 layout) 에 오른쪽 마우스 우클릭 > show context actions > convert to data binding layout

클릭 시 자동으로 layout 태그로 감싸진다.

data 태그에 variable 태그로 아래와 같이 데이터명과 타입을 정의해서 사용하면, textView의 text data로 바로 바인딩이된다.

=> @{lastName} 부분

<?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="name"
            type="String" />

        <variable
            name="lastName"
            type="String" />

    </data>

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

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{lastName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

xml에 layout 태그로 데이터 바인딩을 했다면, MainActiviyBinding 클래스(Activity명+Binding) 가 자동으로 생긴다

(필자는 뭔 짓을 안해도 생기지가 않아서 한참을 헤맸다.)

1. 맥북 os 업데이트

2. Android Studio 업데이트

3. gradle 업데이트 (얘로 해결된 것 같은 느낌...)

위에 3가지를 다하고 나서 <layout>에 <data> 태그와 <variable> 태그까지 생성하니 그제서야 만들어졌다. 

 

(3) Activity에서 DataBindingUtil.setContentView() 호출하기 

MainActiviyBinding class 가 생겼다면 DataBindingUtil 객체를 생성한 후,  MainActivity의 onCreate() 에서 기존의 setcontentView를 DataBindingUtil.setContentView(this, R.layout.~~~) 로 변경한다. 

binding.setActivity(this)로 MainActivity를 바인딩 해주면 끝! 

public class MainActivity extends AppCompatActivity {
    ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setActivity(this);
    }
}

앞에서 variable에 name을 쓴 것 과 달리, Activity를 variable로 삼을 거라면 type에 package명.activity명으로 지정한다. 

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

        <!--type은 packageName.ActivityName으로-->
        <variable
            name="activity"
            type="com.example.sampledatabinding.MainActivity" />

    </data>

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

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{lastName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

(4) Data Class 선언하기

고양이 클래스를 작성해보았다. MainActivity에서 cat 인스턴스 생성시 속성을 바로 넣을 수 있게 생성자도 만들었다. 

public class Cat {
    public String name;
    public int age;
    public String color;
    
    Cat(String catName, int catAge, String catColor){
        this.name = catName;
        this.age = catAge;
        this.color = catColor;
    }
    
}

 

(5) xml <layout> 태그 내에 <data> 태그 작성하기 

앞에서 MainActivity를 variable 태그에서 호출했던 것처럼, CatActivity도 똑같이 호출한다.

<variable> 태그 내에 type은 package명.Class명으로 한다. 

이후 TextView에서 text 데이터로 바로 사용하고 싶다면, @{} 를 이용한다. {}안에는 variable의 name.속성명 이 들어간다. 

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <variable
            name="activity"
            type="com.example.samplebinding.MainActivity" />
        <variable
            name="cat"
            type="com.example.samplebinding.Cat" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout 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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

		<!--여기선 button 무시해도 됨 뒤에 이벤트에서 할 것 -->
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="@{activity::goSecondActivity}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button"
            android:text="@{cat.name}"/>


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

주의: Cat class의 속성들이 public 이어야 xml에서 접근가능하다. 

 

 

(6) Activity에서 Data 초기화하기 

Cat cat = new Cat("야옹이", 3, "흰색"); 으로 객체를 생성한 후, 해당 객체를 바인딩한다. 

바인딩은 binding.setCat(cat); 으로 할 수 있다. 

public class MainActivity extends AppCompatActivity {
    ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setActivity(this);

        Cat cat = new Cat("야옹이", 3, "흰색");
        binding.setCat(cat);
    }
}

* 데이터 바인딩 결과

아주 작지만....cat.name으로 설정한 야옹이가 TextView에 성공적으로 뜬 걸 확인할 수 있다.

 

2. 이벤트 처리

데이터 바인딩으로도 이벤트를 처리할 수 있다. 이벤트 처리 방식은 크게 두 가지가 있다. 

 

(1) Method 참조

 Class에서 선언한 메서드를 데이터 바인딩을 통해 xml에 직접 연결할 수 있다. 예로 버튼을 클릭하면 MainActivity에서 SecondActivity로 이동하는 이벤트 메서드를 작성해보자. 해당 메서드는 goSecondActivity 이다. intent로 화면전환이 이루어진다.

public class MainActivity extends AppCompatActivity {
    ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setActivity(this);

        Cat cat = new Cat("야옹이", 3, "흰색");
        binding.setCat(cat);
    }
    public void goSecondActivity(View view){
        Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
        startActivity(intent);
    }
}

goSecondActivity를 원래라면 button id를 갖고와서 onClickListener로 작성해야 하지만, xml에 직접 해당 메서드를 버튼에다가 연결할 수 있다. 이를 위해선 <layout> 태그 내, <variable>에 메서드가 있는 클래스를 작성하고, 버튼의 onClick 속성에 @{class변수명::메서드명} 으로 작성하면 된다. 예로 com.example.samplebinding.MainActivity 내의 goSecondActivity 메서드라면, @{activity::goSecondActivity} 로 쓰면된다. 

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <variable
            name="activity"
            type="com.example.samplebinding.MainActivity" />
        <variable
            name="cat"
            type="com.example.samplebinding.Cat" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout 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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Go Second Activity"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="@{activity::goSecondActivity}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button"
            android:text="@{cat.name}"/>


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

여기서 주의할 점은, 메서드의 매개변수와 리턴타입이 모두 동일해야 한다. 즉, xml의 onClick의 메서드는 리턴타입이 void이고 매개변수가 View이므로 Activity의 메서드도 다음과 같이 리턴타입, 매개변수가 void와 View이다.

 public void goSecondActivity(View view){
        Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
        startActivity(intent);
    }

[메서드 참조 결과]

버튼 클릭해서 화면 전환 

 

(2) 리스너 바인딩 

리스너 바인딩에서는 임의의 데이터 바인딩 식을 이용할 수 있다. 예로 고양이의 이름과 나이를 로그로 출력하는 버튼을 만들어보았다. 

 

[MainActivity]

1. 고양이 객체 cat 생성

2. binding.setCat(cat); 으로 데이터 바인딩

public class MainActivity extends AppCompatActivity {
    ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setActivity(this);

		// cat 객체 만들고 바인딩 
        Cat cat = new Cat("야옹이", 3, "흰색");
        binding.setCat(cat);
    }
  
    public void printCat(Cat cat){
        Log.d("tag","Cat name: "+cat.name + "Cat age: "+cat.age );
    }
}

 

 

[activity_main.xml]

3. <layout> 태그 내 <variable> 태그 안에 cat, activity 만들기

4. button의 onClick에 리스너 바인딩하기 @{()->activity.printCat(cat)}

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <variable
            name="activity"
            type="com.example.samplebinding.MainActivity" />
        <variable
            name="cat"
            type="com.example.samplebinding.Cat" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout 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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Go Second Activity"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="@{activity::goSecondActivity}"/>

        <Button
            android:id="@+id/printButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:backgroundTint="@color/teal_200"
            android:text="print cat"
            app:layout_constraintBottom_toTopOf="@+id/catName"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button"
            android:onClick="@{()->activity.printCat(cat)}"/>

        <TextView
            android:id="@+id/catName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button"
            android:text="@{cat.name}"/>


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

단, 메서드 참조와 달리 리스너 바인딩은 리턴 타입만 일치하면 된다. 즉, xml onClick에 넣었다고 해서 View가 매개변수일 필요 없고, cat이나 void 등 다양하게 사용할 수 있다. 

public void printCat(Cat cat){
        Log.d("tag","Cat name: "+cat.name + "Cat age: "+cat.age );
    }

[리스너 바인딩 결과]

print cat 버튼을 누르면
고양이 정보가 로그로 출력된다. 

 


Reference

https://salix97.tistory.com/243

https://developer.android.com/codelabs/android-databinding?hl=ko#4 

https://superwony.tistory.com/42 

https://black-jin0427.tistory.com/134 

https://brunch.co.kr/@oemilk/108 

Comments