[MVVM 패턴 공부] DataBinding 기초

LeeEunJae·2022년 9월 15일
1

Study Kotlin

목록 보기
15/20
post-custom-banner

📌 데이터 바인딩이란?

activity_main.xml 에 버튼 하나가 있다고 생각해보자. 그 버튼의 클릭 이벤트를 지정해야 한다면, 가장 쉽고 직관적인 방법으로는 뷰바인딩으로 setOnClickListener 를 사용하는 방법이 있다.
하지만, 뷰바인딩은 ViewController(Activity) 에서 클릭 이벤트를 처리 하는 것이므로 MVVM 패턴을 공부하는 입장에서는 좋지 않은 방법이다.
버튼이 만약 1~2개라면 뷰바인딩으로 해도 좋지만, 만약 10개, 20개라면 코드가 굉장히 길어질 것이다.
데이터 바인딩을 간단히 설명하자면 이렇다.

findViewById 를 사용하지 않아도 된다.
RecyclerView 사용시 각각의 item 들을 bind 해주지 않아도 xml 단에서 처리가 가능하다.
Observable 을 이용해 실시간으로 데이터를 바꿀 수 있다.

📌 기본 세팅

데이터 바인딩을 사용하기 위해서 gradle 파일을 수정해야 한다.

android{
	...
    buildFeatures{
        dataBinding = true
    }
}

📌 사용방법

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

    <data>
    	...
    </data>

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

    </androidx.appcompat.widget.LinearLayoutCompat>



</layout>

데이터 바인딩을 사용하기 위해서는 xml 파일을 약간 수정해줘야 한다.
전체 레이아웃을 layout 태그로 감싸고, data 라는 태그가 존재한다.
data 태그 안에는 데이터 바인딩을 사용하기 위한 변수에 대한 정보가 들어간다.

이 예제에서는 버튼 클릭시 토스트 메세지가 나오고, 데이터 바인딩을 사용해서 Recyclerview 를 사용하는 방법에 대해서 다룰 것이다.

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

액티비티 단에서도 조금 수정을 해줘야합니다. 평소에 많이 사용하던 ViewBinding 의 사용방법과 크게 다른건 없어보입니다.
setContentView 대신 DataBindingUtil.setContentView 를 사용해야합니다.
xml 의 변수를 참조하고 싶은 경우 binding.변수명 으로 불러오면 됩니다.

📌 버튼 클릭 이벤트

버튼 클릭 이벤트를 지정하기 위해서 다음 과정들을 순서대로 해보자

    <data>
        <variable
            name="activity"
            type="com.dldmswo1209.databindingtest.MainActivity" />

    </data>

data 태그 안에 variable 태그를 사용해서 name 과 type 을 지정해준다.
name 은 변수명, type(패키지 경로에 따라 다릅니다) 은 해당 경로를 나타낸다.

	<androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <Button
            android:id="@+id/btnSample"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="button"
            android:onClick="btnClick"/>

    </androidx.appcompat.widget.LinearLayoutCompat>

button 하나를 추가하고 onClick에 btnClick 이라는 이름을 지정해 줍니다.
현재는 btnClick 이라는 함수가 없으므로 오류가 뜰 수 밖에 없습니다.
오류를 없애기 위해서 MainActivity 에서 함수를 만들어 줍시다.

    fun btnClick(view: View){
        Toast.makeText(this, "Button Click", Toast.LENGTH_SHORT).show()
    }
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.activity = this@MainActivity
    }

함수를 만들고 binding.activity 를 이용해서 해당 변수에 MainActivity 를 넣어줍니다.

📌 RecyclerView에 적용하기

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

    <data>
        <variable
            name="activity"
            type="com.dldmswo1209.databindingtest.MainActivity" />

    </data>

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <Button
            android:id="@+id/btnSample"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="button"
            android:onClick="btnClick"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/mainRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            android:orientation="vertical"/>

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

RecyclerView 를 사용하기 위해서 xml 에 RecyclerView 를 추가했습니다.

ProfileData.kt

data class ProfileData(
    var name: String,
    var age: Int
)

간단하게 이름과 나이만 표시하도록 data class 를 만들어줍니다.

rcy_list_item.xml

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

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="com.dldmswo1209.databindingtest.ProfileData" />
    </data>
    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="center_vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="30sp"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:paddingLeft="10dp"
            android:text="@{user.name}"/>'

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="20sp"
            android:gravity="center_vertical"
            android:padding="10dp"
            android:text="@{Integer.toString(user.age)}"/>

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

리사이클러뷰에 들어갈 item 을 만들어줍니다.
데이터 바인딩을 사용하기 위해서 TextView 의 text 속성에 user.name 과 user.age 를 각각 지정해줍니다. 여기서 user 는 variable 에서 name 으로 지정한 변수명 입니다.

ProfileAdapter.kt

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.dldmswo1209.databindingtest.databinding.RcvListItemBinding

class ProfileAdapter : ListAdapter<ProfileData, ProfileAdapter.ViewHolder>(diffUtil){

    inner class ViewHolder(val binding: RcvListItemBinding) : RecyclerView.ViewHolder(binding.root){
        fun onBind(data : ProfileData){
            binding.user = data
        }
    }

    override fun onCreateViewHolder(
        parent: ViewGroup, viewType: Int
    ): ViewHolder = ViewHolder(RcvListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))


    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.onBind(currentList[position])
    }

    companion object{
        private val diffUtil = object: DiffUtil.ItemCallback<ProfileData>(){
            override fun areItemsTheSame(oldItem: ProfileData, newItem: ProfileData): Boolean {
                return oldItem == newItem
            }

            override fun areContentsTheSame(oldItem: ProfileData, newItem: ProfileData): Boolean {
                return oldItem == newItem
            }

        }
    }
}

어답터는 ListAdapter 로 만들었습니다. ViewHolder 부분을 보시면 데이터 바인딩을 사용해서 binding.user = data 로 ProfileData 만 넘겨주면 xml 단에서 처리해주기 때문에 코드가 훨씬 간결해졌습니다.
그 외 부분들은 ViewBinding 을 사용했을 때와 별로 다를게 없어보입니다.

MainActivity 전문

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import com.dldmswo1209.databindingtest.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.activity = this@MainActivity

        setRcv()
    }

    fun btnClick(view: View){
        Toast.makeText(this, "Button Click", Toast.LENGTH_SHORT).show()
    }
    fun setRcv(){
        val profileAdapter = ProfileAdapter()
        binding.mainRecyclerView.adapter = profileAdapter
        profileAdapter.submitList(mutableListOf(
            ProfileData(name = "Kang", age=26),
            ProfileData(name = "Lee", age=24),
            ProfileData(name = "Park", age=24),
            ProfileData(name = "Choi", age=27),
            ProfileData(name = "Kim", age=23),
        ))
    }
}

리사이클러뷰와 어답터를 연결하는 작업을 serRcv 에서 하고 onCreate 에서 호출해줍니다.

👀 출처 및 참고자료

https://kangmin1012.tistory.com/16?category=879935
이 게시물은 위 링크 게시물의 예제를 따라하면서 배운 내용을 토대로 정리한 글 입니다.

profile
매일 조금씩이라도 성장하자
post-custom-banner

0개의 댓글