App/개발

코틀린 커스텀 뷰 캘린더 레이아웃, 클래스 코드

Say simple 2020. 7. 16. 19:40
728x90
반응형

코틀린을 이용한 캘린더 커스텀 뷰 소스 저장

캘린더 어뎁터를 만들어서 LinearLayout을 상속 받은 클래스에 바인딩한다.

CalendarAdapter.kt

class CalendarAdapter(
    context: Context,
    days: ArrayList<Date>,
    eventDays: HashSet<Date>,
    inputMonth: Int
) : ArrayAdapter<Date>(
    context,
    R.layout.view_calendar,
    days
) {
    // for view inflation
    private val inflater: LayoutInflater = LayoutInflater.from(context)

    override fun getView(
        position: Int,
        view: View?,
        parent: ViewGroup
    ): View {
        var view = view
        val calendar = Calendar.getInstance()
        val date = getItem(position)
        calendar.time = date
        val day = calendar.get(Calendar.DATE)
        val month = calendar.get(Calendar.MONTH)
        val year =
            calendar.get(Calendar.YEAR)

        // 오늘에 해당하는 캘린더를 가져옴
        val today = Date()
        val calendarToday = Calendar.getInstance()
        calendarToday.time = today

        // 날짜 디자인으로 먼저 만들어 둔 calendar_day_layout을 inflate
        if (view == null) {
            view = inflater.inflate(R.layout.view_calendar, parent, false)
        }

        // 여기에서 기호에 따라 뷰의 생김새와 일자의 디자인을 변경이 가능.
        (view as TextView).setTypeface(null, Typeface.NORMAL)
        view.setTextColor(Color.parseColor("#56a6a9"))

        if (month == calendarToday.get(Calendar.MONTH) && year == calendarToday.get(Calendar.YEAR) && day == calendarToday.get(
                Calendar.DATE
            )
        ) {
            // 오늘의 날짜에 하고싶은 짓(?)을 정의 }
        }
        // 날짜를 텍스트뷰에 설정
        view.text = calendar.get(Calendar.DATE).toString()
        return view
    }
}


class CalendarView : LinearLayout {

    lateinit var header: LinearLayout
    lateinit var gridView: GridView

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
        initControl(context, attrs!!)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    private fun assignUiElements() {
        // layout is inflated, assign local variables to components
        header = view_calendar_header
        gridView = view_calendar_grid
    }

    fun updateCalendar(events: HashSet<Date>, inputCalendar: Calendar) {
        val cells = ArrayList<Date>()
        inputCalendar.set(Calendar.DAY_OF_MONTH, 1)

        // 여기서 빼주는 값 1의 경우 한 주의 시작요일에 따라 다르게 설정해주면 됨.
        // 필자가 쓴 캘린더의 경우 일요일부터 시작하는 관계로 1을 감산해주었음.

        val monthBeginningCell = inputCalendar.get(Calendar.DAY_OF_WEEK) - 1

        // 캘린더의 month를 추가함
        inputCalendar.add(Calendar.DAY_OF_MONTH, -monthBeginningCell)

        // 그리드에 집어넣을 cell들의 setup.
        while (cells.size < (Calendar.DAY_OF_MONTH) + inputCalendar.getActualMaximum(Calendar.DAY_OF_MONTH)) {
            // 캘린더의 Day를 추가함
            cells.add(inputCalendar.time)
            inputCalendar.add(Calendar.DAY_OF_MONTH, 1)
        }

        // 그리드 업데이트
        gridView.adapter =
            CalendarAdapter(context, cells, events, inputCalendar.get(Calendar.MONTH))
    }

    private fun initControl(context: Context, attrs: AttributeSet) {
        val inflater =
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        inflater.inflate(R.layout.view_calendar, this)
        assignUiElements()
    }
}

 

캘린더 자체의 레이아웃

view_calendar.xml

<?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">

    <LinearLayout
        android:id="@+id/view_calendar_header"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#222222"
            android:text="월" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#222222"
            android:text="화" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#222222"
            android:text="수" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#222222"
            android:text="목" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#222222"
            android:text="금" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#222222"
            android:text="토" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#222222"
            android:text="일" />

    </LinearLayout>

    <GridView
        android:id="@+id/view_calendar_grid"
        android:layout_width="match_parent"
        android:layout_height="240dp"
        android:numColumns="7" />


</androidx.constraintlayout.widget.ConstraintLayout>

캘린더의 날(숫자)을 넣기 위한 레이아웃

view_calendar_day.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView android:layout_width="38dp"
    android:layout_height="38dp"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:text="1"
    android:gravity="center"
    android:textSize="22sp"
    android:padding="5dp"
    android:layout_margin="15dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent" />
728x90
반응형