Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat : 학사 일정 데이트 피커 #263

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.dongyang.android.youdongknowme.ui.view.schedule

import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.NumberPicker
import androidx.fragment.app.activityViewModels
import com.dongyang.android.youdongknowme.R
import com.dongyang.android.youdongknowme.databinding.DialogDatepickerBinding
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.prolificinteractive.materialcalendarview.CalendarDay
import org.threeten.bp.LocalDate

class DatePickerDialog(
private val year: Int,
private val month: Int,
private val listener: ScheduleClickListener
Comment on lines +18 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

생성자로 연도, 월, 클릭리스너를 받아야 할까요?

) : BottomSheetDialogFragment() {

private var _binding: DialogDatepickerBinding? = null
private val binding get() = _binding!!

override fun onCreateView(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fragment의 생명주기에 대해서 알고 계신가요 ? onCreateView에서 해야할 일과 onViewCreated에서 해야할 일을 분리하면 좋을 것 같슴니다
참고할 블로그

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이전에 타프로젝트에서 언급주셨던 부분이라 기억하고 있습니다.
로직 변경을 하면서 고려해보겠습니다.

inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {

_binding = DialogDatepickerBinding.inflate(inflater, container, false)
val view = binding.root
Comment on lines +32 to +33
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

뷰바인딩을 초기화하고 반환 이후에 binding 객체를 사용하는 게 더 안전해보입니다!


// 현재 연도
val currentYear = LocalDate.now().year

// 순환 막기
binding.numberpickerDialogDatepickerYear.wrapSelectorWheel = false
binding.numberpickerDialogDatepickerMonth.wrapSelectorWheel = false

// editText 설정 막기
binding.numberpickerDialogDatepickerYear.descendantFocusability =
NumberPicker.FOCUS_BLOCK_DESCENDANTS
binding.numberpickerDialogDatepickerMonth.descendantFocusability =
NumberPicker.FOCUS_BLOCK_DESCENDANTS

// 연도 최소/최대값 설정 및 출력 방식 설정
with(binding.numberpickerDialogDatepickerYear) {
minValue = currentYear - 1
maxValue = currentYear + 1
displayedValues =
((minValue..maxValue).map { "$it${getString(R.string.calendar_year)}" }
.toTypedArray())
}

// 월 최소/최대값 설정 및 출력 방식 설정
with(binding.numberpickerDialogDatepickerMonth) {
minValue = 1
maxValue = if (year == currentYear + 1) 2 else 12

// 연도 선택에 따른 월의 최대값 동적 설정
huiwoo-jo marked this conversation as resolved.
Show resolved Hide resolved
binding.numberpickerDialogDatepickerYear.setOnValueChangedListener { _, _, newYear ->
maxValue = if (newYear == currentYear + 1) 2 else 12
}

displayedValues =
((minValue..maxValue).map { "$it${getString(R.string.calendar_month)}" }
.toTypedArray())
}

// 초기 설정
binding.numberpickerDialogDatepickerYear.value = year
binding.numberpickerDialogDatepickerMonth.value = month

Comment on lines +72 to +75
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

초기값을 설정하는 건 onCreateView에서의 역할이 아닌 것 같습니다! 뷰바인딩이 세팅이 끝난 후인 onViewCreated로 옮겨보시죵

return view
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

binding.tvDialogPermissionComplete.setOnClickListener {
val year = binding.numberpickerDialogDatepickerYear.value
val month = binding.numberpickerDialogDatepickerMonth.value
val date = CalendarDay.from(year, month, 1)

listener.buttonClick(date = date)

dismiss()
Comment on lines +83 to +90
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금 방법은 MVVM 패턴이 아닌 것 같습니다! view 변경 시 ViewModel 내 메서드 호출 > fragment 에서 viewModel의 파라미터 observing > 학사일정 변경
이 로직대로 가야 할 것 같아요! 지금 로직은 interface로 직접 값을 넘기는 것 같습니다.

}
}


override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

return BottomSheetDialog(requireContext(), R.style.CustomBottomSheetDialogTheme)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 친구는 onCreateView에서 작동을 안 하나요 ?
onCreateView에

setStyle(STYLE_NORMAL, R.style.WebsosoDialogTheme)

요 코드 넣어보시면 될 거예요

}

override fun onDestroyView() {

super.onDestroyView()
_binding = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.dongyang.android.youdongknowme.ui.view.schedule

import com.prolificinteractive.materialcalendarview.CalendarDay

interface ScheduleClickListener {
fun buttonClick(date: CalendarDay)
}
Comment on lines +5 to +7
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인터페이스를 제거해보시죠!

Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
package com.dongyang.android.youdongknowme.ui.view.schedule

import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.PorterDuff
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.dongyang.android.youdongknowme.R
Expand All @@ -21,20 +14,65 @@ import org.koin.androidx.viewmodel.ext.android.viewModel
import org.threeten.bp.LocalDate

/* 학사 일정 화면 */
class ScheduleFragment : BaseFragment<FragmentScheduleBinding, ScheduleViewModel>() {
class ScheduleFragment : BaseFragment<FragmentScheduleBinding, ScheduleViewModel>(),
ScheduleClickListener {

override val layoutResourceId: Int = R.layout.fragment_schedule
override val viewModel: ScheduleViewModel by viewModel()

private lateinit var adapter: ScheduleAdapter

private var _year = LocalDate.now().year.toString()
private var year = _year

private var _month = LocalDate.now().month.toString()
private var month = _month
Comment on lines +25 to +29
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

둘 다 접근제한자가 private인데 프래그먼트에서 배킹프로퍼티를 사용하는 이유가 있나요 ?

Copy link
Contributor Author

@huiwoo-jo huiwoo-jo Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존 코드들을 참고하여서 배킹 프로퍼티의 의미를 제대로 생각 못한거 같습니다🤔
코드를 다시 살펴본 후 명확히 답변 드리곘습니다.


override fun buttonClick(date: CalendarDay) {
viewModel.setPickedDate(date)
}
Comment on lines +31 to +33
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 인터페이스를 만들지 않고, 다이얼로그에서 버튼을 클릭했을 때 viewModel.setPickedDate 를 사용하면 될 것 같아요


// 연/월 방식으로 타이틀 처리
fun setCalenderHeader() {
binding.mvScheduleCalendar.setTitleFormatter { day ->
val inputText: LocalDate = day.date
val calendarHeaderElements = inputText.toString().split("-").toTypedArray()

year = calendarHeaderElements[0]
month = calendarHeaderElements[1]

val calendarHeaderBuilder = StringBuilder()
calendarHeaderBuilder.append(year)
.append(getString(R.string.calendar_year))
.append(" ")
.append(month)
.append(getString(R.string.calendar_month))
calendarHeaderBuilder.toString()
}
}

override fun initStartView() {

binding.vm = viewModel

viewModel.setPickedDate(binding.mvScheduleCalendar.currentDate)

binding.mvScheduleCalendar.leftArrow.setTintList(ColorStateList.valueOf(ContextCompat.getColor(this.requireContext(), R.color.blue300)))
binding.mvScheduleCalendar.rightArrow.setTintList(ColorStateList.valueOf(ContextCompat.getColor(this.requireContext(), R.color.blue300)))
binding.mvScheduleCalendar.leftArrow.setTintList(
ColorStateList.valueOf(
ContextCompat.getColor(
this.requireContext(),
R.color.blue300
)
)
)
binding.mvScheduleCalendar.rightArrow.setTintList(
ColorStateList.valueOf(
ContextCompat.getColor(
this.requireContext(),
R.color.blue300
)
)
)

adapter = ScheduleAdapter()
binding.rvScheduleList.apply {
Expand All @@ -55,10 +93,18 @@ class ScheduleFragment : BaseFragment<FragmentScheduleBinding, ScheduleViewModel
else dismissLoading()
}

viewModel.pickYear.observe(viewLifecycleOwner) {
viewModel.getSchedules()
}

viewModel.pickMonth.observe(viewLifecycleOwner) {
viewModel.getSchedules()
}

viewModel.selectedDate.observe(viewLifecycleOwner) { date ->
updateCalendarViewHeader(date)
}

viewModel.scheduleList.observe(viewLifecycleOwner) {
adapter.submitList(it)
}
Expand All @@ -68,34 +114,35 @@ class ScheduleFragment : BaseFragment<FragmentScheduleBinding, ScheduleViewModel
}
}

private fun updateCalendarViewHeader(date: CalendarDay) {
binding.mvScheduleCalendar.setCurrentDate(date)

setCalenderHeader()
}


override fun initAfterBinding() {

binding.mvScheduleCalendar.setOnMonthChangedListener { _, date ->
viewModel.setPickedDate(date)
}

binding.mvScheduleCalendar.setOnTitleClickListener {
val dialog = DatePickerDialog(
year = year.toInt(),
month = month.toInt(),
listener = this@ScheduleFragment
)
dialog.show(requireActivity().supportFragmentManager, "CustomDialog")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tag 값은 상수화해서 사용하는 게 좋을 것 같아요! 그리고 커스텀 다이얼로그 보다는 다른 네이밍을 사용하면 좋을 것 같습니당

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requireActivityrequireContext 의 차이가 무엇일까요! 어디에는 위에서 컬러값을 가져올 땐 requireContext를 사용하셨는데 요기선 requireActivity 를 사용하신 이유가 궁금해용

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dialog Manager에서만 사용하는 부분으로 중요하다고 생각을 못하여 네이밍을 크게 신경쓰지 못했네요.
ScheduleDialog정도로 수정 후 Tag 상수화를 사용하겠습니다.

requireActivity 액티비티 반환으로 activity!! 와 동일한 작용으로 알고있습니다.
dialogactivity 상속 개념으로 인하여 사용한 부분입니다.

requireContext는 컬러 리소스값에 접근해야하기에 Context를 사용합니다.
getContext와 달리 null 값이 아닌 context를 필수적으로 전달한다고 하여 사용했습니다.
requireContext는 null 처리가 되지 않아 코드 확인 후 getContext 사용과 requireContext 중 뭐가 더 나을지 고민해보겠습니다.

}

// 최소 날짜, 최대 날짜 지정
binding.mvScheduleCalendar.apply {
this.state().edit().setMinimumDate(CalendarDay.from(2023, 1, 1))
.setMaximumDate(CalendarDay.from(2025, 2, 28))
.commit()
}

// 연/월 방식으로 타이틀 처리
binding.mvScheduleCalendar.setTitleFormatter { day ->
val inputText: LocalDate = day.date
val calendarHeaderElements = inputText.toString().split("-").toTypedArray()

val year = calendarHeaderElements[0]
val month = calendarHeaderElements[1]

val calendarHeaderBuilder = StringBuilder()
calendarHeaderBuilder.append(year)
.append(getString(R.string.calendar_year))
.append(" ")
.append(month)
.append(getString(R.string.calendar_month))
calendarHeaderBuilder.toString()
}
setCalenderHeader()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ class ScheduleViewModel(private val scheduleRepository: ScheduleRepository) : Ba
private val _pickMonth = MutableLiveData<Int>()
val pickMonth: LiveData<Int> = _pickMonth

private val _selectedDate = MutableLiveData<CalendarDay>()
val selectedDate: LiveData<CalendarDay> = _selectedDate

fun setPickedDate(date: CalendarDay) {
_selectedDate.value = date
_pickYear.value = date.year
_pickMonth.value = date.month
getSchedules()
}

fun getSchedules() {
Expand All @@ -51,7 +56,6 @@ class ScheduleViewModel(private val scheduleRepository: ScheduleRepository) : Ba
val scheduleList = result.data

// 선택한 연월 조건에 따라 리스트 출력

_scheduleList.postValue(getSchedulesForPickDate(scheduleList))

scheduleRepository.setLocalSchedules(Gson().toJson(scheduleList))
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/bg_white_radius_20dp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="20dp" />
<solid android:color="@color/white" />
</shape>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_dialog_handle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="5dp"
android:viewportWidth="48"
android:viewportHeight="5">
<path
android:pathData="M0,2.035C0,0.911 0.911,0 2.035,0H45.965C47.089,0 48,0.911 48,2.035V2.035C48,3.159 47.089,4.07 45.965,4.07H2.035C0.911,4.07 0,3.159 0,2.035V2.035Z"
android:fillColor="#7F8295"/>
</vector>
56 changes: 56 additions & 0 deletions app/src/main/res/layout/dialog_datepicker.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:layout_margin="20dp">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_dialog_handle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="10dp"/>

<NumberPicker
android:id="@+id/numberpicker_dialog_datepicker_year"
style="@style/PretendardMedium24"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="36dp"
android:theme="@style/Theme.YouDongKnowMe.NumberPicker"
app:layout_constraintEnd_toStartOf="@id/numberpicker_dialog_datepicker_month"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<NumberPicker
android:id="@+id/numberpicker_dialog_datepicker_month"
style="@style/PretendardMedium24"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="36dp"
android:theme="@style/Theme.YouDongKnowMe.NumberPicker"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/numberpicker_dialog_datepicker_year"
app:layout_constraintTop_toTopOf="parent" />

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/tv_dialog_permission_complete"
style="@style/PretendardMedium24"
android:layout_width="0dp"
android:layout_height="56dp"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="20dp"
android:background="@drawable/btn_gray_to_blue_10dp_enabled"
android:text="@string/dialog_datepicker_comlete"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/numberpicker_dialog_datepicker_year" />
</androidx.constraintlayout.widget.ConstraintLayout>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,7 @@
<string name="dialog_permission_early_content">내용</string>
<string name="dialog_permission_early_cancel">취소</string>
<string name="dialog_permission_early_complete">설정 화면 이동</string>

<!-- datepicker dialog -->
<string name="dialog_datepicker_comlete">완료</string>
</resources>
8 changes: 8 additions & 0 deletions app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,12 @@
<item name="android:fontFamily">@font/pretendard_medium</item>
<item name="android:textSize">@dimen/font_size_12</item>
</style>

<style name="CustomBottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
<item name="bottomSheetStyle">@style/CustomBottomSheetCornerStyle</item>
</style>

<style name="CustomBottomSheetCornerStyle" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">@drawable/bg_white_radius_20dp</item>
</style>
</resources>
Loading
Loading