sean.jin
Spark Code Blog
sean.jin
전체 방문자
오늘
어제
  • 분류 전체보기
    • 개발공부
      • Kotlin
      • LeetCode
      • Algorithm
      • React
    • 주식차트
    • 책리뷰
    • 유틸리티

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 오
  • 네마녀의날
  • 트리플 위칭데이
  • 주식투자
  • 자기개발
  • 부의 추월차선
  • 경제
  • 책리뷰
  • 쿼드러플위칭데이
  • 아빠와 딸의 주식투자 레슨
  • 주식입문자
  • 주식책리뷰
  • 초보
  • 변동성
  • 책
  • 책추천

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
sean.jin

Spark Code Blog

[Kotlin] MVVM Room Database : @Update로 Database 데이터 수정 - 간단한 저장소 만들기 4편
개발공부/Kotlin

[Kotlin] MVVM Room Database : @Update로 Database 데이터 수정 - 간단한 저장소 만들기 4편

2021. 7. 27. 22:06
반응형

지난 포스트

 

[Kotlin] MVVM Room Database 3편 : 간단한 저장소 만들기 - RecyclerView Adapter에 ViewModel LiveData 적용하기

지난 포스트 [Kotlin] MVVM Room Database 2편 : 간단한 저장소 만들기 - Navigation Graph, NavHostFragment, Insert 구성하기 지난 포스트 [Kotlin] MVVM Room Database 1편 : 간단한 저장소 만들기 - Entity, R..

underdog11.tistory.com

 

목표


이 포스트는 총 5편으로 이루어져 있습니다. 밑줄 친 항목이 이번 포스트에서 다룰 항목 입니다. 

 

RoomDatabase 개념

  • Roomdatabase의 기본 요소인 Entity, Database, Dao 개념/구현 (1편)
  • LiveData, ViewModel, Repository를 이용하여 MVVM 아키텍처 구성(1편)

Insert 구성

  • Navigation Graph , NavHostFragment추가(2편)
  • 각 list, update, add fragment의 layout 및 프래그먼트 구현(2편)
  • Add Fragment를 여는 플로팅 버튼 추가 (2편)
  • DB browser를 통해 database 확인(2편)
  • RecyclerView에 들어갈 ItemLayout과 ListAdapter 추가(3편)
  • RecyclerView와 LiveData 적용하기(3편)
  • 위와 같이 Room database에 저장된 값을  Recyclerview와 연결하기 (3편)

Update 구성

  • Update layout 생성(4편)
  • Update Layout Navigation Graph에 연결(4편)
  • Navigation Argument 추가하기(4편)
  • 데이터베이스에 수정된 값 업데이트(4편)

Delete 구성

  • 제거 버튼 Action bar에 추가(5편)
  • 선택한 데이터 삭제(5편)
  • 모든 데이터 삭제(5편)

 

Update 구성


 

Recyclerview에 있는 리스트를 클릭했을때 클릭한 데이터를 수정할수있도록, Update를 구성하려합니다. 그러기위해서 새로운 fragment가 필요합니다. 

 

 

Update 레이아웃 생성

 

layout에는 Add에 썻던 layout에 타이틀만 바꿔서 사용하였습니다. id만 바꿔주겠습니다. 

 

  • fragment_update

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="24dp"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".fragments.update.UpdateFragment">

    <EditText
        android:id="@+id/updateFirstName_et"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="88dp"
        android:ems="10"
        android:hint="First Name"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/updateLastName_et"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Last Name"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="@+id/updateFirstName_et"
        app:layout_constraintStart_toStartOf="@+id/updateFirstName_et"
        app:layout_constraintTop_toBottomOf="@+id/updateFirstName_et" />

    <EditText
        android:id="@+id/updateAge_et"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="age"
        android:inputType="number"
        app:layout_constraintEnd_toEndOf="@+id/updateLastName_et"
        app:layout_constraintStart_toStartOf="@+id/updateLastName_et"
        app:layout_constraintTop_toBottomOf="@+id/updateLastName_et" />

    <Button
        android:id="@+id/update_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="@+id/updateAge_et"
        app:layout_constraintStart_toStartOf="@+id/updateAge_et"
        app:layout_constraintTop_toBottomOf="@+id/updateAge_et" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

 

Update Layout Navigation Graph에 연결

 

이제 recyclerview list가 클릭되면 update fragment로 화면전환이 이뤄지도록 2편에서 다뤘던 navigation graph에 update fragment를 list fragment 와 연결해주겠습니다. 

 

 

 

Navigation Argument로 데이터 전달

 

Update fragment에 Argument가 필요합니다. 

 

Argument는 navigation 대상간에 데이터 전달을 할때 사용하게 됩니다. 

 

Argument를 사용하면 유저가 위에서 list Fragment에서 아이템을 클릭하면 update fragment로 이동하면서 어떤 USER 오브젝트 아이템이 클릭되었는지 전달하게 됩니다. 

 

