Skip to content

Commit

Permalink
✨ Feat: 보관함 캘린더 현재 달에 따라 텍스트 색 변경 구현
Browse files Browse the repository at this point in the history
Related to: #348
  • Loading branch information
edv-Shin committed Dec 15, 2024
1 parent 4169a7d commit 418aa56
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 51 deletions.
4 changes: 2 additions & 2 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 22 additions & 11 deletions .idea/other.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -112,38 +112,48 @@ class DiaryCalendarFragment :
var scrollJob: Job? = null // 코루틴 Job 변수로 스크롤을 제어

binding.diaryCalendarRv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
private var isScrollingFast = false
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
// 스크롤이 멈췄을 때만 API 호출
scrollJob?.cancel() // 기존 Job 취소
scrollJob = CoroutineScope(Dispatchers.Main).launch {
delay(200)
setDiaryIndicator(recyclerView)
updateVisibleMonth(recyclerView) // 스크롤 멈췄을 때 중앙 달 업데이트
}
isScrollingFast = false // 스크롤 멈추면 속도 상태 초기화
}
}

override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
checkFirstDay(recyclerView)
updateReturnBtnVisible()
// 스크롤 속도가 빠른지 감지
isScrollingFast = dy > recyclerView.height / 3
if (!isScrollingFast) {
if (scrollJob == null || scrollJob?.isActive == false) {
scrollJob = CoroutineScope(Dispatchers.Main).launch {
delay(300)
setDiaryIndicator(recyclerView)
}
if (scrollJob == null || scrollJob?.isActive == false) {
scrollJob = CoroutineScope(Dispatchers.Main).launch {
delay(300)
updateVisibleMonth(recyclerView) // 스크롤 도중에도 중앙 달 업데이트
}
}
}
})
}

private fun updateVisibleMonth(recyclerView: RecyclerView) {
val layoutManager = recyclerView.layoutManager as GridLayoutManager
val firstVisiblePosition = layoutManager.findFirstVisibleItemPosition()
val lastVisiblePosition = layoutManager.findLastVisibleItemPosition()

// 중앙에 위치한 아이템 계산
val centerPosition = (firstVisiblePosition + lastVisiblePosition) / 2
val centerItem = calendarAdapter.getItemAtPosition(centerPosition)

// 어댑터에 중앙에 보이는 달 전달
centerItem?.let {
val currentMonth = it.toYearMonth()
calendarAdapter.updateVisibleMonth(currentMonth)
}
}




private fun updateReturnBtnVisible() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,63 @@ import com.mongmong.namo.domain.model.CalendarDate
import com.mongmong.namo.domain.model.CalendarDay
import com.mongmong.namo.domain.model.ScheduleType
import com.mongmong.namo.presentation.utils.converter.DiaryDateConverter.toYearMonth
import java.util.Calendar

