본문 바로가기

Android/Architecutre

MVVM 시작하기(3) ― ViewModel, 데이터 바인딩(Data Binding)

728x90
반응형

 

앞에서 model부분인 Room의 Entity, Dao, Database를 만들어 주었습니다.

이전 글을 참조해 주세요.

[Android/이론] - MVVM 시작하기(1) ― LiveData, Room을 MVVM패턴으로 사용해보자

[Android/이론] - MVVM 시작하기(2) ― Room Entity, Dao, Database 만들기

이제 View와 Model을 연결하는 ViewModel을 만들고, 연결해야하는데요.

 

이에 앞서, 또 한가지 알아야할 것은, Data Binding입니다.

Data Binding을 설명하면서 View와 ViewModel을 만들어 보겠습니다.

 

ViewModel


먼저, View와 Model을 연결하는 ViewModel을 만들어 줍니다.

  • 종속성 추가
// Lifecycle components
def archLifecycleVersion = '2.2.0'
implementation "androidx.lifecycle:lifecycle-extensions:$archLifecycleVersion"
kapt "androidx.lifecycle:lifecycle-compiler:$archLifecycleVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$archLifecycleVersion"

위 종속성 추가를 위해 또한 gradle 맨 윗부분에 kotlin-kapt plugin을 추가해주어야 합니다.

apply plugin: 'kotlin-kapt'

 

  • MainViewModel.kt
class MainViewModel(application: Application) : AndroidViewModel(application) {
    var main_text: ObservableField<String> = ObservableField("Main")
}

 

String text 하나 변수로 가지는 MainViewModel을 만들어 주었습니다.

버튼 텍스트와 연결될 main_text 변수는 Observable 변수로 선언해 안의 value가 바뀌면 버튼 텍스트가 동적으로 바뀌게 됩니다.

OvservableField의 사용은 databinding을 사용하기 위해 databinding = true를 gradle에 추가해 주어야 합니다.(아래 데이터 바인딩 에서 추가 확인할수 있음.)

main_text.set(value)를 통해 view를 바꿀 수 있습니다.

ViewModel을 상속해도 되지만,

AndroidViewModel을 상속하면 application을 인자로 가질 수 있습니다.

 

  • ViewModel

ViewModel 클래스는 수명주기를 고려하여 UI 관련 데이터를 저장하고 관리하도록 설계되었습니다.

ViewModel 클래스를 사용하면 화면 회전과 같이 구성을 변경할 때도 데이터를 유지할 수 있습니다.

ViewModel 객체는 구성이 변경되는 동안 자동으로 보관되므로, 객체가 소유한 데이터는 다음 activity나  fragment에서 즉시 사용할 수 있습니다.

ViewModel은 LifecycleOwner 또는 view의 특정 인스턴스들보다 오래살도록 디자인 되있으므로, view와 Lifecycle을 모르고도 쉽게 테스트 할 수 있습니다.

하지만 주의할 것은, ViewModel은 Lifecycle 또는 view, activity context를 참조하면 메모리 릭이 발생하므로, 이러한 객체를 참조해서는 안됩니다.

또한 LiveData와 같은 LifecycleObserver들을 포함할 수 있지만, ViewModel은 (LiveData와 같은) lifecycler-aware observable들의 변화를 observe해서는 안됩니다.

만약 context가 필요하다면 AndroidViewModel을 상속하여 application 생성자를 사용하면 됩니다.

 

ViewModel은 Room 및 LiveData와 함께 작업하여 로더를 대체합니다. ViewModel은 기기 구성이 변경 되어도 데이터가 유지되도록 보장하고, 데이터베이스가 변경되면 Room에서 LiveData에 변경을 알려, 알림을 받은 LiveData는 수정된 데이터로 UI를 업데이트 합니다.

 

ViewModel을 사용하기 위해 MainActivity에서 ViewModel에 접근합니다.

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
    }
}

 

혹은 activity-ktx나 fragment-ktx artifact로 by viewModels()를 사용하여 쉽게 접근할 수 있습니다.

class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    }
}

 

 

 

by viewModels() 사용하는 법, by viewModels() 종속성 추가

