경험의 기록

2021.05.24 - [안드로이드/개발] - [Android 개발일지] MVVM 패턴으로 Todo, Done List 만들기 - (3) Todo리싸이클러뷰, 메모 추가 기능

 

[Android 개발일지] MVVM 패턴으로 Todo, Done List 만들기 - (3) Todo리싸이클러뷰, 메모 추가 기능

2021.05.22 - [안드로이드/개발] - [Android 개발일지] MVVM 패턴으로 Todo, Done List 만들기 - (2) 레이아웃, DB 만들기 [Android 개발일지] MVVM 패턴으로 Todo, Done List 만들기 - (2) 레이아웃, DB 만들기 2..

hanyeop.tistory.com

에서 이어지는 글입니다.

 


1️⃣ 구조 (모델) 수정

 

Memo

@Entity
data class Memo(
    val check : Boolean,
    val content : String,
    val year : Int,
    val month : Int,
    val day : Int
    ) {
    @PrimaryKey(autoGenerate = true)
    var id : Int = 0
}

메모의 날짜를 확인하기 위하여 메모 체크 여부와 내용 이외에

년,월,일 을 추가로 저장하기 위한 변수를 추가해주어 구조를 변경해준다.

 

item 레이아웃

메모 아이템에서 이런식으로 메모의 날짜를 표시해줄 것이다.

 

 

TodoListFragment

// 다이얼로그에서 추가버튼 클릭 됐을 때
    override fun onOkButtonClicked(content: String) {

        // 현재의 날짜를 불러옴
        val cal = Calendar.getInstance()
        val year = cal.get(Calendar.YEAR)
        val month = cal.get(Calendar.MONTH) + 1
        val day = cal.get(Calendar.DATE)

        // 현재의 날짜로 메모를 추가해줌
        val memo = Memo(false,content, year, month, day)
        memoViewModel.addMemo(memo)
        Toast.makeText(activity,"추가", Toast.LENGTH_SHORT).show()
    }

받아온 메모내용과 현재 날자를 불러와 년월일로 메모 객체를 만들어 메모를 추가한다.

 

TodoAdapter

// 뷰 홀더에 데이터를 바인딩
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val currentItem = memoList[position]
        val currentContent = currentItem.content
        val currentYear = currentItem.year
        val currentMonth = currentItem.month
        val currentDay = currentItem.day

        val s_currentYear = currentYear.toString()
        var s_currentMonth = currentMonth.toString()
        var s_currentDay = currentDay.toString()

        if(currentMonth < 10) s_currentMonth = "0$currentMonth"
        if(currentDay < 10) s_currentDay = "0$currentDay"

        holder.binding.memoCheckBox.text = currentContent
        holder.binding.dateTextView.text = "$s_currentYear/$s_currentMonth/$s_currentDay"

    }

이제 리싸이클러뷰에서 그 메모의 날짜가 표시되어야 하므로

각각 날짜에 해당하는 데이터를 바인딩해준다.

 

MemoDao

// 큰 날짜부터 출력
    @Query("SELECT * FROM Memo ORDER BY year DESC, month DESC, day DESC, id DESC")
    fun readAllData() : Flow<List<Memo>>

큰 날짜부터 위로 오게하여 출력하기 위해 쿼리를 수정해준다.

 

테스트, 테스트2, 테스트3 순으로 임의의 날짜로 추가하였으며 날짜별로 잘 정렬된 것을 확인할 수 있다.

 

 

2️⃣ 캘린더 프래그먼트

 

캘린더 프래그먼트 레이아웃 생성

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".UI.fragment.CalendarFragment">

    <CalendarView
        android:id="@+id/calendarView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/calendarRecyclerview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/calendarView" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/calendar_DialogButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="20dp"
        android:clickable="true"
        android:focusable="true"
        android:src="@drawable/ic_baseline_edit_24"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

캘린더와 리싸이클러뷰, Fab로 구성된 레이아웃을 만들어준다.

캘린더의 날짜를 선택하면 그 날에 해당하는 메모를 밑의 리싸이클러뷰에서 보여주고,

Fab를 클릭하면 그 날짜에 메모를 추가할 수 있도록 하려고 한다.

 

MemoDao

