BuNa_
IT Story
BuNa_
전체 방문자
오늘
어제
  • 분류 전체보기 (117)
    • CS (14)
      • 운영체제 (8)
      • 네트워크 (0)
      • Design Pattern (1)
      • OOP (4)
    • 대외활동 (24)
      • 우아한테크코스 (14)
      • DND 동아리 (4)
      • UMC 동아리 (5)
      • 해커톤 (1)
    • Android (29)
      • MVVM (2)
      • 스터디 (11)
      • Compose (3)
      • Unit Test (1)
    • Project (5)
      • 어따세워 (5)
      • DnD 과외 서비스 (0)
    • Programming (11)
      • Kotlin (4)
      • 파이썬 (7)
    • Git (1)
    • 인공지능 (22)
    • 백준 (8)
    • 기타 (3)
      • IntelliJ (1)
      • 일상 (0)

블로그 메뉴

  • 홈

공지사항

인기 글

태그

  • k-means++
  • 우아한테크코스
  • 운영체제
  • 파이썬
  • 원시값 포장
  • 객체지향 생활체조
  • UMC
  • Android
  • RecyclerView
  • 다이나믹 프로그래밍
  • 우테코 5기
  • 백준
  • Compose
  • 안드로이드
  • MVVM
  • 우테코
  • ViewModel
  • 컴공선배
  • 선형회귀
  • K-means
  • 우테코 프리코스
  • External fragmentation
  • 딥러닝
  • 어따세워
  • 외부 단편화
  • 인공지능 분류
  • Ai
  • 인공지능
  • Baekjoon
  • 셀레니움

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
BuNa_

IT Story

[Android][1] MVVM패턴 Databinding 맛보기!
Android/MVVM

[Android][1] MVVM패턴 Databinding 맛보기!

2021. 12. 14. 21:56

* 본 내용은 스스로 학습한 내용을 정리한 게시글입니다. *

 

데이터바인딩(DataBinding)이란?

 

기존에는 레이아웃의 뷰를 참조하기 위해서는 아래와 같이 findViewById() 메서드를 사용하였다.

val button: Button = findViewById(R.id.my_button)

 

하지만 이러한 방식은 같은 한 화면에는 여러 개의 뷰가 존재하는 액티비티 클래스에

동일한 메서드를 너무나도 많이 호출시키게 만들어 클린 코드에 적합하지 못하다고 판단하였다.

그래서 등장하게 된 것이 바로 데이터바인딩(DataBinding)이다.

 

데이터바인딩을 사용했을 때의 장점

1. findViewById를 연속적으로 사용할 필요 없이 binding 객체 하나만 있으면 뷰를 참조할 수 있다.

2. RecyclerView의 Item에 DTO 모델을 bind 해주기만 하면 된다.

3. Observer 패턴을 적용하여 실시간으로 데이터를 업데이트할 수 있다.

 

 

설정 방법

데이터 바인딩을 사용하기 위해서는 build.gradle(app level)로 들어가 아래와 같이 작성해준다.

android {
    ...
    dataBinding {
      enabled = true
    }
    ...
}

 

만약 코틀린을 사용한다면 아래의 코드를 추가로 작성해준다.

여기서 gradleVersion에는 본인 프로젝트의 gradle 버전을 넣어준다.

plugins {
    id 'kotlin-kapt'
}
...
dependencies {
	...
    kapt 'com.android.databinding:compiler:$gradleVersion'
	...
}

 

위 코드를 작성하고 Sync Now를 눌렀을 때 정상적으로 빌드가 되었다면 성공이다.

이제부터 바인딩을 시작해보자!

 

 

우선 XML로 이동하여 상위 뷰 태그에 커서를 대고 Ctrl+Enter를 눌러보면 위와 같은 옵션들이 나오는데

'Convert to data binding layout'을 선택해준다.

 

<!--activity_main.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>

    </data>

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

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

 

그럼 이렇게 <layout/> 태그로 한 번 더 감싸지는 모습을 볼 수 있다.

그 하위에 <data/> 태그에 대해서는 아래에서 자세히 다루도록 하겠다.

이제 Activity로 이동해서 바인딩 객체를 생성해주어야 한다.

 

package com.practice.buna.mvvm

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import com.practice.buna.mvvm.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 기존의 setContentView 사용 안 함.
        //setContentView(R.layout.activity_main)
        // 데이터 바인딩 객체 생성
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }
}

 

데이터바인딩을 하면서 기본으로 작성되어 있던 setContentView() 메서드는 더 이상 사용하지 않는다.

대신에 DatabindingUtil.setContentView()를 호출해주는데, 첫 번째 인자로는 Activity 객체, 두 번째로는 레이아웃 id를 입력한다.

그럼 사용하기 위한 세팅은 모두 끝이 났다.

 

이제 예제를 통해 살펴보자!

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

    </data>

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

        <ImageView
            android:id="@+id/camera_button"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/ic_menu_camera"
            android:clickable="true"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/camera_name"/>

        <TextView
            android:id="@+id/camera_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:textColor="@color/black"
            android:textSize="20sp"
            android:text="카메라 버튼"/>

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

 

위 xml은 중앙에 이미지 뷰 하나와 텍스트뷰 하나가 있는 코드이다.

