일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- DiffUtil
- Kotlin
- ListAdapter
- 안드로이드
- Build variants
- Jetpack
- Flow
- cleanarchitecture
- 코틀린
- 로그인
- UiState
- NavHost
- sharedFlow
- Compose
- 회원가입
- coroutine
- 파이어베이스
- MVVM
- 커스텀뷰
- Android
- 리사이클러뷰
- Authentication
- 알고리즘
- XML
- 코딩테스트
- 뷰
- 컴포즈
- NavController
- 플레이스토어
- 클린아키텍처
- Today
- Total
Grusie 안드로이드 개발 기술 블로그
[Android] 커스텀 데이트 피커 만들기(Number Picker) 본문
데이트 피커를 사용하여 날짜를 선택하는 바텀시트를 만들려고 하는데, 안드로이드 기본 데이트 피커는 커스텀이 용이하지 못한 것 같아, Number Picker로 만들어보았다.
디자이너의 요구사항은 ios와 유사한 형태의 datePicker를 요구하였으나, 힌트가 위 아래로 두 개씩 롤 모양으로 나오는 것은 커스텀하기 복잡해서 제외하고 가기로 하였다.
고려해야 할 점
1. 각 숫자 뒤에 년/월/일로 suffix가 붙어있다.
2. 각 월 / 2월일 경우(윤년)을 고려하여 일(day)를 조정해야한다.
3. 지난 과거는 선택 할 수는 있되, 선택 시 오늘 날짜로 들어가야 한다.
4. 처음 열렸을 때 날짜가 오늘 날짜여야 한다.
NumberPicker XML
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="20dp"
android:paddingBottom="30dp"
tools:context=".ui.mailbox.DatePickerBottomSheetFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="47dp"
android:layout_marginTop="45dp"
android:layout_marginBottom="30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/tv_btn_save"
app:layout_constraintTop_toTopOf="parent">
<NumberPicker
android:id="@+id/np_year"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/CustomDatePickerStyle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/np_month" />
<NumberPicker
android:id="@+id/np_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
app:layout_constraintEnd_toStartOf="@id/np_day"
app:layout_constraintStart_toEndOf="@id/np_year"
android:theme="@style/CustomDatePickerStyle"
app:layout_constraintTop_toTopOf="parent"
/>
<NumberPicker
android:id="@+id/np_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
android:theme="@style/CustomDatePickerStyle"
app:layout_constraintStart_toEndOf="@id/np_month"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
우선 각 년/월/일을 담당할 NumberPicker를 생성한다.
Theme
<style name="CustomDatePickerStyle" parent="">
<item name="colorControlNormal">@color/black_0E0E12</item>
<item name="android:textColorPrimary">@color/black_0E0E12</item>
</style>
스타일을 지정해준다.
colorControlNormal의 경우 NumberPicker의 divider 색상을 나타내고, textColorPrimary는 선택된 텍스트의 색상을 나타낸다.
DatePickerBottomSheetFragment
private fun initDatePicker() {
val currentDate = Calendar.getInstance()
val currentYear = currentDate.get(Calendar.YEAR)
val currentMonth = currentDate.get(Calendar.MONTH) + 1
binding.run {
npYear.minValue = currentYear - 100
npYear.maxValue = currentYear + 100
npMonth.minValue = 1
npMonth.maxValue = 12
npDay.minValue = 1
npDay.maxValue = getDaysInMonth(currentYear, currentMonth)
npYear.setOnValueChangedListener { _, _, p2 ->
val maxDayValue = getDaysInMonth(p2, npMonth.value)
npDay.maxValue = maxDayValue
}
npMonth.setOnValueChangedListener { _, _, p2 ->
val maxDayValue = getDaysInMonth(npYear.value, p2)
npDay.maxValue = maxDayValue
}
npYear.displayedValues = getDisplayValues(currentYear - 100, currentYear + 100, getString(R.string.mailbox_edit_date_suffix_year))
npMonth.displayedValues = getDisplayValues(1, 12, getString(R.string.mailbox_edit_date_suffix_month))
npDay.displayedValues = getDisplayValues(1, 31, getString(R.string.mailbox_edit_date_suffix_day))
npYear.value = currentYear
npMonth.value = currentMonth
npDay.value = currentDate.get(Calendar.DAY_OF_MONTH)
}
}
바텀시트의 뷰가 생성된 후, DatePicker를 초기화 하는 코드이다.
- 각각 minValue와 maxValue를 사전에 지정을 해두고, 현재 날짜를 가져와서 value로 지정해준다.
- displayedValues를 수정해줌으로서, suffix를 넣을 수 있도록 구현하였다.
- 년/월이 수정됨에 따라, 일의 최대날짜가 변경되어야 하기에 각 ValueChangedListener에서 maxDay를 가져온다.
날짜관련 함수들
private fun getDaysInMonth(year: Int, month: Int): Int {
val calendar = Calendar.getInstance()
calendar.set(Calendar.YEAR, year)
calendar.set(Calendar.MONTH, month - 1)
return calendar.getActualMaximum(Calendar.DAY_OF_MONTH)
}
private fun getDisplayValues(start: Int, end: Int, suffix: String): Array<String> {
val displayValues = mutableListOf<String>()
for (value in start..end) {
displayValues.add("${value}${suffix}")
}
return displayValues.toTypedArray()
}
getDaysInMonth() : 해당 년/월에 해당하는 월의 마지막 날을 받아오는 함수
getDisplayValues() : suffix를 붙히기 위해서, displayValues에 넣을 배열을 반환하는 함수
결과
참고
후기
DatePicker를 커스텀해서 만들어보았다. 날짜를 건드릴 경우, 생각해야할 점이 너무 많아 걱정이 많았었는데, 예상 외로 간단하게 해결하게 된 것 같았다.
'안드로이드 개발 > 뷰' 카테고리의 다른 글
[Android] 크롭 이미지 DiffUtil 연동 (선택 / 수정 / 삭제) (0) | 2024.06.12 |
---|---|
[Android] 커스텀 이미지 크롭 기능 만들기 (0) | 2024.05.28 |
[Android] 이미지 축소 확대, 회전 커스텀 뷰 만들기 (feat. 터치 이벤트 종류, 각도 함수) (0) | 2024.04.24 |
[Android] BottomSheetDialogFragment 사용하기 (+ 둥근 모서리) (0) | 2024.04.18 |
[Android] RecyclerView, ItemDecoration으로 마진 조절하기 (0) | 2024.04.11 |