-
Notifications
You must be signed in to change notification settings - Fork 3
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
[스터디 상세보기 뷰] 스터디 시작 시 보여질 요일 선택 뷰를 구현합니다. #481
Changes from all commits
72cc5bc
84cffff
14124d7
26829ff
c881779
3017205
3032497
2f82373
cf81e49
5f80e72
c4bff0b
94653cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.created.team201.presentation.common.customview.dayofselector | ||
|
||
import androidx.annotation.StringRes | ||
import com.created.team201.R | ||
|
||
enum class DayOfWeek(@StringRes val stringRes: Int) { | ||
MONDAY(R.string.monday), | ||
TUESDAY(R.string.tuesday), | ||
WEDNESDAY(R.string.wednesday), | ||
THURSDAY(R.string.thursday), | ||
FRIDAY(R.string.friday), | ||
SATURDAY(R.string.saturday), | ||
SUNDAY(R.string.sunday), | ||
; | ||
|
||
companion object { | ||
fun getValuesWithStartDay(startDay: DayOfWeek = MONDAY): List<DayOfWeek> { | ||
val values = DayOfWeek.values() | ||
return values.slice(startDay.ordinal until values.size) + values.slice(0 until startDay.ordinal) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
package com.created.team201.presentation.common.customview.dayofselector | ||
|
||
import android.content.Context | ||
import android.content.res.TypedArray | ||
import android.graphics.drawable.Drawable | ||
import android.util.AttributeSet | ||
import android.view.LayoutInflater | ||
import android.widget.TextView | ||
import androidx.annotation.DrawableRes | ||
import androidx.constraintlayout.widget.ConstraintLayout | ||
import androidx.core.content.res.ResourcesCompat | ||
import androidx.core.view.children | ||
import androidx.databinding.BindingAdapter | ||
import com.created.team201.R | ||
import com.created.team201.databinding.ViewDayOfWeekSelectorBinding | ||
|
||
class DayOfWeekSelector @JvmOverloads constructor( | ||
context: Context, | ||
attrs: AttributeSet? = null, | ||
) : ConstraintLayout(context, attrs) { | ||
|
||
private val binding: ViewDayOfWeekSelectorBinding by lazy { | ||
ViewDayOfWeekSelectorBinding.inflate(LayoutInflater.from(context), this, true) | ||
} | ||
|
||
private val dayTextViews: Map<DayOfWeek, TextView> by lazy { initDayTextViews() } | ||
|
||
private val textViewDays: Map<TextView, DayOfWeek> by lazy { initTextViewDays() } | ||
|
||
private var canMultiSelect: Boolean = false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. canMultiSelect의 Default값은 false이고, 생성자로 받게 되는 attributeSet은 nullable 합니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 초기화 값으로 사용한 값이 default로 사용될 수 있다는 값으로 확실하게 인지 했어야하는데 놓친부분이라 수정하였습니다! |
||
|
||
private var dayOnClick: DayOnClickListener? = null | ||
|
||
fun interface DayOnClickListener { | ||
fun onClick(day: DayOfWeek) | ||
} | ||
|
||
init { | ||
attrs?.let { setTypedArray(getAttrs(it)) } | ||
setupDayTexts() | ||
setupDayClickListeners() | ||
} | ||
|
||
private fun initDayTextViews(): Map<DayOfWeek, TextView> = | ||
DayOfWeek.getValuesWithStartDay() | ||
.zip(binding.clDayOfWeekBackground.children.filterIsInstance<TextView>().toList()) | ||
.toMap() | ||
|
||
private fun initTextViewDays(): Map<TextView, DayOfWeek> = | ||
dayTextViews.entries.associateBy({ it.value }, { it.key }) | ||
|
||
private fun getAttrs(attrs: AttributeSet): TypedArray { | ||
return context.obtainStyledAttributes(attrs, R.styleable.DayOfWeekSelector) | ||
} | ||
|
||
private fun setTypedArray(typedArray: TypedArray) { | ||
canMultiSelect = | ||
typedArray.getBoolean(R.styleable.DayOfWeekSelector_canMultipleSelect, false) | ||
|
||
if (canMultiSelect) { | ||
dayTextViews.values.forEach { | ||
it.background = getBackground(R.drawable.bg_day_of_week_selector_multi_select) | ||
} | ||
} else { | ||
dayTextViews.values.forEach { | ||
it.background = getBackground(R.drawable.bg_day_of_week_selector_single_select) | ||
} | ||
} | ||
|
||
typedArray.recycle() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오.. 이건 왜 해주는건가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 메서드 이름이 헷갈릴 수 있는데 |
||
} | ||
|
||
private fun getBackground(@DrawableRes id: Int): Drawable? = ResourcesCompat.getDrawable( | ||
resources, | ||
id, | ||
null, | ||
) | ||
|
||
private fun setupDayClickListeners() { | ||
dayTextViews.values.forEach { dayTextView -> | ||
dayTextView.setOnClickListener { it -> | ||
val clickedDay: DayOfWeek = | ||
requireNotNull(textViewDays[it]) { "$it 에 해당하는 요일이 존재하지 않습니다." } | ||
if (canMultiSelect) { | ||
selectDayTextView(dayTextView) | ||
dayOnClick?.onClick(clickedDay) | ||
return@setOnClickListener | ||
} | ||
|
||
if (it.isSelected) return@setOnClickListener | ||
|
||
selectDayTextView(dayTextView) | ||
|
||
dayTextViews.values | ||
.filterNot { it == dayTextView } | ||
.forEach { it.isSelected = false } | ||
dayOnClick?.onClick(clickedDay) | ||
} | ||
} | ||
} | ||
|
||
private fun setupDayTexts() { | ||
val days: List<DayOfWeek> = DayOfWeek.getValuesWithStartDay() | ||
dayTextViews.values.forEachIndexed { index, textView -> | ||
textView.text = resources.getString(days[index].stringRes) | ||
} | ||
} | ||
|
||
fun setSelectableDays(selectableDays: List<DayOfWeek>) { | ||
dayTextViews.keys.forEach { | ||
if (selectableDays.contains(it)) { | ||
dayTextViews[it]?.isEnabled = true | ||
return@forEach | ||
} | ||
dayTextViews[it]?.isEnabled = false | ||
} | ||
} | ||
Comment on lines
+109
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 이 함수는 어떤 역할을 수행하는 함수 인가요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 함수는 싱글 선택 모드일 때 선택할 수 있는 요일들을 리스트로 넘겨 지정하게해주는 로직입니다. 피그마에서 보여드렸듯 싱글 선택 모드는 선택 가능한 요일은 어두운 초록 동그라미, 선택 된 요일은 밝은 초록 동그라미인데요, |
||
|
||
fun selectDay(day: DayOfWeek) { | ||
dayTextViews[day]?.isSelected = true | ||
} | ||
|
||
fun selectDays(days: List<DayOfWeek>) { | ||
days.forEach { day -> | ||
val textView: TextView = | ||
requireNotNull(dayTextViews[day]) { "$day 에 해당하는 TextView가 존재하지 않습니다." } | ||
|
||
textView.isSelected = !(textView.isSelected) | ||
} | ||
} | ||
|
||
private fun selectDayTextView(day: TextView) { | ||
day.isSelected = !day.isSelected | ||
} | ||
|
||
fun getSelectedDays(): List<DayOfWeek> { | ||
val selectedDayTextViews: List<TextView> = dayTextViews.values.filter { it.isSelected } | ||
return selectedDayTextViews.map { requireNotNull(textViewDays[it]) { "$it 에 해당하는 요일이 존재하지 않습니다." } } | ||
} | ||
|
||
fun setDayOnClickListener(dayOnClickListener: DayOnClickListener) { | ||
dayOnClick = dayOnClickListener | ||
} | ||
|
||
fun getSelectedDaysSize(): Int { | ||
return dayTextViews.values.count { it.isSelected } | ||
} | ||
|
||
companion object { | ||
|
||
@JvmStatic | ||
@BindingAdapter("selectableDays") | ||
fun setSelectableDays( | ||
dayOfWeekSelector: DayOfWeekSelector, | ||
selectableDays: List<DayOfWeek>, | ||
) { | ||
dayOfWeekSelector.setSelectableDays(selectableDays) | ||
} | ||
|
||
@JvmStatic | ||
@BindingAdapter("dayOnClick") | ||
fun setDayOnClickListener( | ||
dayOfWeekSelector: DayOfWeekSelector, | ||
dayOnClickListener: DayOnClickListener, | ||
) { | ||
dayOfWeekSelector.setDayOnClickListener(dayOnClickListener) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package com.created.team201.presentation.studyDetail.bottomSheet | ||
|
||
import android.os.Bundle | ||
import android.view.View | ||
import android.widget.Toast | ||
import androidx.fragment.app.activityViewModels | ||
import com.created.team201.R | ||
import com.created.team201.databinding.FragmentStudyStartBottomSheetBinding | ||
import com.created.team201.presentation.common.BindingBottomSheetFragment | ||
import com.created.team201.presentation.studyDetail.StudyDetailViewModel | ||
|
||
class StudyStartBottomSheetFragment : | ||
BindingBottomSheetFragment<FragmentStudyStartBottomSheetBinding>( | ||
R.layout.fragment_study_start_bottom_sheet, | ||
) { | ||
private val studyDetailViewModel: StudyDetailViewModel by activityViewModels() | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
initBinding() | ||
setupDayOnClickListener() | ||
} | ||
|
||
private fun initBinding() { | ||
binding.viewModel = studyDetailViewModel | ||
binding.onCancelClickListener = { dismiss() } | ||
binding.onStartClickListener = ::onStartButtonClick | ||
binding.lifecycleOwner = viewLifecycleOwner | ||
} | ||
|
||
private fun setupDayOnClickListener() { | ||
binding.dowsStudyStartBottomSheetDayOfWeekSelector.setDayOnClickListener { | ||
val selectedDaysSize: Int = | ||
binding.dowsStudyStartBottomSheetDayOfWeekSelector.getSelectedDaysSize() | ||
|
||
if (selectedDaysSize > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 마법숫자 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 0은 0 자체로 주는 뜻이 있다고 생각합니다. 현재 로직에서도 0의 특성을 그대로 살려 로직을 작성하였습니다. 0 보다 크다 -> 자연수이다. 따라서 상수화를 하지 않았던 것인데요. 혹시 어떻게 생각하시나요? |
||
binding.tvStudyStartBottomSheetBtnStart.isEnabled = true | ||
return@setDayOnClickListener | ||
} | ||
binding.tvStudyStartBottomSheetBtnStart.isEnabled = false | ||
} | ||
} | ||
|
||
private fun onStartButtonClick() { | ||
// val studyId = arguments?.getLong(KEY_STUDY_ID) ?: INVALID_STUDY_ID | ||
// validateStudyId(studyId) | ||
// studyDetailViewModel.startStudy(studyId) ToDo: 서버 연결시 수정 | ||
Toast.makeText( | ||
context, | ||
binding.dowsStudyStartBottomSheetDayOfWeekSelector.getSelectedDays().toString(), | ||
Toast.LENGTH_SHORT, | ||
).show() | ||
dismiss() | ||
} | ||
|
||
private fun validateStudyId(studyId: Long) { | ||
if (studyId == INVALID_STUDY_ID) { | ||
Toast.makeText( | ||
context, | ||
getString(R.string.study_start_bottom_sheet_dialog_fragment_not_valid_study), | ||
Toast.LENGTH_SHORT, | ||
).show() | ||
dismiss() | ||
} | ||
} | ||
|
||
companion object { | ||
private const val KEY_STUDY_ID: String = "KEY_STUDY_ID" | ||
private const val INVALID_STUDY_ID: Long = 0L | ||
|
||
fun newInstance(studyId: Long): StudyStartBottomSheetFragment = | ||
StudyStartBottomSheetFragment().apply { | ||
arguments = Bundle().apply { | ||
putLong(KEY_STUDY_ID, studyId) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||
<item> | ||
<shape android:shape="rectangle"> | ||
<corners android:radius="10dp" /> | ||
</shape> | ||
</item> | ||
</selector> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
||
<item android:state_selected="true"> | ||
<shape android:angle="0" android:shape="oval" android:useLevel="false"> | ||
|
||
<solid android:color="@color/green10_CC3AD353" /> | ||
</shape> | ||
</item> | ||
|
||
</selector> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
||
<item android:state_enabled="true" android:state_selected="true"> | ||
<shape android:angle="0" android:shape="oval" android:useLevel="false"> | ||
|
||
<solid android:color="@color/green10_CC3AD353" /> | ||
</shape> | ||
</item> | ||
|
||
<item android:state_enabled="true" android:state_selected="false"> | ||
<shape android:angle="0" android:shape="oval" android:useLevel="false"> | ||
|
||
<solid android:color="@color/green03_016D32" /> | ||
</shape> | ||
</item> | ||
|
||
</selector> |
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="32dp" | ||
android:height="32dp" | ||
android:viewportWidth="32" | ||
android:viewportHeight="32"> | ||
<path | ||
android:pathData="M17.414,16L24,9.414L22.586,8L16,14.586L9.414,8L8,9.414L14.586,16L8,22.586L9.414,24L16,17.414L22.586,24L24,22.586L17.414,16Z" | ||
android:fillColor="#ffffff"/> | ||
</vector> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||
<item android:state_enabled="false" android:color="@color/green09_799E82" /> | ||
<item android:color="#ffffff"/> | ||
</selector> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 두개의 차이점이 무엇일까요..?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
똑같은 값을 초기화해주는 것 같은데 key로 찾기 value로 찾기로는 부족해서 두개를 따로 분리해주신걸까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사실은 <key, key> 와 같은 형태의 자료구조가 필요했습니다.
map의 경우 value로는 key값을 찾지 못합니다.
day enum으로도 해당하는 textview를 찾을 수 있고, textView로도 해당하는 day enum을 찾을 수가 없었기에 지금과 같은 방법을 사용하였습니다.