코틀린

[코틀린] Room (ViewModel, DataBinding, LiveData) 2

edit0 2021. 3. 28. 22:40

이번 포스팅에서는 Room에 대한 설명은 하지 않습니다.

 

Room을 처음 접하시는 분들은 아래 링크를 참고해주시면 감사하겠습니다.

edit0.tistory.com/48

 

[코틀린] Room (SELECT, INSERT, UPDATE, DELETE)

이번 포스팅에서는 안드로이드 내부 데이터베이스 Room에 대해 알아보도록 하겠습니다. 다들 아시겠지만, 데이터베이스는 기본적으로 데이터를 담아두고 있는 공간입니다. 그리고 이 데이터를 S

edit0.tistory.com

오늘은 바로 이전 포스팅에서 다루었던 Room 코드를 ViewModel, LiveData, DataBinding 을 적용하여 바꿔보도록 하겠습니다.

 

그래들.app 설정

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
    id 'kotlin-kapt'
}

android {
    ...

    dataBinding {
        enabled true
    }
}

dependencies {
    ...

    implementation 'androidx.room:room-runtime:2.2.5'
    kapt 'androidx.room:room-compiler:2.2.5'
    implementation 'androidx.activity:activity-ktx:1.1.0'
}

 

데이터베이스 테이블인 User 클래스(데이터 모델 클래스)는 이전과 동일합니다.

 

User.kt

@Entity
data class User (
    var name:String,
    var age: Int,
    var major: String
) {

    @PrimaryKey(autoGenerate = true)
    var id: Int = 0
}

 

UserDao 클래스에서는 모든 데이터를 조회하여 가지고 오는 getAll() 메소드 타입을 LiveData로 감싸줍니다.

 

 

UserDao.kt

@Dao
interface UserDao {

    @Insert
    fun insert(user: User)

    @Query("DELETE FROM User WHERE name = :NAME")
    fun deleteUsingNAME(NAME: String)

    @Query("UPDATE User SET name = :newNAME WHERE name = :oldNAME")
    fun updateUsingNAME(newNAME: String, oldNAME: String)

    @Query("SELECT * FROM User WHERE name = :NAME")
    fun selectUsingNAME(NAME: String): List<User>

    @Query("SELECT * FROM User")
    fun getAll(): LiveData<List<User>>

}

 

UserDatabase 클래스는 이전과 동일합니다.

 

UserDatabase.kt

@Database(entities = [User::class], version = 1)
abstract class UserDatabase : RoomDatabase(){
    abstract fun UserDao():UserDao
}

 

데이터바인딩을 사용하므로 그에 맞게 XML을 설정해줍니다.

데이터바인딩과 뷰모델을 잘 모르시겠다면 참고하시면 도움될 것이라 생각합니다.

edit0.tistory.com/46

 

[코틀린] ViewModel, Databinding 사용해보기 (with LiveData)

Databinding은 말그대로 데이터를 묶는 의미로, XML(UI)과 데이터(ViewModel 내)를 결합하여 사용할 수 있도록 도와주는 라이브러리입니다. 물론 UI를 담당하는 Java나 Kotlin 소스 코드에서 데이터나 UI 업

edit0.tistory.com

 

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"
    tools:context=".MainActivity">

    <data>
        <variable
            name="viewModel"
            type="com.kotlin.k_room_with_viewmodel.Main_ViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="2">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <EditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/name"
                    android:hint="이름"
                    android:text="@={viewModel.name}"/>

                <EditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/age"
                    android:hint="나이"
                    android:text="@={viewModel.age}"/>

                <EditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/major"
                    android:hint="전공"
                    android:text="@={viewModel.major}"/>

                <Button
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/addButton"
                    android:text="추가하기"
                    android:onClick="@{() -> viewModel.add()}"/>

                <EditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/delete"
                    android:hint="삭제하고 싶은 이름"
                    android:text="@={viewModel.deleteNmae}"/>

                <Button
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/deleteButton"
                    android:text="삭제하기"
                    android:onClick="@{() -> viewModel.delete()}"/>

                <EditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/oldNAME"
                    android:hint="기존 이름"
                    android:text="@={viewModel.oldname}"/>

                <EditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/newNAME"
                    android:hint="새로운 이름"
                    android:text="@={viewModel.newname}"/>

                <Button
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/updateButton"
                    android:text="업데이트"
                    android:onClick="@{() -> viewModel.update()}"/>

                <EditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/findName"
                    android:hint="검색하고 싶은 이름"
                    android:text="@={viewModel.selectName}"/>

                <Button
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/findNameButton"
                    android:text="이름으로 검색"
                    android:onClick="@{() -> viewModel.select()}"/>

            </LinearLayout>
        </ScrollView>


        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="20dp"
                    android:id="@+id/text"
                    android:text="@{viewModel.result}"/>
            </LinearLayout>
        </ScrollView>

    </LinearLayout>

