안드로이드 커스텀 캘린더 만들기

차누·2023년 5월 3일
0
post-thumbnail

서론

Todo-list를 개발중에 캘린더를 활용하여 해달 날짜에 일정을 추가하고 리스트로 보여지는 기능을 만들고 싶었다. 당시 안드로이드 스튜디오에서 제공하는 Calendar 뷰를 활용할려고 했었지만 해당 날짜 클릭시 년, 월, 일만 변경할 수 있는 기능만 제공이 돼 한정적이라는 느낌을 받아 직접 나만의 커스텀 캘린더를 만들기로 했고 기존에 나와있던 오픈소스를 활용하기로 했다.

본론

UI는 직접 디자인 하였고, 최대한 코드를 이해하고 어떤 구조인지 파악하고 사용할려고 했다. 모르는 부분은 구글이나 Android Developer를 통해 찾아보며 개발을 하였다.

구현

레이아웃

calendar.xml

캘린더 뷰가 보여질 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    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:id="@+id/calendar_layout"
    android:orientation="vertical"
    android:background="@color/white">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/cal_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toBottomOf="@id/calendar_layout">
    </androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>

month_item.xml

월 ~ 일요일까지 요일 리스트가 보여질 item view

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/white">


    <!--현재 날짜-->
    <androidx.appcompat.widget.AppCompatTextView
        android:text="날짜"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_weight="1"
        android:padding="16dp"
        android:textSize="18sp"
        android:textColor="@color/black"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:textAlignment="center"
        android:id="@+id/title"
        android:background="#FFD700">

    </androidx.appcompat.widget.AppCompatTextView>

    <!--요일-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="@color/white"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@id/title"
        android:id="@+id/month_layout">

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text=""
            android:textSize="15dp"
            android:textColor="#FF0000"/>

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text=""
            android:textColor="@color/black"
            android:textSize="15dp" />

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text=""
            android:textColor="@color/black"
            android:textSize="15dp" />

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text=""
            android:textColor="@color/black"
            android:textSize="15dp" />

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text=""
            android:textColor="@color/black"
            android:textSize="15dp" />

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text=""
            android:textColor="@color/black"
            android:textSize="15dp" />

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text=""
            android:textSize="15dp"
            android:textColor="#0000FF"/>
    </LinearLayout>
    <!--달력뷰-->

           <!-- 요일이 보여질 리사이클러뷰-->
    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="550dp"
        android:id="@+id/month_recycler"
        app:layout_constraintTop_toBottomOf="@id/month_layout">
    </androidx.recyclerview.widget.RecyclerView>

    <androidx.appcompat.widget.AppCompatButton
        android:layout_width="70dp"
        android:layout_height="40dp"
        android:id="@+id/add"
        app:layout_constraintTop_toBottomOf="@id/month_recycler"
        android:background="@drawable/button_custom"
        android:text="추가"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginRight="30dp">

    </androidx.appcompat.widget.AppCompatButton>
</androidx.constraintlayout.widget.ConstraintLayout>

day_item.xml

요일이 보여질 item view

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_day_layout"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:layout_marginTop="5dp"
    android:layout_marginRight="5dp"
    android:background="@color/white">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/item_day_text"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:textSize="19dp"
        android:layout_marginTop="18dp"
        android:text="9"
        android:layout_marginLeft="18dp"
        android:textColor="@color/black"/>
</LinearLayout>

클래스

Calendar_frag.kt

class Calendar_frag : Fragment(R.layout.calendar) {

    //fragment
    private var _binding: CalendarBinding ?= null

    private lateinit var recyclerView: RecyclerView

    //view
    private val binding get() = _binding
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = CalendarBinding.inflate(inflater,container,false)

        val view = binding?.root

        initView(_binding!!)

        create_data()
        return view
    }

    private fun initView(binding: CalendarBinding) {
        recyclerView = binding.calRecycler
        var position: Int = Int.MAX_VALUE / 2

        binding?.calRecycler?.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
        binding?.calRecycler?.adapter = Month_adpater()

        //item의 위치 지정
        binding?.calRecycler?.scrollToPosition(position)

        //항목씩 스크롤 지정
        val snap = PagerSnapHelper()
        snap.attachToRecyclerView(binding?.calRecycler)

    }

    private fun create_data() {
        binding?.calRecycler?.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)

    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
    }