각각 id를 camera_button, camera_name로 지정하였다.

 

 

그럼 이렇게 지정한 뷰 ID는 '_ (Under Scope)'를 기준으로 대문자로 바뀌어, 레이아웃에 접근할 수 있도록 설정된다.

코틀린의 with안에 binding을 넣어주어, 기존의 길고 보기 싫던 findViewById를 반복해서 사용할 필요가 없어졌다.

만약 카메라 버튼을 눌렀을 때 cameraName 텍스트뷰의 문자가 바뀌게 하기 위해서는 어떻게 해야 할까??

 

그러기 위해서는 여러 방법이 있겠지만, 조금 더 MVVM 패턴스럽게 접근하기 위해

위에서 언급했던 <data/> 태그와 ViewModel, LiveData를 활용해보자.

 

dependencies {
	...
    def lifecycle_version = "2.3.0"
    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    ...
}

 

이 기능들을 사용하기 위해 아까처럼 gradle(app level) 파일로 이동하여 dependencies 태그 안에 위 코드를 삽입해준다.

 

package com.practice.buna.mvvm.feature.viewmodel

import android.view.View
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {

    // 카메라 버튼이 눌렸는지 확인하기 위한 MutableLiveData
    var isPressed = MutableLiveData<Boolean>(false)

    // 카메라 눌림 감지를 메서드
    fun onClickCameraBtn(v: View) {
        // 버튼 눌림 상태 Toggle
        isPressed.value = isPressed.value?.not()
    }
}

 

부모 클래스로 ViewModel()을 사용하는 뷰 모델을 만들어준다.

그리고 버튼이 눌렸는지 감지하기 위한 isPressed 옵저버 변수를 하나 생성해준다.

만약 버튼이 눌리면 onClickCameraBtn() 메서드가 호출되어 변수를 토글(Toggle)시켜줄 것이다.

 

<?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="viewModel"
            type="com.practice.buna.mvvm.feature.viewmodel.MainViewModel" />
    </data>

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

        <ImageView
            android:id="@+id/camera_button"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/ic_menu_camera"
            android:clickable="true"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/camera_name"
            android:onClick="@{viewModel::onClickCameraBtn}"/>
...

</layout>

 

xml로 이동하여 data 태그 안에 뷰 모델을 작성해준다.

'name'에는 xml 내에서 사용할 객체의 변수명으로 본인이 사용하기 편한 이름을 작성해주면 된다.

'type'에는 사용하고자 하는 클래스를 작성해준다.

 

그리고 ImageView 맨 아래 부분을 보면 알 수 있듯이, onClick에 뷰 모델의 onClickCameraBtn()를 추가하였다.

만약 이 ImageView를 클릭하면 해당 함수를 실행시키겠다. 라는 의미이다.

 

마지막으로 Activity로 이동하여 ViewModel을 생성하고 xml에 전달해주어야 할 일이 남아있다.

 

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding
    lateinit var mainViewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 기존의 setContentView 사용 안 함.
        //setContentView(R.layout.activity_main)

        // 데이터 바인딩 객체 생성
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        // 뷰모델 생성
        mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)

	// isPressed 변수 관찰
        mainViewModel.isPressed.observe(this) { isPressed ->
            if(isPressed) {
                binding.cameraName.text = "카메라 버튼 눌림!"
            } else {
                binding.cameraName.text = "카메라 버튼 뗌!"
            }
        }

        with(binding) {
            viewModel = mainViewModel // xml의 viewModel과 View에서 생성한 viewModel을 바인딩
        }
    }
}

 

ViewModel 객체를 생성하는 데에는 다양한 방법이 있지만, 이 포스팅에서는 ViewModelProvider를 사용하여 생성해주겠다.

그리고 뷰 모델의 isPressed 변수를 관찰하면 되는데, 버튼을 누를 때마다 눌림 상태가 변경되어 텍스트를 업데이트해준다!

이렇게 생성한 뷰 모델을 레이아웃의 viewModel로 전달해주면 끝이다.

 

한 번 AVD를 실행시켜보자!

 

 

 

예상대로 클릭 전에는 "카메라 버튼 뗌!" 이라는 텍스트가 나오고,

버튼을 한 번 클릭하면 "카메라 버튼 눌림!" 텍스트로 업데이트된다.

 

이번 포스팅에서는 LiveData와 ViewModel, DataBinding의 기본적인 기능에 대해 다루어보았습니다.

내용에 오류가 있거나, 질문이 있으신 분들은 댓글을 남겨주시면 감사하겠습니다! 😊

저작자표시 비영리 변경금지 (새창열림)

'Android > MVVM' 카테고리의 다른 글

[Android][0] MVVM 패턴이란?  (1) 2021.11.28
    'Android/MVVM' 카테고리의 다른 글
    • [Android][0] MVVM 패턴이란?
    BuNa_
    BuNa_
    안드로이드 개발자를 향해 달리고 있는 공대생입니다! 🧑 Android, Kotlin, Java, Python 등 학습하고 있는 내용과 프로젝트를 주로 업로드하고 있습니다. 지적과 조언은 언제나 환영입니다!😊 github : https://github.com/tmdgh1592

    티스토리툴바