'공공데이터포털'을 이용하여 'MaterialCalendarView'에 공휴일 표시하기

나고수·2021년 9월 28일
0

andriod

목록 보기
13/27
post-thumbnail

공공데이터포털의 공휴일 API 이용하여 MaterialCalendarView 캘린더에 공휴일 표시하기

▶ 결과물


⬆ 추석(9.20~9.22) 개천절 대체공휴일(10.3) 한글날(10.9) 에 빨간날 표시

⬆ 공휴일자를 누르면 날짜 옆에 공휴일이름이 적힘

❌ materialcalendarview의 기본 사용법에 대해서는 이미 많은 자료가 있으므로 설명하지 않습니다!❌

1. 공공데이터포털에서 공휴일 API 신청하기

여기를 들어간다.
회원가입을 하고 활용신청을 한다. 공개데이터라 활용신청방법이 매우매우 쉽다. 사용목적에는 앱개발공부 라고 적었음.


⬆ 인증키는 여기서 확인 할 수 있다.

2. json to kotlin 플러그인을 이용하여 response data class 만들기

먼저 응답받을 api의 url을 체크해보자

postman이나 브라우저를 이용하여 응답형태를 알아본다


⬆ 브라우저에 링크를 복붙하니까 응답이 오긴오는데 한글이 다 깨져 나온다.

⬆ postman에 링크를 복붙하면 응답이 아주 잘온다. 참고로 링크 복붙하고 get방식으로 send하면 된다.

json to kotlin 플러그인을 이용해 응답 data class를 만든다.

위에서 얻은 응답을 복붙해서 json to kotlin 플러그인에 복붙한다.


이까지하면, api 응답에 해당하는 클래스 만들기 완료
내가 사용하고 싶은건 body>itmes>item array 인데 이것만 받아오려니까 오류가 나서 일단 그냥 모든 계층을 다 받아왔다.

3. okhttp를 이용하여 공휴일API 받아오기

class GetHolidays {

    //전년도, 올해, 내년 (3년간) 휴일 일정 받아옴
    val thisYear = CalendarDay.today().year
    val threeYear = arrayOf(thisYear - 1, thisYear, thisYear + 1)


    fun getHolidays() {

        //1.클라이언트를 만들기
        val clinet = OkHttpClient.Builder().build()
        //2.요청만들기
        //3년동안 공휴일 데이터를 받아오기 위해 
        //3개년도 숫자가 들어있는 array만큼 for문을 돌렸다.
        for (i in threeYear) {
           val url =
                "http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo?solYear=${i}&ServiceKey=본인서비스키넣기&_type=json&numOfRows=100"
            val req = Request.Builder().url(url).build()
            //3.응답받기 - execute방식은 동기방식임
            //따라서 코루틴의 scope(dispatcher.IO)안에서 실행시켜줘야한다.
            clinet.newCall(req).execute().use { response ->
                val response = response.body()?.string()
                val gson = Gson()
                val holiInfo: HolidayResponse =
                    gson.fromJson(response, HolidayResponse::class.java) //json으로 받은 응답을 gson으로 변환 (아까 만든 응답 data class이름이 HolidayResponse임)

                //공휴일 중에 쉬는날(isHoliday=="Y")이 아닌날이 있는지 모르겠지만 혹시나 공휴일인데 안 쉴 수도 있으니까 쉬는날만 holidaysList에 넣어줌
                for (i in holiInfo.response.body.items.item) {
                    if (i.isHoliday == "Y")
                        holidaysList.add(i)
                }
            }
        }

    }

    companion object {
        val holidaysList = arrayListOf<HolidayResponse.Response.Body.Items.Item>()
    }
}

4. 공휴일 받아오는 okhttp를 코루틴 안에서 실행하기

//공휴일 받아오기
runblocking{
CoroutineScope(Dispatchers.IO).async {
      GetHolidays().getHolidays()}.await() //코투린 Dispatchers.IO 안에서 okhttp 실행 

//decorator에 쓸 holidayDateList 만들기

//현재 holidaysList에는 공휴일에 관한 모든 정보(날짜, 이름, isholiday 등등)이 들어있다.
//점찍어주는 class의 매개변수로는 Array<Calendarday>가 들어가야하므로 공휴일의 날짜만 들어있는 holidayDateList를 만든다.
//holidayList의 locdate(날짜)는 단순히 20210101 처럼 int 타입 이기 때문에 이걸 적절히 파싱해서 Calendarday 타입으로 변환해준다.
//나는 그냥 앞4자리, 중간2자리, 끝2자리 이렇게 지정해서 숫자를 가져왔는데 더 좋은 방법이 있는지는 모르겠다.	
    for (i in holidaysList) {
	var holidayDate = i.locdate.toString()
        var year = holidayDate.substring(0, 4).toInt()
        var month = holidayDate.substring(4, 6).toInt() - 1
        var day = holidayDate.substring(6).toInt()
        holidayDateList.add(CalendarDay.from(year, month, day))
        	}
        }

5. 공휴일 빨간 글씨로 바꿔주는 클래스 만들기

//materialcalendarview 특정 날짜에 점찍어주는 클래스는 구글에 검색하면 많이 나온다.
//그걸 약간 활용해서 공휴일에 점 찍어주는 클래스를 만들었다.

class HolidayDecorator(var holidays: ArrayList<CalendarDay>) : DayViewDecorator {
    override fun shouldDecorate(day: CalendarDay): Boolean {
        return holidays.contains(day)
    } //여기서 true가 리턴되면 decorate 함수가 실행된다.

    override fun decorate(view: DayViewFacade?) {
        if (view != null) {
            view.addSpan(ForegroundColorSpan(Color.RED))
        }
    }

}

6. decorator을 이용하여 달력 커스텀하기

//캘린더뷰에 날짜별로 색상 다르게 하는 decorator달기
//나는 공휴일 decorator말고 다른 decorator도 많다.
//예를들어 SaturdayDecorator은 토요일은 파란글자로 나오게 해놨음
//중요한건 deocorator가 순서대로 실행이 되기 때문에 만약
//HolidayDecorator > SaturdayDecorator 이 순으로 실행하면
//공휴일인 토요일에 빨간글자가 아니라 파란 글자가 나온다.
//그래서 나는 HolidayDecorator을 가장 마지막에 위치시켰다.
        binding.calendarView.addDecorators(
            AlldayDecorator(),
            EventDecorator(myViewModel.calendarDotsAll),
            SundayDecorator(),
            SaturdayDecorator(),
            TodayDecorator(get_context),
            HolidayDecorator(holidayDateList)
        )

7. 공휴일을 누르면 공휴일이름 뜨게하기

사실 날짜를 누르면 스케줄을 보여주는 fragment로 이동하는 걸 구현했는데, 공휴일이면 스케줄 뿐만 아니라 날짜 옆에 무슨 공휴일인지 적어놨을 뿐이다.

즉, 날짜 누르면 넘어가는 fragment에 아까 holidayList에 저장해놓은 공휴일이름(dataName)을 이동할 fragment에 bundle로 끼워놓고 이동한 fragment에서 찾으면 된다.

공휴일 클릭 시 공휴일이름 bundle에 담아서 다음 fragment로 보내기

//캘린더뷰의 날짜가 선택되면 그날의 일정을 보여주는 프래그먼트로 이동
        binding.calendarView.setOnDateChangedListener { widget, date, selected ->
            selctedDate = date
            val bundle = Bundle()

            //만약 선택된 날이 공휴일이면 공휴일이름을 onedayscheduleFragment에 보낸다.
            for (i in holidaysList) {
                var holidayDate = i.locdate.toString()
                var year = holidayDate.substring(0, 4).toInt()
                var month = holidayDate.substring(4, 6).toInt() - 1
                var day = holidayDate.substring(6).toInt()
                if (selctedDate == CalendarDay(year, month, day)) {
                    bundle.putString("holidayTitle", "(${i.dateName})") //공휴일 이름을 bundle에 끼워서
                }
            }

//다음 fragment로 보낸다.
            (activity as BaseActivity).replaceFragment(
                OnedaySchedulesFragment.newInstance(),
                "schedules",
                bundle
            )
        }

//참고로 프래그먼트 끼리 이동하는 함수는 이렇게 만들어놨다.
   fun replaceFragment(fragment: Fragment, tag: String,bundle: Bundle) {
        BaseActivity.fragmentManager = supportFragmentManager
        fragment.arguments=bundle
        val fragmentTransaction = BaseActivity.fragmentManager!!.beginTransaction()
        fragmentTransaction.replace(R.id.frame, fragment, tag).addToBackStack(null).commit()

    }
    

다음 fragment에서 공휴일 이름 받기

//calednarFragment에서 보낸 데이터 받기 
        var holidayTitle :String? = ""
        if (arguments != null) {
            holidayTitle = arguments?.getString("holidayTitle","")
        }
        //제목(클릭한 날짜)달기
        binding.txtSelectedDate.text =
            "" + CalendarFragment.selctedDate.year + "년 " + (CalendarFragment.selctedDate.month + 1) + "월 " + CalendarFragment.selctedDate.day + "일 " +"${holidayTitle}"
profile
되고싶다

0개의 댓글