</layout>

 

액티비티 객체를 얻기 위해 AndroidViewModel()을 상속 받았습니다.

이전 포스팅에서는 Room 데이터베이스를 메인액티비티에서 만들어주었지만, 여기서는 뷰모델에서 만들어줍니다.

그리고 .allowMainThreadQueries().build() 부분에서 .allowMainThreadQueries() 부분이 빠졌습니다.

이유는 AsyncTask를 사용하여 메인 쓰레드가 아닌 새로운 스레드를 만들어 처리해주었습니다.

 

변수 userResult 타입을 보시면 LiveData로 감싸져 있는 것을 보실 수 있습니다.

아까 Dao 클래스에서 LiveData로 감싸주었던 값을 받을 변수입니다.

 

Main_ViewModel.kt

class Main_ViewModel(application: Application) : AndroidViewModel(application){

    var name = ObservableField<String>()
    var age = ObservableField<String>()
    var major = ObservableField<String>()
    var deleteNmae = ObservableField<String>()
    var oldname = ObservableField<String>()
    var newname = ObservableField<String>()
    var selectName = ObservableField<String>()

    var result = ObservableField<String>()
    var db: UserDatabase

    var userResult: LiveData<List<User>>


    init {
        db = Room.databaseBuilder(getApplication(), UserDatabase::class.java, "user").build()
        userResult = db.UserDao().getAll()
    }

    fun add(){
        UserAsyncTask().execute("add")
    }

    fun delete(){
        UserAsyncTask().execute("delete")
    }

    fun update(){
        UserAsyncTask().execute("update")
    }

    fun select(){
        UserAsyncTask().execute("select")
    }

    inner class UserAsyncTask : AsyncTask<String,Unit,Unit>(){
        override fun doInBackground(vararg params: String?) {
            var type = params[0]

            when(type){
                "add" -> {
                    var user = User(name.get().toString(), Integer.parseInt(age.get().toString()), major.get().toString())
                    db.UserDao().insert(user)

                    name.set("")
                    age.set("")
                    major.set("")
                }

                "delete" -> {
                    db.UserDao().deleteUsingNAME(deleteNmae.get().toString())

                    deleteNmae.set("")
                }

                "update" -> {
                    db.UserDao().updateUsingNAME(newname.get().toString(), oldname.get().toString())

                    newname.set("")
                    oldname.set("")
                }

                "select" -> {
                    var selectedName = db.UserDao().selectUsingNAME(selectName.get().toString())
                    selectName.set("")
                    var str:TextView = TextView(getApplication())
                    str.text = "검색 결과"
                    for(i in selectedName){
                        str.append("\n ${i.id}, ${i.name}, ${i.age}, ${i.major}")
                    }

                    result.set(str.text.toString())
                }
            }
        }
    }

}

 

viewModel 변수에 ViewModelProvider를 통해 ViewModel의 액티비티 Lifecycle을 등록한 값을 바인딩 변수에 넣어줍니다.

LiveData를 사용하기 위해 setLifecyclerOwner에 현재 액티비티 Lifecycle을 설정해줍니다.

Observer를 사용하여 뷰모델의 userResult 변수를 감시해줍니다. userResult의 값이 변할 때 마다 호출될 것 입니다.

 

MainActivity.kt

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var viewModel = ViewModelProvider(this).get(Main_ViewModel::class.java)

        binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
        binding.setLifecycleOwner(this)
        binding.viewModel = viewModel


        var user_result = Observer<List<User>> { result ->
            Toast.makeText(applicationContext, "Observer 호출됨 ", Toast.LENGTH_SHORT).show()

            var userlistText = "User List"
            for(i in 0 until result.size){
                userlistText += "\n ${result.get(i).id}, ${result.get(i).name}, ${result.get(i).age}, ${result.get(i).major}"
            }

            viewModel.result.set(userlistText)
        }
        viewModel.userResult.observe(this, user_result)

    }
}

 

결과

 

이전 포스팅의 결과와 다른 점은 데이터가 바뀌면 Observer가 호출되어 토스트 메시지가 띄어질 것 입니다.

 

 

감사합니다.