by viewModels()를 사용하면 ViewModelProvider를 사용하지 않고 viewmodel을 지연 생성할 수 있습니다. private lateinit var viewModel: MyViewModel ... viewModel = ViewModelProvider(this).get(MyViewModel:..

ddangeun.tistory.com

 

 


 

 

Data Binding


이제, view를 만들고 데이터 바인딩을 사용하여 view와 binding할것입니다.

 

  • 종속성 추가
android {
    buildFeatures{
        dataBinding = true
    }
}
dependencies {
...
    //Data Binding
    kapt "com.android.databinding:compiler:4.0.1"
    
    // Material design
    def materialVersion = '1.1.0'
    implementation "com.google.android.material:material:$materialVersion"
...
}

 

데이터를 추가할 수 있는 버튼을 만들고, Room의 데이터를 RecyclerView를 통해 보여줄 것입니다.

 

  • recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/user_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_orange_light"
        android:textSize="15sp"
        android:textColor="#000"/>
    <TextView
        android:layout_marginStart="20dp"
        android:id="@+id/gender"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_orange_light"
        android:textSize="15sp"
        android:textColor="#000"/>

    <TextView
        android:layout_marginStart="20dp"
        android:id="@+id/birth"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_orange_light"
        android:textSize="15sp"
        android:textColor="#000"/>
</LinearLayout>

 

  • activity_main.xml

데이터 바인딩을 사용하기위해 <layout> 으로 기존 코드를 감싸주고, 아래처럼 <data> 안에 변수 viewModel을 type MainViewModel로 추가해주면 됩니다.

<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">
    <data>
        <variable
            name="viewModel"
            type="com.example.mymvvm.MainViewModel" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <Button
            android:layout_marginLeft="10dp"
            android:id="@+id/btnAdd"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="button"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="10dp"
            tools:listitem="@layout/recycler_item" />
    </LinearLayout>
</layout>

  • data binding

이중 Button 부분을 data binding 해보겠습니다.

<Button
   android:layout_marginLeft="10dp"
   android:id="@+id/btnAdd"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@{viewModel.main_text}"
   android:onClick="@{()->viewModel.onClickButton()}"/>

 

Button 부분의 layout을 수정해줍니다.

text와 onClick을 확인해주세요. viewModel의 main_text변수와 text를 바인딩하고,

클릭시 viewModel의 onClickButton()메서드를 실행하도록 해줍니다.

 

이제 MainActivity에서 viewModel을 binding 해줍니다.

setContentView를 없애주고 binding을 통해 layout을 설정해주면 됩니다.

class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.viewModel = viewModel
        binding.lifecycleOwner = this
    }
}

 

layout 이름이 activity_main이기 때문에 ActivityMainBinding이 됩니다.

binding.viewModel = viewModel을 통해 layout의 viewModel변수를 viewModel과 바인딩해주었습니다.

 

  • binding.lifecycleOwner = this : 이 코드는 LiveData를 Databinding으로 쓸경우 꼭 써줘야 합니다. Observable 대신 LiveData를 사용하여 DataBinding 가능합니다.

 

ViewModel은 공유 가능합니다. 만약 activity의 ViewModel을 Fragment에서도 쓰고 싶다면, 아래와 같이 사용할 수 있습니다.

private lateinit var viewModel: MainViewModel
private lateinit var binding: FragmentMainBinding
...
override fun onCreateView(
     inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false)
        activity?.let {
            viewModel = ViewModelProvider(it).get(MainViewModel::class.java)
            binding.viewModel = viewModel
            binding.lifecycleOwner = this 
        }
        return binding.root
    }

 

 

이제, ViewModel로 가서 로직을 짜면 됩니다.

class MainViewModel(application: Application) : AndroidViewModel(application) {
    var main_text: ObservableField<String> = ObservableField("Main")
    val mApplication = application

    fun onClickButton(){
        // TODO: Click 시 Room에 데이터를 추가해야 함.
        Toast.makeText(mApplication,"Click!",Toast.LENGTH_SHORT).show()
    }
}

 

 

버튼에 Data binding이 잘 된것을 확인할 수있습니다.

 


 

이어서, Repository를 만들고, Room과 ViewModel, View를 연결해보겠습니다.

[Android/이론] - MVVM 시작하기(4) ― Repository, Model-View 연동

 

참고 문서

728x90
반응형