class DiaryCalendarAdapter(
private val recyclerView: RecyclerView,
private val items: List<CalendarDay>,
private val listener: OnCalendarListener
) : RecyclerView.Adapter<DiaryCalendarAdapter.ViewHolder>() {

private var diaryDates: MutableMap<String, Set<CalendarDate>> = mutableMapOf() // "yyyy-MM": [기록된 날짜들]
private var isOpeningBottomSheet: Boolean = false
private var isBottomSheetOpen: Boolean = false // 바텀시트 상태
private var selectedDateView: TextView? = null // 선택된 날짜의 TextView
private var selectedDate: CalendarDay? = null // 선택된 날짜
private var visibleMonth: String? = null // 현재 화면 중앙의 달 ("yyyy-MM")

fun updateDiaryDates(yearMonth: String, diaryDates: Set<CalendarDate>) {
this.diaryDates[yearMonth] = diaryDates

items.forEachIndexed { index, calendarDay ->
if (calendarDay.toYearMonth() == yearMonth && diaryDates.any { it.date == calendarDay.date.toString() }) {
if (calendarDay.toYearMonth() == yearMonth) {
notifyItemChanged(index)
}
}
}

fun updateBottomSheetState(isOpened: Boolean) {
this.isOpeningBottomSheet = isOpened

// 화면에 보이는 아이템들의 위치를 가져옴
val layoutManager = recyclerView.layoutManager ?: return
val firstVisibleItemPosition = (layoutManager as GridLayoutManager).findFirstVisibleItemPosition()
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()

// 화면에 보이는 아이템들에 대해서는 애니메이션 적용
for (i in firstVisibleItemPosition..lastVisibleItemPosition) {
val viewHolder = recyclerView.findViewHolderForAdapterPosition(i) as ViewHolder
viewHolder.updateItemWithAnimate(isOpened)
fun updateVisibleMonth(visibleMonth: String) {
if (this.visibleMonth != visibleMonth) {
this.visibleMonth = visibleMonth
notifyDataSetChanged() // 현재 보이는 달이 바뀌었을 때 전체 갱신
}
}

// 화면에 보이지 않는 아이템들은 높이 변경을 직접 적용하도록 notify
if (firstVisibleItemPosition > 0) {
notifyItemRangeChanged(0, firstVisibleItemPosition)
}
if (lastVisibleItemPosition < itemCount - 1) {
notifyItemRangeChanged(lastVisibleItemPosition + 1, itemCount - lastVisibleItemPosition - 1)
fun updateBottomSheetState(isOpened: Boolean) {
if (isBottomSheetOpen != isOpened) {
isBottomSheetOpen = isOpened

// 화면에 보이는 아이템들의 위치를 가져옴
val layoutManager = recyclerView.layoutManager ?: return
val firstVisibleItemPosition = (layoutManager as GridLayoutManager).findFirstVisibleItemPosition()
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()

// 보이는 아이템은 애니메이션 적용
for (i in firstVisibleItemPosition..lastVisibleItemPosition) {
val viewHolder = recyclerView.findViewHolderForAdapterPosition(i) as? ViewHolder
viewHolder?.updateItemWithAnimate(isOpened)
}

// 보이지 않는 아이템은 notify로 높이 변경
if (firstVisibleItemPosition > 0) {
for (i in 0 until firstVisibleItemPosition) {
notifyItemChanged(i, isOpened)
}
}
if (lastVisibleItemPosition < itemCount - 1) {
for (i in lastVisibleItemPosition + 1 until itemCount) {
notifyItemChanged(i, isOpened)
}
}
}
}

Expand All @@ -65,7 +81,18 @@ class DiaryCalendarAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
holder.bind(item)
holder.updateItem(isOpeningBottomSheet)

// 현재 달 상태에 따라 아이템을 업데이트
holder.updateItem(isBottomSheetOpen)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.isNotEmpty()) {
val isOpened = payloads[0] as Boolean
holder.updateItem(isOpened)
} else {
super.onBindViewHolder(holder, position, payloads)
}
}

fun getItemAtPosition(position: Int): CalendarDay? {
Expand All @@ -80,6 +107,7 @@ class DiaryCalendarAdapter(
fun bind(calendarDay: CalendarDay) {
binding.calendarDay = calendarDay

// Indicator 설정
val dateType = diaryDates[calendarDay.toYearMonth()]?.find { it.date == calendarDay.date.toString() }?.type
binding.diaryCalendarHasDiaryIndicatorIv.visibility = if (dateType != null) View.VISIBLE else View.GONE

Expand All @@ -96,20 +124,29 @@ class DiaryCalendarAdapter(
)
}

// 텍스트 색상 설정
binding.diaryCalendarDateTv.setTextColor(
binding.root.context.getColor(
when {
calendarDay.isSameDate(selectedDate) -> R.color.main
calendarDay.isAfterToday() -> R.color.text_placeholder
else -> R.color.main_text
calendarDay.isSameDate(selectedDate) -> R.color.main // 선택된 날짜
calendarDay.toYearMonth() == visibleMonth -> R.color.main_text // 현재 달
else -> R.color.text_placeholder // 다른 달
}
)
)

// 클릭 리스너 설정
binding.root.setOnClickListener {
updateSelectedDateView(binding.diaryCalendarDateTv, calendarDay)
listener.onCalendarDayClick(calendarDay)
}

// 아이템 높이 설정
val height = dpToPx(if (isBottomSheetOpen) OPEN_HEIGHT else CLOSE_HEIGHT, binding.root.context)
binding.root.layoutParams = binding.root.layoutParams.apply {
this.height = height
}
binding.root.requestLayout()
}

fun updateItem(isOpening: Boolean) {
Expand Down Expand Up @@ -147,7 +184,7 @@ class DiaryCalendarAdapter(
if (selectedDate?.isSameDate(newDate) == true) {
selectedDateView?.setTextColor(
binding.diaryCalendarDateTv.context.getColor(
if (newDate.isAfterToday()) R.color.text_placeholder else R.color.main_text
if (newDate.toYearMonth() == visibleMonth) R.color.main_text else R.color.text_placeholder
)
)
selectedDate = null
Expand All @@ -156,7 +193,7 @@ class DiaryCalendarAdapter(
// 새로운 날짜를 선택
selectedDateView?.setTextColor(
binding.diaryCalendarDateTv.context.getColor(
if (newDate.isAfterToday()) R.color.text_placeholder else R.color.main_text
if (selectedDate?.toYearMonth() == visibleMonth) R.color.main_text else R.color.text_placeholder
)
)
selectedDate = newDate
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/layout/item_diary_calendar_date.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
android:visibility="gone"
android:layout_marginTop="8dp"
android:layout_marginBottom="13dp"
android:src="@drawable/ic_archive_diary_small"
app:layout_constraintTop_toBottomOf="@id/diary_calendar_date_tv"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
Expand Down

0 comments on commit 418aa56

Please sign in to comment.