본문 바로가기
Development/Android

[Android] Kotlin + MVVM + AAC 로 Todo 앱 만들기 - 5

by du.it.ddu 2020. 3. 8.
반응형

이번 포스팅은 RecyclerView에 이벤트 처리를 구현 할 것이다.

아이템을 클릭하면 Todo 아이템을 추가할 때 띄웠던 다이얼로그를 통해 Todo 아이템을 수정 할 것이다.

그리고 롱클릭을 통해 Todo 아이템을 삭제 해 보겠다.

- Interface 구현

class TodoListAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {

	...
    
    interface OnTodoItemClickListener {
        fun onTodoItemClick(position: Int)
        fun onTodoItemLongClick(position: Int)
    }

    var listener: OnTodoItemClickListener? = null

	...
}

우선 어댑터의 상단에 위와 같은 코드를 작성한다.

아이템 클릭 인터페이스를 생성하고 아이템 클릭과 아이템 롱 클릭에 대하여 구현하게 할 것이고 외부에서 리스너를 통해 구현하게 된다.

- TodoListAdapter, ViewHolder 수정

class TodoListAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
	...

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_todo, parent, false)
        val viewHolder = TodoViewHolder(view, listener)

        return viewHolder
    }

    class TodoViewHolder(view: View, listener: OnTodoItemClickListener?): RecyclerView.ViewHolder(view) {
		...
    
        init {
            view.setOnClickListener {
                listener?.onTodoItemClick(adapterPosition)
            }

            view.setOnLongClickListener {
                listener?.onTodoItemLongClick(adapterPosition)
                return@setOnLongClickListener true
            }
        }
    	...
}

뷰홀더의 생성자에 리스너를 전달받도록 수정하고 뷰홀더 객체가 생성될 때 이 리스너를 통해 뷰에 리스너를 달아준다. 그리고 어댑터에서 뷰홀더를 생성할 때 이 리스너를 전달해준다.

- MainAcitivty 수정

class MainActivity : AppCompatActivity() {
	...
    
    private fun initRecyclerView() {
        mTodoListAdater = TodoListAdapter().apply {
            listener = object: TodoListAdapter.OnTodoItemClickListener {
                override fun onTodoItemClick(position: Int) {
                    Toast.makeText(this@MainActivity, "onTodoItemClick!", Toast.LENGTH_SHORT).show()
                }

                override fun onTodoItemLongClick(position: Int) {
                    Toast.makeText(this@MainActivity, "onLongTodoItemClick!", Toast.LENGTH_SHORT).show()
                }
            }
        }
        ...
    }
    
    ...
}

TodoListAdapter를 apply 메서드를 통해 내부 블럭에서 listener에 접근하여 인터페이스를 구현 해 준다.

토스트 메시지를 통해 잘 동작하는지 확인 해 보았다.

한번 클릭했을 때 onTodoItemClick! 토스트가, 꾹 클릭했을 때 onLongTodoItemClick이 호출되면 성공이다.

- onTodoItemClick 처리 구현

    private fun initRecyclerView() {
        mTodoListAdater = TodoListAdapter().apply {
            listener = object: TodoListAdapter.OnTodoItemClickListener {
                override fun onTodoItemClick(position: Int) {
                    openModifyTodoDialog(getItem(position))
                }
                ...
            }
        }
		...
    }

onTodoItemClick 메서드에서 포지션에 해당하는 TodoModel을 얻어오는 getItem(position: Int)를 호출한 뒤 얻어온 TodoModel을 openModifyTodoDialog 함수에 전달한다.

getItem은 TodoListAdaper에서 구현해야 한다. 간단한 코드이니 패스한다.

    private fun openModifyTodoDialog(todoModel: TodoModel) {
        val dialogView = layoutInflater.inflate(R.layout.dialog_add_todo, null)
        dialogView.et_todo_title.setText(todoModel.title)
        dialogView.et_todo_description.setText(todoModel.description)

        val dialog = AlertDialog.Builder(this)
            .setTitle("수정하기")
            .setView(dialogView)
            .setPositiveButton("확인", { dialogInterface, i ->
                val title = dialogView.et_todo_title.text.toString()
                val description = dialogView.et_todo_description.text.toString()

                todoModel.description = description
                todoModel.title = title
                
                mTodoViewModel.updateTodo(todoModel)
            })
            .setNegativeButton("취소", null)
            .create()

        dialog.show()
    }

