본문 바로가기
안드로이드 공부 노트

Android Jetpack - 5편 ViewModel + DataBinding + LiveData 통합 사용법

by 지게요 2022. 2. 24.
728x90
반응형

이번에는 Android Jetpack - 4편에 설명한 Data Binding을 ViewModel + LiveData를 활용해서 구현해보겠다.

본인이 이해한 것이 정확하지 않을 수 있어서 참고만 하길 바란다.

 

예제로는 간단히 YouTube처럼 좋아요와 싫어요 개수를 구하고 총합계를 구하는 예제로 해보겠다.

# 사용법

1. bulid.gradle (Module) 추가

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

2. ViewModel + LiveData 생성

package com.example.databinding_ex.viewmodel

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel(){
    var likeCount : MutableLiveData<Int> = MutableLiveData()
    var noLikeCount : MutableLiveData<Int> = MutableLiveData()
    var totLikeCount : MutableLiveData<Int> = MutableLiveData()

    init {
        likeCount.value = 0
        noLikeCount.value = 0
        totLikeCount.value = 0
    }

    // 좋아요 버튼
    fun likeAdd(){
        likeCount.value = likeCount.value?.plus(1)
        totLikeCount.value = likeCount.value?.minus(noLikeCount.value!!)
    }
    
    // 싫어요 버튼
    fun noLikeAdd(){
         noLikeCount.value = noLikeCount.value?.plus(1)
         totLikeCount.value = likeCount.value?.minus(noLikeCount.value!!)
    }
    
    // 초기화 버튼
    fun reset(){
        totLikeCount.value = 0
        likeCount.value = 0
        noLikeCount.value = 0
    }
}

수정이 가능한 MutableLiveData를 생성해준다. 그 후 좋아요, 싫어요, 초기화 버튼을 눌렀을 때 실행할 함수들을 만들어준다.

3. activity.xml / string.xml 삽입 및 수정

<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">
    <data>
        <variable
            name="viewModel"
            type="com.example.databinding_ex.viewmodel.MainViewModel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageButton
            android:id="@+id/like"
            android:layout_width="45dp"
            android:layout_height="65dp"
            android:layout_marginStart="5dp"
            android:layout_marginTop="5dp"
            android:src="@drawable/ic_thumb_up"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="15dp"
            android:layout_marginTop="25dp"
            android:text="@{@string/Count(viewModel.likeCount)}"
            android:textSize="20sp"
            app:layout_constraintStart_toEndOf="@+id/like"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageButton
            android:id="@+id/noLike"
            android:layout_width="45dp"
            android:layout_height="65dp"
            android:layout_marginStart="5dp"
            android:layout_marginTop="5dp"
            android:src="@drawable/ic_thumb_down_24"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/like" />

        <Button
            android:id="@+id/reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="45dp"
            android:layout_marginEnd="35dp"
            android:text="초기화"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{@string/Count(viewModel.noLikeCount)}'
            android:textSize="20sp"
            android:layout_marginStart="15dp"
            android:layout_marginTop="45dp"
            app:layout_constraintStart_toEndOf="@+id/noLike"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{@string/TotCount(viewModel.totLikeCount)}"
            android:textSize="20sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/noLike" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

<strings.xml> (res -> values -> strings.xml)

<resources>
    <string name="app_name">DataBinding_EX</string>
    <string name="Count">%d 개</string>
    <string name="TotCount">총합 : %d 개</string>
</resources>

<data> 태그 안에 <variable>에 위에서 만들어준 viewModel을 선언해준다.

그 후 TextView안에 text를 string format을 사용해 넣어준다.

4. MainActivity.kt 작성

package com.example.databinding_ex

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import com.example.databinding_ex.databinding.ActivityMainBinding
import com.example.databinding_ex.viewmodel.MainViewModel