Argument를 추가해 보도록 하겠습니다. 

 

Argument를 추가하기전 USER 모델을 Parcel가능하게 만들어야합니다. 왜냐하면 fragment사이에서 User오브젝트 자체를 주고받을수없고 오브젝트를 먼저 parcelable 하게 해야 전달 가능해집니다. User 모델로 가줍니다.

 

  • model/user

 

@Parcelize //추가
@Entity(tableName = "user_table")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id:Int,
    val firstName:String,
    val lastName:String,
    val age:Int
):Parcelable{ //추가

}
  • res/navigation/my_nav

my_nav로 들어가준후 designtab으로 이동합니다. 그리고 Argument Tab에서 +를 눌러줍니다.

Add Argument창이 뜨면 이름을 CurrentUser로 하고 타입을 custom Parcelable로 하면 Parcel가능한 오브젝트 리스트가 보이게됩니다. 

방금 parcelable로 설정해준 User모델을 볼수있습니다. 선택후 OK눌러줍니다. 

그러면 argument가 추가되었습니다. 

 

이제 listFragment에서 리스트에 setOnClickListener를 추가해줘야합니다.

 

  • fragments/list/ListAdapter

 

class ListAdapter: RecyclerView.Adapter<ListAdapter.MyViewHolder>() {

    private var userList = emptyList<User>() //

    class MyViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.custom_row,parent,false))
    }

    override fun getItemCount(): Int {
        return userList.size
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        
        val currentItem = userList[position]
        holder.itemView.id_txt.text = currentItem.id.toString()
        holder.itemView.firstName_txt.text = currentItem.firstName
        holder.itemView.lastName_txt.text = currentItem.lastName
        holder.itemView.age_txt.text = currentItem.age.toString()

//추가 시작
        holder.itemView.rowLayout.setOnClickListener{
        //actionListFragment 함수가 자동으로 생성 되지않는다면 프로젝트를 rebuild 해주세요 
            val action = ListFragmentDirections.actionListFragmentToUpdateFragment(currentItem)
            holder.itemView.findNavController().navigate(action)
        }
//추가 끝
        
    }
 

    fun setData(user:List<User>){      
        this.userList = user
        notifyDataSetChanged()
    }




}

 

이제 실행을 해보겠습니다. 그럼 아래와같이 editText에 빈값이 들어가있는걸 확인할수있습니다. 데이터베이스에 User오브젝트 데이터 값을 넣어주어서 유저가 데이터를 관리하기 더 편리하게 해보겠습니다.

 

  • fragments/update/UpdateFragment 

 

class UpdateFragment : Fragment() {

    private val args by navArgs<UpdateFragmentArgs>() //만약 에러가 보인다면 dependency를 추가해야합니다.

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    
        val view =  inflater.inflate(R.layout.fragment_update, container, false)
        
        //editText부분을 argument에서 받아온 값을 넣어줍니다. 
        view.updateFirstName_et.setText(args.currentUser.firstName)
        view.updateLastName_et.setText(args.currentUser.firstName)
        view.updateAge_et.setText(args.currentUser.age.toString())

        return view
    }

 

**fragmentArgs에 에러가 보인다면 아래 compile options과 kotlin options를 추가해주세요**

 

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.tassiecomp.roomdatabasepractice"
        minSdkVersion 26
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    
    //추가시작
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }
    //추가 끝


}

 

**그래도 해결이 안된다면 Gradle 아래에 플러그인이 있는지 확인해주세요**

 

build.gradle(module)

apply plugin: "androidx.navigation.safeargs.kotlin"

buildGradle(project)

dependencies {
        ...
        def nav_version = "2.3.5"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }

 

 

데이터베이스에 수정된 값 업데이트

 

우리가 updateFragment에서 수정된값을 EditText에 입력하면 데이터에 이 값을 전달하기 위해서 새로운 함수를 UpdateFragment에 만들어줘야합니다. 

 

class UpdateFragment : Fragment() {

    private val args by navArgs<UpdateFragmentArgs>()

    private lateinit var mUserViewModel:UserViewModel


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view =  inflater.inflate(R.layout.fragment_update, container, false)

        mUserViewModel = ViewModelProvider(this).get(UserViewModel::class.java)

        view.updateFirstName_et.setText(args.currentUser.firstName)
        view.updateLastName_et.setText(args.currentUser.firstName)
        view.updateAge_et.setText(args.currentUser.age.toString())

        view.update_button.setOnClickListener{
            updateItem()
        }

        //Add Menu
        setHasOptionsMenu(true)

