material CalendarView를 커스텀하기(원하는 달로 한번에 이동하는 메뉴구현)

개린이의 개발 노트·2023년 7월 22일
1

캘린더뷰 상단을 클릭하면 년/달 선택 메뉴가 나타나고 달에 해당하는 버튼을 누를 시에 해당 년/월로 한번에 이동하는 캘린더를 구현해 보았다.

이건 메뉴 부분을 xml로 디자인한 거다.

<?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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:elevation="4dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cl_select_year"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="20dp"
        app:layout_constraintLeft_toLeftOf="@id/cl_month_group"
        app:layout_constraintRight_toRightOf="@id/cl_month_group"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/iv_prev_year"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/chevron_left"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_year"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fontFamily="@font/pretendard_bold"
            android:text=""
            android:textColor="@color/black"
            android:textSize="26sp"
            android:layout_marginHorizontal="75dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@id/iv_prev_year"
            app:layout_constraintRight_toLeftOf="@id/iv_next_year"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/iv_next_year"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/chevron_right"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cl_month_group"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingHorizontal="18dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/cl_select_year">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/cl_month_group1"
            android:layout_width="309dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <Button
                android:id="@+id/btn_month_1"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="1월"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toLeftOf="@id/btn_month_2"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/btn_month_2"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="2월"
                android:layout_marginHorizontal="6dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/btn_month_1"
                app:layout_constraintRight_toLeftOf="@id/btn_month_3"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/btn_month_3"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="3월"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/btn_month_2"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/cl_month_group2"
            android:layout_width="309dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/cl_month_group1">

            <Button
                android:id="@+id/btn_month_4"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="4월"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toLeftOf="@id/btn_month_5"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/btn_month_5"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="5월"
                android:layout_marginHorizontal="6dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/btn_month_4"
                app:layout_constraintRight_toLeftOf="@id/btn_month_6"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/btn_month_6"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="6월"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/btn_month_5"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/cl_month_group3"
            android:layout_width="309dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/cl_month_group2">

            <Button
                android:id="@+id/btn_month_7"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="7월"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toLeftOf="@id/btn_month_8"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/btn_month_8"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="8월"
                android:layout_marginHorizontal="6dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/btn_month_7"
                app:layout_constraintRight_toLeftOf="@id/btn_month_9"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/btn_month_9"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="9월"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/btn_month_8"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/cl_month_group4"
            android:layout_width="309dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/cl_month_group3">

            <Button
                android:id="@+id/btn_month_10"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="10월"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toLeftOf="@id/btn_month_11"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/btn_month_11"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="11월"
                android:layout_marginHorizontal="6dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/btn_month_10"
                app:layout_constraintRight_toLeftOf="@id/btn_month_12"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/btn_month_12"
                android:layout_width="99dp"
                android:layout_height="38dp"
                android:background="@drawable/button_design_gray_50"
                android:text="12월"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/btn_month_11"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

그리고 이건 내가 구현한 캘린더뷰 프래그먼트의 xml코드 중 일부이다.

<androidx.constraintlayout.widget.ConstraintLayout
                    android:id="@+id/cl_select_date"
                    android:layout_width="match_parent"
                    android:layout_height="25dp"
                    android:layout_marginHorizontal="120dp"
                    android:layout_marginTop="31dp"
                    app:layout_constraintTop_toTopOf="parent" />

                <FrameLayout
                    android:id="@+id/fl_select_date"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginHorizontal="24dp"
                    android:layout_marginTop="36dp"
                    android:layout_marginBottom="126dp"
                    android:background="@drawable/layout_select_date_white"
                    android:elevation="4dp"
                    android:paddingTop="32dp"
                    android:paddingBottom="30dp"
                    android:visibility="gone"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

달력 상단의 클릭 리스너를 구현하기 위해
cl_select_date를 만들어서 달력 상단에 덧붙였고, 내가 만든 메뉴 선택 프래그먼트를 fl_select_date 프레임 레이아웃에 붙였다. (일단 클릭 전엔 안보여야 하므로 visibility속성을 gone으로 주었다.)

