Skip to content

Demo Week3

doyeon edited this page Nov 11, 2021 · 4 revisions

🌈Demo week3

🖥3주차 발표 영상

notto_week3.mp4

🤼‍♀️ 이번주 구현 내용 Preview

1. DB 구조 변경

하나의 Todo에 날짜별 달성여부를 체크하고 추출한 키워드를 담아야하기 때문에 Entity를 수정하였습니다.

2. Todo 추가/수정

플로팅 버튼 + 과 각 Todo의 연필 버튼 ✏을 눌러 Todo 추가/수정이 가능한 편집화면으로 이동할 수 있습니다.

플로팅 버튼을 누르면 새로운 Todo를 작성하여 DB에 저장하도록 하였습니다. Todo의 연필 버튼을 누르면 해당 Todo를 Todo 편집 화면에 넘겨주고, 이 Todo를 편집하여 업데이트 할 수 있도록 하였습니다.

각 Todo는 내용, 반복 설정, 알림 설정, 키워드 설정을 할 수 있습니다.

3. Label 추가/수정/삭제

Label 리스트의 오른쪽 버튼을 눌러 추가/수정/삭제가 가능한 편집화면으로 이동할 수 있습니다.

📚 기술적인 구현 내용

Room 1:N, N:N 구현

  1. DailyTodo : Todo = 1 : N
@Entity
data class Todo(
    ...
): Serializable



@Entity(primaryKeys = ["date", "parent_todo_id"])
data class DailyTodo(
    ...
    @ColumnInfo(name = "parent_todo_id") val parentTodoId: Int,
)

Todo와 DailyTodo가 연결된 데이터를 묶어서 가져온다.

data class TodoWithDailyTodo(
    @Embedded val todo: Todo,
    @Relation(
        parentColumn = "todoId",
        entityColumn = "parent_todo_id"
    )
    val dailyTodos: List<DailyTodo>
)

// DAO.kt
@Transaction
@Query("SELECT * FROM Todo")
suspend fun getTodosWithDailyTodos(): List<TodoWithDailyTodo>
  1. Todo : Label = N : N
@Entity
data class Todo(
    ...
): Serializable



@Entity
data class Label(
    ...
)

Todo와 Label이 연결된 데이터를 묶어서 가져온다. 1:N, 1:1과 다르게 N:N은 Todo, Label을 추가해주고 두 pk를 묶고있는 Entity에도 추가해주어야한다.

data class TodoWithLabel(
    @Embedded val todo: Todo,
    @Relation(
        parentColumn = "todoId",
        entityColumn = "labelId",
        associateBy = Junction(TodoLabelCrossRef::class)
    )
    val labels: List<Label>
)

@Entity(primaryKeys = ["todoId", "labelId"])
data class TodoLabelCrossRef(
    val todoId: Int,
    val labelId: Int
)

// DAO.kt
@Transaction
@Query("SELECT * FROM Todo")
suspend fun getTodosWithLabels(): List<TodoWithLabel>
    
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(todoLabelCrossRef: TodoLabelCrossRef)

Recyclerview Item Move 구현

ItemTouchHelper를 이용해 RecyclerView의 아이템 드래그 기능 구현
ItemTouchHelper.Callback 구현

class ItemTouchCallback(private val moveItemCallback: (Int, Int) -> Unit) :
    ItemTouchHelper.SimpleCallback(
        ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.START or ItemTouchHelper.END,
        0
    ) {

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        val adapter = recyclerView.adapter as EditLabelAdapter
        val from = viewHolder.bindingAdapterPosition
        val to = target.bindingAdapterPosition

        moveItemCallback(from, to)
        adapter.notifyItemMoved(from, to)

        return true
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}

    override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
        super.onSelectedChanged(viewHolder, actionState)

        if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
            viewHolder?.itemView?.alpha = 0.5f
        }
    }

    override fun clearView(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ) {
        super.clearView(recyclerView, viewHolder)

        viewHolder.itemView.alpha = 1.0f
    }
}

Dialog 구현

  1. Dialog View 방식
  • Dialog() 클래스 사용

  • Not Todo 다이얼로그에 적용

  • BaseDialog를 생성 후 아래의 다이얼로그가 BaseDialog를 상속받음

    • 시계 다이얼로그
    • 캘린더를 띄우는 다이얼로그
    • 반복 유형을 띄우는 다이얼로그
    • 삭제 확인 다이얼로그

  • 각 다이얼로그는 xml 상에서 취소/확인을 표시하는 layout을 따로 include함

  • 통일성을 위해 추후 DialogFragment 방식으로 수정할 계획

  1. Dialog Fragment 방식