        return view
    }

    private fun updateItem(){

//        변경된 값들을 editText에서 가져옵니다.
        val firstName = updateFirstName_et.text.toString()
        val lastName = updateLastName_et.text.toString()
        val age = Integer.parseInt(updateAge_et.text.toString())

        if (inputCheck(firstName,lastName,updateAge_et.text)){
            
            //updatedUser는 업데이트된 데이터입니다.
            val updatedUser = User(args.currentUser.id, firstName, lastName,age)
            
            //updateUser쿼리를 만들어서 Update Query를 이용하여 database에 추가해줘야합니다.
            //Update 쿼리는 DAO에서 추가해야합니다.
            //지금은 viewModel에 update 쿼리가 생기면 updatedUser가 전달되도록 구현만 해놓겠습니다. 
            
            mUserViewModel.updateUser(updatedUser)

            Toast.makeText(requireContext(),"UpdatedSuccessfully",Toast.LENGTH_SHORT).show()
            
            //navigate back
            findNavController().navigate(R.id.action_updateFragment_to_listFragment)
        
        } else{
            
            //EditText가 빈칸이면 토스트 메세지
            Toast.makeText(requireContext(),"Please fill out all field",Toast.LENGTH_SHORT).show()
        }
    }
    
    //editText가 비어있는지 확인하는 함수
    private fun inputCheck(firstName:String, lastName:String, age: Editable):Boolean{
        return !(TextUtils.isEmpty(firstName)&& TextUtils.isEmpty(lastName)&& age.isEmpty())
    }
    
}

 

  • data/UserDao

 

interface UserDao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun addUser(user: User)

	//추가시작
    @Update
    suspend fun updateUser(user:User) //suspend 코루틴사용됨
    //추가끝
    
    @Query("SELECT * FROM user_table ORDER BY id ASC")
    fun readAllData(): LiveData<List<User>>
    
}

 

  • repository/UserRepository

 

class UserRepository(private val userDao: UserDao) {

    val readAllData: LiveData<List<User>> = userDao.readAllData()

    suspend fun addUser(user: User){ //suspend를 붙여준 이유는 coroutine을 사용하기 위함입니다.
        userDao.addUser(user) //DAO에서 만들었던 adduser을 실행합니다.
    }

    suspend fun updateUser(user:User){
        userDao.updateUser(user)
    }
}

 

  •  viewmodel/UserViewModel

 

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

    val readAllData:LiveData<List<User>>
    private val repository: UserRepository

    init {
        val userDao = UserDatabase.getDatabase(application).userDao()
        repository = UserRepository(userDao) //initialize repository
        readAllData = repository.readAllData
    }

    fun addUser(user: User){
        viewModelScope.launch(Dispatchers.IO) {
            repository.addUser(user)
        }
    }

    fun updateUser(user:User){
        viewModelScope.launch(Dispatchers.IO) {
            repository.updateUser(user)
        }
    }
}

 

다음포스트에서는 데이터베이스에서 항목을 delete 쿼리를 사용하여 삭제해보겠습니다. 

 

 

[Kotlin] MVVM Room Database 5편 : 간단한 저장소 만들기 - @Delete으로 Database에 데이터 삭제

지난 포스트 [Kotlin] MVVM Room Database 4편 : 간단한 저장소 만들기 - @Update로 Database 데이터 수정 지난 포스트 [Kotlin] MVVM Room Database 3편 : 간단한 저장소 만들기 - RecyclerView Adapter에 ViewMo..

underdog11.tistory.com

 

반응형

'개발공부 > Kotlin' 카테고리의 다른 글

[Android] 색상 선택창, 팔레트 구현하기 - Color Sheet 라이브러리 사용법  (2) 2021.08.16
[Kotlin] MVVM Room Database : @Delete으로 Database에 데이터 삭제 - 간단한 저장소 만들기 5편  (0) 2021.07.28
[Kotlin] MVVM Room Database : RecyclerView Adapter에 ViewModel LiveData 적용하기 - 간단한 저장소 만들기 3편  (1) 2021.07.27
[Kotlin] MVVM Room Database : Navigation Graph 사용법, NavHostFragment, Insert 구성하기 간단한 저장소 만들기 - 2편  (0) 2021.07.26
[Kotlin] MVVM Room Database : Entity, RoomDatabase, DAO, repository, ViewModel, coroutine, MVVM 구성하기 - 간단한 저장소 만들기 1편  (0) 2021.07.09
    '개발공부/Kotlin' 카테고리의 다른 글
    • [Android] 색상 선택창, 팔레트 구현하기 - Color Sheet 라이브러리 사용법
    • [Kotlin] MVVM Room Database : @Delete으로 Database에 데이터 삭제 - 간단한 저장소 만들기 5편
    • [Kotlin] MVVM Room Database : RecyclerView Adapter에 ViewModel LiveData 적용하기 - 간단한 저장소 만들기 3편
    • [Kotlin] MVVM Room Database : Navigation Graph 사용법, NavHostFragment, Insert 구성하기 간단한 저장소 만들기 - 2편
    sean.jin
    sean.jin
    앱개발, 알고리즘, JS, Kotlin, 미국 취업준비

    티스토리툴바