Month_adapter.kt


class Month_adpater: RecyclerView.Adapter<Month_adpater.Month>() {
    var calendar: Calendar = Calendar.getInstance()


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Month {

        val view = LayoutInflater.from(parent.context).inflate(R.layout.month_item,parent,false)
        return Month(view)
    }


    override fun onBindViewHolder(holder: Month, position: Int) {


        //리사이클러뷰 초기화
        var list_layout: RecyclerView = holder.view.findViewById(R.id.month_recycler)


        //달 구하기


        calendar.time = Date() //현재 날짜 초기화
        calendar.set(Calendar.DAY_OF_MONTH,1) //스크롤시 현재 월의 1일로 이동
        calendar.add(Calendar.MONTH , position) //스크롤시 포지션 만큼 달이동

        //title 텍스트 초기화
        var title_text: TextView =  holder.view.findViewById<TextView>(R.id.title)
        var add_button: Button = holder.view.findViewById <Button>(R.id.add)

        //현재 날짜 출력
        title_text.setText("${calendar.get(Calendar.YEAR)}년 ${calendar.get(Calendar.MONTH) + 1}월")

        val tempMonth = calendar.get(Calendar.MONTH)

        //일 구하기


        //6주 7일로 날짜를 표시
        var dayList: MutableList<Date> = MutableList(6 * 7 ) { Date() }

        for(i in 0..5) { //주
            for (k in 0..6) { //요일
                //각 달의 요일만큼 캘린더에 보여진다
                //요일 표시
                calendar.add(Calendar.DAY_OF_MONTH, (1 - calendar.get(Calendar.DAY_OF_WEEK)) + k)
                dayList[i * 7 + k] = calendar.time //배열 인덱스 만큼 요일 데이터 저장
            }
            //주 표시
            calendar.add(Calendar.WEEK_OF_MONTH, 1)
        }


        list_layout.layoutManager = GridLayoutManager(holder.view.context,7)
        list_layout.adapter = Day_adapter(tempMonth,dayList)
    }




    override fun getItemCount(): Int {
        return Int.MAX_VALUE / 2
    }

    class Month(val view: View) : RecyclerView.ViewHolder(view)
    }

Day_adapter.kt

class Day_adapter(val tempMonth:Int, val dayList: MutableList<Date>) : RecyclerView.Adapter<Day_adapter.DayView>() {
    val ROW =6
    class DayView(val layout: View): RecyclerView.ViewHolder(layout)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DayView {
       var view = LayoutInflater.from(parent.context).inflate(R.layout.day_item,parent,false)

        return DayView(view)
    }

    override fun onBindViewHolder(holder: DayView, position: Int) {

        //초기화
        var day_text: TextView = holder.layout.findViewById<TextView>(R.id.item_day_text)

        
        //날짜 표시
        day_text.text = dayList[position].date.toString()
        if(tempMonth != dayList[position].month) {
            day_text.alpha=0.4f
        }

        //토요일이면 파란색 || 일요일이면 빨간색으로 색상표시
        if((position + 1) % 7 == 0) {
            day_text.setTextColor(ContextCompat.getColor(holder.layout.context,R.color.blue))
        } else if (position == 0 || position % 7 == 0) {
            day_text.setTextColor(ContextCompat.getColor(holder.layout.context,R.color.red))
        }
    }


    override fun getItemCount(): Int {
        return ROW*7
    }
}

결과

후기

처음으로 오픈소스를 사용해봤고 구조를 알고 코드를 이해하는데만 2주넘게 걸린거 같다. 또한 모르는 부분은 구글이나 Android Developer를 찾아서 했는데 이해하는데 알것만 같은 느낌이 들어서 조금씩 성장하고 있구나 느꼈다.

profile
to be good programmer

0개의 댓글