우찬쓰 개발블로그

안드로이드 간단한 달력 만들기 본문

안드로이드/안드로이드 개발

안드로이드 간단한 달력 만들기

이우찬 2019. 3. 25. 14:48
반응형

요구사항에 따라 앱을 만들다보면 간단한 달력을 구현할 일이 생긴다.


기본 calendar view를 사용할 수도 있지만, 대게 디자이너의 요구사항은 많은 커스텀이 들어간 상태이다.


필요한 캘린더를 위해 github의 수많은 라이브러리를 둘러보는 시간이 아까우니 캘린더 로직을 직접 만들어 보자.


java는(kotlin도) Calendar 추상 클래스가 기본적으로 존재하기 때문에 RecyclerView의 GridLayout와 함께 사용하면 매우 간단하게 달력을 구현할 수 있다.


필자는 종속성을 줄이기 위해 아래와 같이 BaseCalendar를 구현하였다.


/**
* Created by WoochanLee on 25/03/2019.
*/
class BaseCalendar {

companion object {
const val DAYS_OF_WEEK = 7
const val LOW_OF_CALENDAR = 6
}

val calendar = Calendar.getInstance()

var prevMonthTailOffset = 0
var nextMonthHeadOffset = 0
var currentMonthMaxDate = 0

var data = arrayListOf<Int>()

init {
calendar.time = Date()
}

/**
* Init calendar.
*/
fun initBaseCalendar(refreshCallback: (Calendar) -> Unit) {
makeMonthDate(refreshCallback)
}

/**
* Change to prev month.
*/
fun changeToPrevMonth(refreshCallback: (Calendar) -> Unit) {
if(calendar.get(Calendar.MONTH) == 0){
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) - 1)
calendar.set(Calendar.MONTH, Calendar.DECEMBER)
}else {
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1)
}
makeMonthDate(refreshCallback)
}

/**
* Change to next month.
*/
fun changeToNextMonth(refreshCallback: (Calendar) -> Unit) {
if(calendar.get(Calendar.MONTH) == Calendar.DECEMBER){
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 1)
calendar.set(Calendar.MONTH, 0)
}else {
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1)
}
makeMonthDate(refreshCallback)
}

/**
* make month date.
*/
private fun makeMonthDate(refreshCallback: (Calendar) -> Unit) {

data.clear()

calendar.set(Calendar.DATE, 1)

currentMonthMaxDate = calendar.getActualMaximum(Calendar.DAY_OF_MONTH)

prevMonthTailOffset = calendar.get(Calendar.DAY_OF_WEEK) - 1

makePrevMonthTail(calendar.clone() as Calendar)
makeCurrentMonth(calendar)

nextMonthHeadOffset = LOW_OF_CALENDAR * DAYS_OF_WEEK - (prevMonthTailOffset + currentMonthMaxDate)
makeNextMonthHead()

refreshCallback(calendar)
}

/**
* Generate data for the last month displayed before the first day of the current calendar.
*/
private fun makePrevMonthTail(calendar: Calendar) {
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1)
val maxDate = calendar.getActualMaximum(Calendar.DATE)
var maxOffsetDate = maxDate - prevMonthTailOffset

for (i in 1..prevMonthTailOffset) data.add(++maxOffsetDate)
}

/**
* Generate data for the current calendar.
*/
private fun makeCurrentMonth(calendar: Calendar) {
for (i in 1..calendar.getActualMaximum(Calendar.DATE)) data.add(i)
}

/**
* Generate data for the next month displayed before the last day of the current calendar.
*/
private fun makeNextMonthHead() {
var date = 1

for (i in 1..nextMonthHeadOffset) data.add(date++)
}
}


설명이 필요 없을 정도로 간단한 소스이다.


다음은 ReyclerView의 Adapter에 연결한다.


/**
* Created by WoochanLee on 22/03/2019.
*/
class RecyclerViewAdapter(val mainActivity: MainActivity) : RecyclerView.Adapter<ViewHolderHelper>() {

val baseCalendar = BaseCalendar()

init {
baseCalendar.initBaseCalendar {
refreshView(it)
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderHelper {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_schedule, parent, false)
return ViewHolderHelper(view)
}

override fun getItemCount(): Int {
return BaseCalendar.LOW_OF_CALENDAR * BaseCalendar.DAYS_OF_WEEK
}

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

if (position % BaseCalendar.DAYS_OF_WEEK == 0) holder.tv_date.setTextColor(Color.parseColor("#ff1200"))
else holder.tv_date.setTextColor(Color.parseColor("#676d6e"))

if (position < baseCalendar.prevMonthTailOffset
|| position >= baseCalendar.prevMonthTailOffset + baseCalendar.currentMonthMaxDate) {
holder.tv_date.alpha = 0.3f
} else {
holder.tv_date.alpha = 1f
}
holder.tv_date.text = baseCalendar.data[position].toString()
}

fun changeToPrevMonth() {
baseCalendar.changeToPrevMonth {
refreshView(it)
}
}

fun changeToNextMonth() {
baseCalendar.changeToNextMonth {
refreshView(it)
}
}

private fun refreshView(calendar: Calendar) {
notifyDataSetChanged()
mainActivity.refreshCurrentMonth(calendar)
}
}


그다음 구미에 맞게 layout을 커스텀 하면 다음과 같은 심플한 화면을 볼 수 있다.





실제 달력의 로직 구현 부분은 BaseCalendar인데 소스는 100줄 정도 밖에 되지 않는다.


걱정과 달리 Java의 Calendar를 잘 이용하면 달력쯤은 금방 만들 수 있다.


전체 소스가 궁금하다면 아래 링크를 참조 하길 바란다.


https://github.com/WoochanLee/Android-BaseCalendar


반응형
Comments