package com.kuit.conet.Home

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.kuit.conet.databinding.DialogDateChooseBinding
import java.util.*

class choose_date_dialog : Fragment(){

    lateinit var binding : DialogDateChooseBinding

    interface OnButtonClickListener {
        fun onButtonClicked(year : Int, month : Int)
    }

    private var buttonClickListener: OnButtonClickListener? = null

    fun setOnButtonClickListener(listener: OnButtonClickListener) {
        buttonClickListener = listener
    }


    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = DialogDateChooseBinding.inflate(inflater, container, false)
        if(binding.tvYear.text.toString() == ""){
            val calendar = Calendar.getInstance()
            val year = calendar.get(Calendar.YEAR)
            binding.tvYear.text = year.toString()
        }
        binding.ivPrevYear.setOnClickListener {
            var year = binding.tvYear.text.toString().toInt()
            year = year - 1
            binding.tvYear.text = year.toString()
        }
        binding.ivNextYear.setOnClickListener {
            var year = binding.tvYear.text.toString().toInt()
            year = year + 1
            binding.tvYear.text = year.toString()
        }

        binding.btnMonth1.setOnClickListener {
            val month = 1
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }

        binding.btnMonth2.setOnClickListener {
            val month = 2
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }

        binding.btnMonth3.setOnClickListener {
            val month = 3
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }


        binding.btnMonth4.setOnClickListener {
            val month = 4
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }

        binding.btnMonth5.setOnClickListener {
            val month = 5
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }

        binding.btnMonth6.setOnClickListener {
            val month = 6
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }


        binding.btnMonth7.setOnClickListener {
            val month = 7
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }

        binding.btnMonth8.setOnClickListener {
            val month = 8
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)
        }

        binding.btnMonth9.setOnClickListener {
            val month = 9
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }



        binding.btnMonth10.setOnClickListener {
            val month = 10
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }

        binding.btnMonth11.setOnClickListener {
            val month = 11
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }

        binding.btnMonth12.setOnClickListener {
            val month = 12
            val year = binding.tvYear.text.toString().toInt()
            buttonClickListener?.onButtonClicked(year, month)

        }

        return binding.root
    }



}

여기는 내가 구현한 날짜선택 메뉴의 프래그먼트 부분이다. 여기서 클릭한 내용이 캘린더뷰 프래그먼트에도 전달이 되어야하므로 클릭리스너를 인터페이스로 구현해 주었다.

fun showDialog(){
        binding.flSelectDate.visibility = View.VISIBLE
        val chooseDateDialog = choose_date_dialog()
        chooseDateDialog.setOnButtonClickListener(object :
            choose_date_dialog.OnButtonClickListener {
            override fun onButtonClicked(year: Int, month: Int) {

                binding.viewCanlendar.setCurrentDate(CalendarDay.from(year, month-1, 1))
                binding.viewCanlendar.setSelectedDate(CalendarDay.from(year, month-1, 1))
                binding.flSelectDate.visibility = View.GONE
            }
        })
        parentFragmentManager.beginTransaction() // 메뉴 띄워주는 부분
            .replace(R.id.fl_select_date, chooseDateDialog)
            .commitAllowingStateLoss()
        //chooseDateDialog.show(parentFragmentManager, "chooseDateDialog")
    }

여기는 캘린더뷰 프래그먼트에 메뉴를 띄워주는 함수이다.
setCurrentDate와 setSelectedDate메소드와 메뉴에서 클릭한 년/월 데이터를 이용해서 해당 날짜로 이동하는 부분이다. '일' 정보까지는 다루지 않아서 해당 년/월 의 1일 자로 이동하도록 설정해주었다.

사실 스터디때 인터페이스 활용하는 걸 배웠었는데 이번 기회에 그것을 내껄로 만드는 좋은 기회가 될 수 있었던 것 같다.

profile
개발 처음 시작함..... 그러니 잘못되거나 다소 이해안가는 코드들도 있을 수 있습니다 이점 양해 부탁드려요

0개의 댓글