// 날짜 정보를 입력받아 그 날짜에 해당하는 메모만 반환
    @Query("SELECT * FROM Memo WHERE year = :year AND month = :month AND day = :day ORDER BY id DESC")
    fun readDateData(year : Int, month : Int, day : Int) : Flow<List<Memo>>

날짜 정보를 입력받아서 그 날짜에 해당하는 메모만 반환하기 위한 쿼리를 작성하였다.

 

MemoRepository

fun readDateData(year : Int, month : Int, day : Int): Flow<List<Memo>> {
        return memoDao.readDateData(year, month, day)
    }

Dao에서 작성한 인터페이스 메소드를 정의해주고

 

MemoViewModel

fun readDateData(year : Int, month : Int, day : Int): LiveData<List<Memo>> {
        return repository.readDateData(year, month, day).asLiveData()
    }

뷰모델에서 사용한다.

 

CalendarFragment

class CalendarFragment : Fragment(), MyCustomDialogInterface {

    private var binding : FragmentCalendarBinding? = null
    private val memoViewModel: MemoViewModel by viewModels() // 뷰모델 연결
    private val adapter : TodoAdapter by lazy { TodoAdapter() } // 어댑터 선언

    private var year : Int = 0
    private var month : Int = 0
    private var day : Int = 0

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // 뷰바인딩
        binding = FragmentCalendarBinding.inflate(inflater,container,false)

        // 아이템을 가로로 하나씩 보여주고 어댑터 연결
        binding!!.calendarRecyclerview.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL,false)
        binding!!.calendarRecyclerview.adapter = adapter

        binding!!.calendarView.setOnDateChangeListener { calendarView, year, month, day ->
            // 날짜 선택시 그 날의 정보 할당
            this.year = year
            this.month = month+1
            this.day = day

            binding!!.calendarDateText.text = "${this.year}/${this.month}/${this.day}"

            // 리스트 관찰하여 변경시 어댑터에 전달해줌
            memoViewModel.readDateData(this.year,this.month,this.day).observe(viewLifecycleOwner, Observer {
                adapter.setData(it)
            })
        }

        // Fab 클릭시 다이얼로그 띄움
        binding!!.calendarDialogButton.setOnClickListener {
            if(year == 0) {
                Toast.makeText(activity, "날짜를 선택해주세요.", Toast.LENGTH_SHORT).show()
            }
            else {
                onFabClicked()
            }
        }

        return binding!!.root
    }

    // Fab 클릭시 사용되는 함수
    private fun onFabClicked(){
        val myCustomDialog = MyCustomDialog(activity!!,this)
        myCustomDialog.show()
    }

    // 프래그먼트는 뷰보다 오래 지속 . 프래그먼트의 onDestroyView() 메서드에서 결합 클래스 인스턴스 참조를 정리
    override fun onDestroyView() {
        binding = null
        super.onDestroyView()
    }

    override fun onOkButtonClicked(content: String) {
        // 선택된 날짜로 메모를 추가해줌
        val memo = Memo(false, content, year, month, day)
        memoViewModel.addMemo(memo)
        Toast.makeText(activity, "추가", Toast.LENGTH_SHORT).show()
    }
}

하나씩 살펴보면

 

뷰바인딩을 해주고,

메모뷰모델과 리싸이클러뷰 어댑터는 TodoList 프래그먼트에서 사용한 동일한 것을 사용한다.

또한 현재 날짜를 저장하기 위한 변수들을 선언해준다.

 

날짜 선택 시 현재의 날짜를 선택된 날짜로 바꿔주고 옵저버 패턴으로 관찰하여 리스트의 변화를 관찰해 출력한다.

 

처음 프래그먼트에서처럼 fab 클릭시 동일한 화면을 띄우고,

여기에서는 선택된 날짜로 메모를 추가해준다.

 

현재 여러 날짜로 저장되있는 메모를

달력에서 특정 날짜의 메모만 확인할 수 있다.

 

 또한 달력에서 다른 날에 메모를 추가할 수 있으며

 

전체 목록에서도 잘 추가 된 것을 확인할 수 있다.

 


이제 다음에는 메모의 체크여부를 파악하는 기능을 만들고, 메모 수정, 삭제 기능을 만들 것이다.

 

 

https://github.com/HanYeop/TodoneList

 

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading