안녕하세요.
오늘은 dagger + kotlin + MVVm 환경에서 달력을 만드는 실습을 공유하고자 합니다.
기본적인 컨셉은 ViewModel에서 오늘의 날짜를 입력받으면, 해당 달의 날짜들을 각각의 아이템으로 취급하여 달력아이템에 삽입하는 컨셉입니다.
첫번째로 달력을 구성하는 Item View입니다.
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<import type="android.view.View"/>
<variable
name="model"
type="Your ItemModel class in here(Calendar Item)"/>
</data>
<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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/planner_item_day"
bind_number="@{model.date}"
android:textSize="16sp"
android:textColor="@{(model.thisMonth == true) ? @color/colorBlack : @color/colorGrey}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:gravity="center_vertical"/>
<!--
달력의 Item에 이벤트표시를 강조하기 위한 점 Image
-->
<ImageView
android:id="@+id/event_exist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_margin="4dp"
android:src="@drawable/ic_baseline_brightness_1_24"
bind_visibility="@{model.event}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<item_calendar.xml>
다음으로 달력 전체를 담고있는 fragment입니다.(여기 RecyclerView에서 위의 아이템을 담고있는 구조입니다.)
<layout>
<data>
<import type="android.view.View"/>
<variable
name="model"
type="Your ViewModel in here(CalendarViewModel)" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.fragment.CalendarFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/calendar_top_layout"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/calendar_select_layout"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:orientation="vertical"
android:gravity="center_vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/calendar_year_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
bind_year="@{model.date}"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<TextView
android:id="@+id/calendar_month_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="16sp"
bind_month="@{model.date}" />
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/calendar_select_bt"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginRight="16dp"
android:background="@android:color/transparent"
android:text="확인"
android:textColor="#ffffff"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/calendar_today_bt"
android:layout_width="48dp"
android:layout_height="32dp"
android:layout_marginLeft="16dp"
android:background="@android:color/transparent"
android:text="TODAY"
android:textColor="#ffffff"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/calendar_previous_bt"
android:layout_width="32dp"
android:layout_height="32dp"
android:background="@drawable/ic_baseline_chevron_left_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="@{() -> model.moveMonth(-1)}"/>
<Button
android:id="@+id/calendar_next_bt"
android:layout_width="32dp"
android:layout_height="32dp"
android:background="@drawable/ic_baseline_chevron_right_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.7"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="@{() -> model.moveMonth(1)}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/monthly_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/calendar_top_layout"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/history_tv"
android:padding="4dp"
android:gravity="center"
android:textColor="@color/colorBlack"
android:textSize="16sp"
android:text="@string/text_monthly_quantity"/>
<TextView
android:id="@+id/history_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/calendar_top_layout"
app:layout_constraintLeft_toRightOf="@id/monthly_tv"
app:layout_constraintRight_toLeftOf="@id/average_tv"
android:padding="4dp"
android:gravity="center"
android:textColor="@color/colorBlack"
android:textSize="16sp"
android:text="@string/text_history_quantity"/>
<TextView
android:id="@+id/average_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/calendar_top_layout"
app:layout_constraintLeft_toRightOf="@id/history_tv"
app:layout_constraintRight_toLeftOf="@id/day_tv"
android:padding="4dp"
android:gravity="center"
android:textColor="@color/colorBlack"
android:textSize="16sp"
android:text="@string/text_monthly_average"/>
<TextView
android:id="@+id/day_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/calendar_top_layout"
app:layout_constraintLeft_toRightOf="@id/average_tv"
app:layout_constraintRight_toRightOf="parent"
android:padding="4dp"
android:gravity="center"
android:textColor="@color/colorBlack"
android:textSize="16sp"
android:text="@string/text_daily_quantity"/>
<TextView
android:id="@+id/monthly_count"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/monthly_tv"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/whole_count"
app:layout_constraintDimensionRatio="1:1"
android:gravity="center"
android:textColor="@color/colorBlack"
android:textSize="16sp"
bind_number="@{model.monthlyQuantity}"
android:background="@drawable/circle"/>
<TextView
android:id="@+id/whole_count"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/history_tv"
app:layout_constraintLeft_toRightOf="@id/monthly_count"
app:layout_constraintRight_toLeftOf="@id/avg_count"
app:layout_constraintDimensionRatio="1:1"
android:gravity="center"
android:textColor="@color/colorBlack"
android:textSize="16sp"
bind_number="@{model.historyQuantity}"
android:background="@drawable/circle"/>
<TextView
android:id="@+id/avg_count"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/average_tv"
app:layout_constraintLeft_toRightOf="@id/whole_count"
app:layout_constraintRight_toLeftOf="@id/day_count"
app:layout_constraintDimensionRatio="1:1"
android:gravity="center"
android:textColor="@color/colorBlack"
android:textSize="16sp"
bind_float="@{model.averageQuantity}"
android:background="@drawable/circle"/>
<TextView
android:id="@+id/day_count"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/average_tv"
app:layout_constraintLeft_toRightOf="@id/avg_count"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintDimensionRatio="1:1"
android:gravity="center"
android:textColor="@color/colorBlack"
android:textSize="16sp"
bind_number="@{model.dailyQuantity}"
android:background="@drawable/circle"/>
<include
android:id="@+id/calendar_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
layout="@layout/header_calendar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/monthly_count"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/calendar_recyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/calendar_header"
app:layout_constraintBottom_toBottomOf="parent"
android:background="@drawable/border"
bind_adapter="@{model.adapter}"
bind_calendar_items="@{model.calendarItemList}">
</androidx.recyclerview.widget.RecyclerView>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="@id/calendar_recyclerview"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:visibility="@{(model._dataLoading == true) ? View.VISIBLE : View.GONE}"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/calendar_select_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#80000000"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/calendar_top_layout">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#ffffff"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<NumberPicker
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/year_picker"
android:layout_weight="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</NumberPicker>
<NumberPicker
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/month_picker"
android:layout_weight="2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</NumberPicker>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<fragment_calendar.xml>
위의 item_caleadar를 calendar_recyclerview에서 그 수량만큼 추가하는 구조입니다.
다음으로는 데이터 클래스를 보도록하겠습니다.
//package Your package in here
data class CalendarItem (
var date:Int,
var dayOfWeek: Int,
var thisMonth:Boolean,
var time:Long,
var event:Boolean
)
<CalendarItem.kt>
기본적으로 아이템에 들어갈 날짜를 표시할 date와 달력의 가장 처음과 마지막에 이전달과 다음달의 item을 추가하기 위하여 각 item의 요일을 dayOfWeek에 저장합니다.
그리고 이번달과 이전달, 다음달의 색깔을 구분하기 위하여 thisMonth라는 변수를 추가하였습니다.
time과 event는 여러분의 달력에 기호에 맞도록 추가/변경하시면 될 것 같습니다.
다음으로 CalendarFragment입니다.
//package Your package in here
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.*
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.myhome.smoketimer.R
import com.myhome.smoketimer.application.MyApplication
import com.myhome.smoketimer.databinding.FragmentCalendarBinding
import com.myhome.smoketimer.view.fragment.adapter.CalendarRecyclerAdapter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
import kotlin.math.roundToInt
class CalendarFragment : Fragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
lateinit var viewModel: CalendarViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
(requireActivity().application as MyApplication).appComponent.inject(this)
val binding = FragmentCalendarBinding.inflate(inflater)
viewModel = ViewModelProviders.of(this, viewModelFactory)[CalendarViewModel::class.java]
val owner = this as LifecycleOwner
binding.lifecycleOwner = owner
binding.model = viewModel
viewModel.apply {
_date.observe(owner, Observer {
//_date가 변경되면 달력을 초기화함
CoroutineScope(Dispatchers.IO).launch {
_dataLoading.postValue(true)
setCalendarItems(it)
_dataLoading.postValue(false)
}
})
//달력의 줄 수가 다를 수 있기 때문에 해당 달의 주 수에 맞게 높이를 미리 지정함
//어댑터를 생성하여 bindingadapter를 통해 적용시킴
//ViewModel에서 변경이벤트를 동작함
_heightCount.observe(owner, Observer{
val myAdapter = CalendarRecyclerAdapter(it)
//오늘의 날짜를 강조표시하기 위하여 어댑터에 오늘날짜를 전달
myAdapter.dateEvent = dateEvent
_adapter.postValue(myAdapter)
})
val today = Date()
dateEvent.postValue(getDayOfStartTime(today))
val format = SimpleDateFormat("yyyy-MM")
thisMonth = format.format(today)
_date.postValue(today)
_dataLoading.postValue(false)
//아이템의 date를 클릭했을 경우 해당날짜를 알아볼 수 있도록 어댑터에서 클릭 이벤트를 수신함
dateEvent.observe(owner, Observer {
val clickedDate = Date()
clickedDate.time = it
_date.postValue(clickedDate)
})
}
return binding.root
}
companion object {
var INSTANCE:CalendarFragment? = null
@JvmStatic
fun getInstance():CalendarFragment{
if(INSTANCE == null){
INSTANCE = CalendarFragment()
}
return INSTANCE!!
}
}
}
의존성 주입을 위하여 Dagger라이브러리를 사용하였는데 이부분은 각자 구현방식대로 변경하시면 될것같습니다.(@Inject를 통한 ViewModel객체 생성을 OnCreateView에서 직접 수행하시면 됩니다.)
다음은 대망의 ViewModel입니다.
package com.myhome.smoketimer.view.fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.myhome.smoketimer.model.CalendarItem
import com.myhome.smoketimer.repository.SmokeRepository
import com.myhome.smoketimer.util.SingleLiveEvent
import com.myhome.smoketimer.view.fragment.adapter.CalendarRecyclerAdapter
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
class CalendarViewModel @Inject constructor():ViewModel(){
val _date = MutableLiveData<Date>()
val _dataLoading = MutableLiveData(false)
val _heightCount = MutableLiveData(1)
val _calendarItemList = MutableLiveData<List<CalendarItem>>()
val _historyQuantity = MutableLiveData<Int>()
val _monthlyQuantity = MutableLiveData<Int>()
val _averageQuantity = MutableLiveData<Float>()
val _dailyQuantity = MutableLiveData<Int>()
val _adapter = MutableLiveData<CalendarRecyclerAdapter>()
val date: LiveData<Date> get() = _date
val heightCount: LiveData<Int> get() = _heightCount
val calendarItemList:LiveData<List<CalendarItem>> get() = _calendarItemList
val historyQuantity:LiveData<Int> get() = _historyQuantity
val monthlyQuantity:LiveData<Int> get() = _monthlyQuantity
val averageQuantity:LiveData<Float> get() = _averageQuantity
val dailyQuantity:LiveData<Int> get() = _dailyQuantity
val adapter:LiveData<CalendarRecyclerAdapter> get() = _adapter
val dateEvent = SingleLiveEvent<Long>()
var startMonth = 0.toLong()
var endMonth = 0.toLong()
var days = 0
var thisMonth = ""
fun setCalendarItems(date: Date){
val calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Seoul"), Locale.KOREA)
calendar.timeInMillis = date.time
val dayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH)
//해당 달의 첫번째 날짜로 설정
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
calendar.set(Calendar.HOUR_OF_DAY, 0)
//달력의 주 수에 따라 높이 결정
_heightCount.postValue(calendar.get(Calendar.WEEK_OF_MONTH))
val format = SimpleDateFormat("yyyy-MM")
//이달의 첫 날짜로 세팅
calendar.set(Calendar.DAY_OF_MONTH, 1)
//이달의 마지막 날짜를 세팅할 Calendar 추가
val endCalendar = calendar.clone() as Calendar
endCalendar.set(Calendar.DATE, dayOfMonth)
// 아이템 채우기
var dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK)
//달력 아이템으로 추가할 리스트
val calendarItem = ArrayList<CalendarItem>()
//첫날의 요일이 일요일이 아닐 경우(전달의 달력 Preview item 추가)
//수요일일 경우 화, 월, 일 형태로 하루씩 줄면서 리스트에 담기기 때문에
//stack 자료구조를 이용해서 달력 아이템을 추가할 때는 일요일부터 반대로 추가될 수 있도록 설정
val stack = Stack<CalendarItem>()
val prevCalendar = calendar.clone() as Calendar
prevCalendar.add(Calendar.DATE, -1)//전달의 마지막날로 세팅
while(prevCalendar.get(Calendar.DAY_OF_WEEK) != 7){//일요일이 될때까지 반복
stack.push(CalendarItem(prevCalendar.get(Calendar.DATE), prevCalendar.get(Calendar.DAY_OF_WEEK), false, prevCalendar.timeInMillis, false))
prevCalendar.add(Calendar.DATE, -1)
}
while(!stack.isEmpty()) {//스택에 추가된 순서를 반대로 리스트에 추가
calendarItem.add(stack.pop())
}
do{
//다음달 1일이 될때까지 이번달 달력 하루씩 증가시키며 반복
val event = false // 이벤트효과를 위해 true로 설정 가능
calendarItem.add(CalendarItem(calendar.get(Calendar.DATE), calendar.get(Calendar.DAY_OF_WEEK), true, calendar.timeInMillis, event))
calendar.add(Calendar.DATE, 1)
}while(calendar.get(Calendar.DATE) != 1)
//다음달 preview 생성
dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK)
if(dayOfWeek != 1){
//위의 반복문이 토요일까지 추가하고 calendar변수가 일요일이 되었을 경우
//한주가 추가되지 않아도 되므로 실행안함
val nextCalendar = calendar.clone() as Calendar
while(nextCalendar.get(Calendar.DAY_OF_WEEK) != 1){
//일요일이 되기 전(토요일)까지 preview item 추가
calendarItem.add(CalendarItem(nextCalendar.get(Calendar.DATE), nextCalendar.get(Calendar.DAY_OF_WEEK), false, nextCalendar.timeInMillis, false))
nextCalendar.add(Calendar.DATE, 1)
}
}
//캘린더 아이템을 추가하여 view에서 반영할 수 있도록 함
_calendarItemList.postValue(calendarItem)
}
fun moveMonth(value:Int){ // 다음달 이벤트
val thisDate = date.value
val calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Seoul"))
calendar.time = thisDate
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + value)
_date.postValue(calendar.time)
}
fun getDayOfStartTime(date:Date):Long{ // 오늘의 처음시간을 가져오는 함수
val format = SimpleDateFormat("yyyy-MM-dd")
val dateString = format.format(date)
val dayOfStart = format.parse(dateString)
return dayOfStart.time
}
}
<CalendarViewModel.kt>
작업중이던 프로젝트에서 따온 코드이기 때문에 미처 정리가 안된 코드도 포함되어 있습니다. 양해 부탁드립니다.
코드 수행과 관련해서는 주석에 설명을 달아놓았으니 참고하시면 될 것 같습니다.(궁금하신 점은 댓글로 따로 문의 부탁드립니다.)
다음으로는 RecylerView의 Adapter입니다.
//package Your package name in here
import android.annotation.SuppressLint
import android.content.Intent
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.RecyclerView
import com.myhome.smoketimer.R
import com.myhome.smoketimer.databinding.ItemCalendarBinding
import com.myhome.smoketimer.model.CalendarItem
import com.myhome.smoketimer.util.SingleLiveEvent
import kotlinx.android.synthetic.main.item_calendar.view.*
class CalendarRecyclerAdapter(val heightCount:Int) :RecyclerView.Adapter<CalendarRecyclerAdapter.ViewHolder>(){
var items:List<CalendarItem> = ArrayList<CalendarItem>()
lateinit var dateEvent:MutableLiveData<Long>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemCalendarBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.root.layoutParams = LinearLayout.LayoutParams(parent.width / 7, parent.height / heightCount)
return ViewHolder(binding)
}
override fun getItemCount(): Int {
return items.size
}
@SuppressLint("ResourceAsColor")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
holder.binding.root.setOnClickListener {
if(items[position].event){
dateEvent.postValue(items[position].time)
}
}
if(dateEvent.value?.equals(items[position].time) == true){
holder.binding.root.planner_item_day.setBackgroundColor(R.color.colorAccent)
}
}
inner class ViewHolder(val binding:ItemCalendarBinding):RecyclerView.ViewHolder(binding.root){
fun bind(calendarItem:CalendarItem){
binding.model = calendarItem
}
}
}
<CalendarRecyclerAdapter.kt>
이부분에서는 생성자에 한 주의 크기를 결정하는 HeightCount변수가 있다는 점만 생각해주시면 됩니다.
마지막으로 View와 ViewModel을 바인딩하기 위한 BindingAdapter 코드로 포스팅을 마무리하도록 하겠습니다.
//package your package name in here
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.databinding.BindingAdapter
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.myhome.smoketimer.R
import com.myhome.smoketimer.model.CalendarItem
import com.myhome.smoketimer.model.Smoke
import com.myhome.smoketimer.view.fragment.adapter.CalendarRecyclerAdapter
import com.myhome.smoketimer.view.fragment.adapter.SmokeAdapter
import java.text.SimpleDateFormat
import java.util.*
object BindingAdapter {
@BindingAdapter("bind_number")
@JvmStatic
fun bindNumber(textView:TextView, number:Int?) {
if(number == null) return
textView.text = number.toString()
}
@BindingAdapter("bind_float")
@JvmStatic
fun bindFloat(textView:TextView, float:Float?) {
if(float == null) return
textView.text = float.toString()
}
@BindingAdapter(value=["bind_adapter", "bind_calendar_items"])
@JvmStatic
fun bindPlannerItem(recyclerView: RecyclerView, adapter:CalendarRecyclerAdapter?, items:List<CalendarItem>?){
if(items == null || adapter == null) return
if(recyclerView.adapter == null){
recyclerView.layoutManager = GridLayoutManager(recyclerView.context, 7, LinearLayoutManager.VERTICAL, false)
recyclerView.setHasFixedSize(true)
}
recyclerView.adapter = adapter
(recyclerView.adapter as CalendarRecyclerAdapter).items = items
// (recyclerView.adapter as CalendarRecyclerAdapter).dateEvent = dateEvent
recyclerView.adapter?.notifyDataSetChanged()
}
@BindingAdapter("bind_month")
@JvmStatic
fun bindMonth(textView: TextView, date:Date?){
if(date == null) return
val format = SimpleDateFormat("MM")
textView.text = format.format(date) + textView.context.getString(R.string.text_month)
}
@BindingAdapter("bind_year")
@JvmStatic
fun bindYear(textView: TextView, date:Date?){
if(date == null) return
val format = SimpleDateFormat("yyyy")
textView.text = format.format(date) + textView.context.getString(R.string.text_year)
}
@BindingAdapter("bind_visibility")
@JvmStatic
fun bindVisibility(imageView: ImageView, event:Boolean?){
if(event == null) return
if(event){
imageView.visibility = View.VISIBLE
}
else{
imageView.visibility = View.GONE
}
}
}
이상으로 달력만들기 포스팅을 마치도록 하겠습니다.
궁금하신점은 댓글로 남겨주시기 바랍니다. 도움이 되셨다면 좋아요 부탁드려요~~!!
'IT 프로그래밍-Android' 카테고리의 다른 글
[안드로이드] AlarmManager 사용 시 주의할 점 (0) | 2022.02.06 |
---|---|
[Retrofit] java.lang.IllegalStateException: Expected Android API level 21+ but was 29 에러 해결 (0) | 2022.01.18 |
[안드로이드] 카카오 로그인 API v2 적용하기 (0) | 2021.10.23 |
다음(카카오) 주소 검색(우편번호 검색) api 오류 해결 (0) | 2021.05.18 |
BottomSheetDialog MVVM(ViewModel, Databinding) 적용방법 (0) | 2020.09.23 |