class MainActivity : AppCompatActivity() {
    // by lazy로 값을 선언 해준 후 ViewModelProvider로 ViewModel를 만든다
    private val viewModel: MainViewModel by lazy {
        ViewModelProvider(this).get(MainViewModel::class.java)
    }
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.viewModel = viewModel
        binding.lifecycleOwner = this

        binding.like.setOnClickListener {
            viewModel.likeAdd()
        }

        binding.noLike.setOnClickListener {
            viewModel.noLikeAdd()
        }

        binding.reset.setOnClickListener {
            viewModel.reset()
        }

    }
}

ViewModelProvider로 ViewModel을 만들어준다.

각 버튼에 ViewModel에서 만들어준 함수들을 실행한다.

 

만약 여기서 값들이 변경될 때 어떠한 작업을 하고 싶으면 아래와 같이 observe를 달아주면 된다.

private fun observerViewModel(){
    // 좋아요 개수
    viewModel.likeCount.observe(this,object : Observer<Int>{
        override fun onChanged(likeCount: Int?) {
            Log.d(TAG,"likeCount 변경 됨 likeCount 값 : $likeCount" )
        }
    })
    // 싫어요 개수
    viewModel.noLikeCount.observe(this, object : Observer<Int>{
        override fun onChanged(noLikeCount: Int?) {
            Log.d(TAG,"noLikeCount 변경 됨 noLikeCount 값 : $noLikeCount" )
        }
    })

    // 총 개수
    viewModel.totLikeCount.observe(this, object : Observer<Int>{
        override fun onChanged(totLikeCount: Int?) {
            Log.d(TAG,"totLikeCount 변경 됨 totLikeCount 값 : $totLikeCount" )
        }
    })
}

전체 MainActivity 코드

package com.example.databinding_ex

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.databinding_ex.databinding.ActivityMainBinding
import com.example.databinding_ex.viewmodel.MainViewModel

class MainActivity : AppCompatActivity() {
    // by lazy로 값을 선언 해준 후 ViewModelProvider로 ViewModel를 만든다
    private val viewModel: MainViewModel by lazy {
        ViewModelProvider(this).get(MainViewModel::class.java)
    }
    private lateinit var binding: ActivityMainBinding
    private val TAG = "MainActivity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.viewModel = viewModel
        binding.lifecycleOwner = this

        binding.like.setOnClickListener {
            viewModel.likeAdd()
        }

        binding.noLike.setOnClickListener {
            viewModel.noLikeAdd()
        }

        binding.reset.setOnClickListener {
            viewModel.reset()
        }

        observerViewModel()

    }

    private fun observerViewModel(){
        // 좋아요 개수
        viewModel.likeCount.observe(this,object : Observer<Int>{
            override fun onChanged(likeCount: Int?) {
                Log.d(TAG,"likeCount 변경 됨 likeCount 값 : $likeCount" )
            }
        })
        // 싫어요 개수
        viewModel.noLikeCount.observe(this, object : Observer<Int>{
            override fun onChanged(noLikeCount: Int?) {
                Log.d(TAG,"noLikeCount 변경 됨 noLikeCount 값 : $noLikeCount" )
            }
        })

        // 총 개수
        viewModel.totLikeCount.observe(this, object : Observer<Int>{
            override fun onChanged(totLikeCount: Int?) {
                Log.d(TAG,"totLikeCount 변경 됨 totLikeCount 값 : $totLikeCount" )
            }
        })
    }
}


 

조금 더 좋은 방법이 있을 거 같은데 아직 실력이 부족해서 이 정도로 해보았다.

다음에는 MVVM패턴을 이용해 활용해 보겠다.

 

 

GitHub - JiSeokYeom/DataBinding_EX

Contribute to JiSeokYeom/DataBinding_EX development by creating an account on GitHub.

github.com

 

참고 자료
https://webcache.googleusercontent.com/search?q=cache:oX_8a_7twLkJ:https://hanyeop.tistory.com/214+&cd=2&hl=ko&ct=clnk&gl=kr
아키텍처를 알아야 앱 개발이 보인다 - 옥수환 저자
반응형