전달받은 todoModel의 값으로 다이얼로그뷰의 텍스트들을 세팅한다.

그리고 다이얼로그를 통해 값을 받은 뒤 todoModel의 값을 변경하고 뷰모델의 updateTodo 함수에 전달한다.

class TodoViewModel(application: Application): AndroidViewModel(application) {
	...

    fun updateTodo(todoModel: TodoModel) {
        mTodoRepository.updateTodo(todoModel)
    }
    ...
}

class TodoRepository(application: Application) {
	...
    fun updateTodo(todoModel: TodoModel) {
        Observable.just(todoModel)
            .subscribeOn(Schedulers.io())
            .subscribe({
                mTodoDAO.updateTodo(todoModel)
            }, {
                // Handle error.
            })
    }
}

interface TodoDAO  {
	...
    
    @Update
    fun updateTodo(todoModel: TodoModel)
}

뷰모델, 리포지토리, DAO를 위와같이 변경한다. 매우 짧고 간단하다.

- onTodoItemLongClick 처리 구현

롱클릭을 하면 확인과 취소 버튼이 있는 다이얼로그를 출력하고 확인을 누르면 삭제하도록 구현 할 것이다.

    private fun openDeleteTodoDialog(todoModel: TodoModel) {

        val dialog = AlertDialog.Builder(this)
            .setTitle("삭제하기")
            .setMessage("확인을 누르면 삭제됩니다.")
            .setPositiveButton("확인", { dialogInterface, i ->
                mTodoViewModel.deleteTodo(todoModel)
            })
            .setNegativeButton("취소", null)
            .create()

        dialog.show()
    }

기본 다이얼로그를 사용하여 위와 같은 함수를 MainAcitivty 내에 추가한다.

    private fun initRecyclerView() {
        mTodoListAdater = TodoListAdapter().apply {
            listener = object: TodoListAdapter.OnTodoItemClickListener {
            	...
                
                override fun onTodoItemLongClick(position: Int) {
                    openDeleteTodoDialog(getItem(position))
                }
            }
        }
        ...
    }

onTodoItemLongClick 메서드에서 위에서 추가한 다이얼로그 함수를 호출한다.

class TodoViewModel(application: Application): AndroidViewModel(application) {
	...

    fun deleteTodo(todoModel: TodoModel) {
        mTodoRepository.deleteTodo(todoModel)
    }
}

class TodoRepository(application: Application) {
	...

    fun deleteTodo(todoModel: TodoModel) {
        Observable.just(todoModel)
            .subscribeOn(Schedulers.io())
            .subscribe({
                mTodoDAO.deleteTodo(todoModel)
            }, {
                // Handle error.
            })
    }
}

interface TodoDAO  {
	...

    @Delete
    fun deleteTodo(todoModel: TodoModel)
}

위 처럼 뷰모델, 리포지토리, DAO를 수정한다.

굉장히 간단하게 이벤트를 추가하고 처리하는 기능을 구현하게 되었다.

- 다음 할 일

우선 지금까지 한 프로젝트에 문제가 있는데, update를 하고나서 리스트가 업데이트가 안된다는 것이다.

이유는 LiveData와 DiffUtil 때문인 것 같은데, 실제로 업데이트는 잘 이루어지지만 리스트 갱신이 되지 않고 있었다.

또한 예제이고 기능 사용에 중점을 뒀기 때문에 다이얼로그 쪽 코드가 중복이 많다.

심각한 문제는 아니기에 추후 수정으로 미루고, 다음으로는 DataBinding 이라는 것을 적용 볼 것이다.

반응형