업무 중 달력이 필요해서 라이브러리를 가져다가 썼는데 속도는 괜찮지만 각 셀마다 가로세로 비율 조절이 쉽지 않고 했다쳐도 layout measure 타이밍에 따른 버그가 있어 한번 직접 만들어보자고 마음 먹었다
단일 CustomView를 통하여 만들라고 하였지만 애니메이션 코드 관리가 엄두가 나지 않아서 ViewPager를 활용하여 애니메이션과 화면전환을 기본으로 넘기고 간단한 CustomView만 가지고 만들기로 결심!
구글링을 한 결과 대단하신 분이 만든 예제가 있었고
[참고 예제] => https://leveloper.tistory.com/174
해당 예제를 참고하여 재해석 하였습니다.
class CalendarViewAdapter : FragmentStateAdapter {
companion object {
const val START_POSITION = Int.MAX_VALUE / 2
}
constructor(fragmentActivity: FragmentActivity) : super(fragmentActivity)
constructor(fragment: Fragment) : super(fragment)
constructor(fragmentManager: FragmentManager, lifecycle: Lifecycle) : super(
fragmentManager,
lifecycle
)
override fun getItemCount(): Int = Int.MAX_VALUE
override fun getItemId(position: Int): Long {
val cal = Calendar.getInstance()
var currentYear = cal.get(Calendar.YEAR)
var currentMonth = cal.get(Calendar.MONTH) + 1
val move = position - START_POSITION
val bias = if(move < 0) -1 else 1
val moveYear = abs(move) / 12 * bias
val moveMonth = abs(move) % 12 * bias
currentYear += moveYear
when {
(currentMonth + moveMonth) < 1 -> {
currentMonth = 12 + (currentMonth + moveMonth)
currentYear--
}
(currentMonth + moveMonth) > 12 -> {
currentMonth = (currentMonth + moveMonth) - 12
currentYear++
}
else -> {
currentMonth = (currentMonth + moveMonth)
}
}
return (currentYear*100 + currentMonth).toLong()
}
override fun containsItem(itemId: Long): Boolean = 200000L < itemId && itemId < 210001L
override fun createFragment(position: Int): Fragment {
val itemId = getItemId(position)
return CalendarFragment().apply {
arguments = Bundle().apply {
putLong("year" , itemId / 100L)
putLong("month" , itemId % 100L)
}
}
}
}
연월 계산은 사실 Calendar에 MONTH 값만 입력하면 자동으로 계산이 됩니다만 도전정신에 이끌려 직접 작성해봤습니다 :)
class CalendarFragment : Fragment() {
private lateinit var binding : FragmentCalendarBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentCalendarBinding.inflate(inflater , container , false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val year = requireArguments().getLong("year")
val month = requireArguments().getLong("month")
binding.calendarView.setYearMonth(year.toInt() , month.toInt())
}
}
class CalendarDayView : View {
private val dayRects = List<Rect>(42) { _ -> Rect()}
private val dayPaints = List<Paint>(42) { _ -> Paint().apply {
color = when(Random.nextInt(0 until 3)) {
0 -> Color.RED
1 -> Color.BLUE
else -> Color.GREEN
}
textSize = 30f
}}
private val dates = MutableList<Int>(42){ i -> -1}
private var year = Calendar.getInstance().get(Calendar.YEAR)
private var month = Calendar.getInstance().get(Calendar.MONTH) + 1
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
val widthAspect = w / 7
val heightAspect = h / 6
var stackWidth = 0
var stackHeight = 0
for(i in 0 until 6) {
stackWidth = 0
for(j in 0 until 7) {
dayRects[i * 7 + j].set(stackWidth , stackHeight , stackWidth + widthAspect , stackHeight + heightAspect )
stackWidth += widthAspect
}
stackHeight += heightAspect
}
}
fun setYearMonth(year : Int , month : Int) {
this.year = year
this.month = month
val cal = Calendar.getInstance()
cal.set(Calendar.YEAR , year)
cal.set(Calendar.MONTH , month)
cal.set(Calendar.DATE , 0)
val dayOfMonth = cal.get(Calendar.DAY_OF_MONTH)
cal.set(Calendar.DATE , 1)
val dayOfWeek = cal.get(Calendar.DAY_OF_WEEK)
cal.set(Calendar.MONTH , month -1)
cal.set(Calendar.DATE , 0)
var prevMonthLastDate = cal.get(Calendar.DAY_OF_MONTH)
for(i in dayOfWeek-2 downTo 0) {
dates[i] = prevMonthLastDate--;
dayPaints[i].color = Color.GRAY
}
var date = 1
for(i in dayOfWeek-1 until dayOfWeek+dayOfMonth-1) {
dates[i] = date++
dayPaints[i].color = when {
(i % 7 == 0) -> Color.RED
(i % 7 == 6) -> Color.BLUE
else -> Color.BLACK
}
}
var nextMonthDate = 1
for(i in dayOfWeek+dayOfMonth-1 until 42) {
dates[i] = nextMonthDate++
dayPaints[i].color = Color.GRAY
}
invalidate()
}
override fun onDraw(canvas: Canvas?) {
canvas?.let {
for(i in 0 until 6) {
for(j in 0 until 7) {
//it.drawRect(dayRects[i* 7 + j] , dayPaints[i * 7 + j])
it.drawText(
dates[i * 7 + j].toString() ,
dayRects[i * 7 + j].left.toFloat() ,
dayRects[i * 7 + j].bottom.toFloat(),
dayPaints[i * 7 + j]
)
}
}
}
}
}
2021년 4월
2021년 5월