이런 식으로 알림 시간을 설정해줘야 했는데,
- 오전/오후
- 1시 ~ 12시
- 00분 or 30분
으로 설정되어야 하는데, 그러려면 넘버 피커가 3개 필요했다. 스타일도 설정해줘야 해서 꽤 힘들었던 기억이 있다.
알림 기본 시간은 hint로 표현했는데, 이 또한 hint 텍스트를 읽어들여 넘버피커에 넣을 값으로 변환해줘야 했고, 힌 번 선택한 시간에 대해서는 알림 시간을 바꾸려 들어갔을 때 똑같은 시간의 넘버피커를 띄워줘야 했다.
<style name="AppTheme.NumberPicker" parent="">
<item name="android:textColorPrimary">@color/white</item>
<item name="android:textColorHighlight">@color/gray_700</item>
<item name="android:textSize">20sp</item>
<item name="selectableItemBackground">@color/Jblue</item>
<item name="colorControlNormal">@color/colorPrimary</item>
</style>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bottom_nav_bg"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/period_done_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"
android:text="Done"
android:textSize="20sp"
android:textColor="@color/Jblue"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="horizontal"
android:background="@color/gray_700"
app:layout_constraintTop_toBottomOf="@id/period_done_tv"
app:layout_constraintStart_toStartOf="parent">
<NumberPicker
android:id="@+id/alert_datepicker_ampm"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="50dp"
android:layout_marginEnd="20dp"
android:selectionDividerHeight="1dp"
android:theme="@style/AppTheme.NumberPicker"
tools:targetApi="q" />
<NumberPicker
android:id="@+id/alert_datepicker_hour"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:selectionDividerHeight="1dp"
android:theme="@style/AppTheme.NumberPicker"
tools:targetApi="q" />
<NumberPicker
android:id="@+id/alert_datepicker_minute"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="20dp"
android:layout_marginEnd="50dp"
android:selectionDividerHeight="1dp"
android:theme="@style/AppTheme.NumberPicker"
tools:targetApi="q" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
앞선 1에서 만든 Number 스타일을 지정하면 위와 같은 식으로 보인다.
저기 오른쪽 화면에 +가 3개 보이는 것은 각각
- 오전/오후
- 1시 ~ 12시
- 00분 or 30분
넘버피커 자리이다.
넘버피커 값 넣어주기 + 시간 선택하고 보여주기 작업
interface AlertPickerDialogInterface {
fun onClickDoneButton(id: Int, meridiem: Int, hour: Int, minute: Int)
}
@RequiresApi(Build.VERSION_CODES.O)
class AlertPickerDialog(
pickerDialogInterface: AlertPickerDialogInterface,
id: Int,
meridiem: Int, hour: Int, minute: Int
) : BottomSheetDialogFragment() {
private lateinit var binding: DialogAlertpickerBinding
// Initializing a new string array with elements
private val meridiemArr = arrayOf("오전", "오후") // am, pm
private val hoursArr = Array(12) { (it + 1).toString() }
private val minutesArr = arrayOf("00", "30", "00", "30")
private var pickerDialogInterface: AlertPickerDialogInterface? = null
private var id: Int? = null
// 선택된 값
private var meridiem: Int? = null
private var hour: Int? = null
private var minute: Int? = null
init {
this.meridiem = meridiem
this.hour = hour
this.minute = minute
this.id = id
this.pickerDialogInterface = pickerDialogInterface
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DialogAlertpickerBinding.inflate(inflater, container, false)
val ampmPicker = binding.alertDatepickerAmpm
val hoursPicker = binding.alertDatepickerHour
val minutesPicker = binding.alertDatepickerMinute
// Done 버튼 눌러서 창 닫기
binding.periodDoneTv.setOnClickListener {
// 값 가져오기
meridiem = ampmPicker.value
hour = hoursPicker.value
minute = minutesPicker.value
this.pickerDialogInterface?.onClickDoneButton(id!!, meridiem!!, hour!!, minute!!)
// 닫기
dismiss()
}
// 순환 안되게 막기
ampmPicker.wrapSelectorWheel = false
// editText 설정 해제
ampmPicker.descendantFocusability = NumberPicker.FOCUS_BLOCK_DESCENDANTS
// 최소값 설정
ampmPicker.minValue = 0
hoursPicker.minValue = 1
minutesPicker.minValue = 0
// 최대값 설정
ampmPicker.maxValue = meridiemArr.size - 1
hoursPicker.maxValue = 12
minutesPicker.maxValue = minutesArr.size - 1
// array 값 넣기
ampmPicker.displayedValues = meridiemArr
hoursPicker.displayedValues = hoursArr
minutesPicker.displayedValues = minutesArr
// 선택된 기본값 설정
ampmPicker.value = meridiem!!
hoursPicker.value = hour!!
minutesPicker.value = minute!!
return binding.root
}
}
넘버피커를 나오게 할 화면이다.
계획 세우기, 계획 실행 알람에 대해
- 오전/오후
- 1시 ~ 12시
- 00분 or 30분
를 모두 전역 변수로 선언했다.
오전/오후는 0과 1로 구분하고,
기본 알람 시간이 계획 세우기는 오전 9시고, 계획 실행은 오후 9시이기에 해당 값으로 초기화해주었다.
// 계획 세우기
private var planMeridiem: Int = 0
private var planHour: Int = 9
private var planMinute: Int = 0
// 계획 실행
private var actionMeridiem: Int = 1
private var actionHour: Int = 9
private var actionMinute: Int = 0
또한, 서버에 보낼 때는 계획 세우기와 실행 시간 형태를
{
"planTime": "string", // 계획 세우기 알림 시간(hh:mm:00 24h)
"actionTime": "string"// 실전 확인 알림 시간(hh:mm:00 24h)
}
형태로 보내주어야 했기에 해당 부분은
// 서버에 보낼 값
private lateinit var alertTime: AlertBody
private var planTime = ""
private var actionTime = ""
로 String 형태의 전역 변수를 만들어줬다.
그리고 계획 세우기와 계획 실행 알림 시간을 보여주는 부분은 모두 EditText로 이루어져 있는데, 이를 클릭할 때 현재 설정된 오전/오후, 시간, 분을 앞선 3번에서 만든 AlertPickerDialog
에 전달해 주었다.
// 계획 세우기 알림
binding.myAlertMakingPlanEt.setOnClickListener {
// 알림 dialog
val dialog = AlertPickerDialog(this@AlertLoginFragment, 0, planMeridiem, planHour, planMinute)
activity?.supportFragmentManager?.let { it1 -> dialog.show(it1, dialog.tag) }
}
// 계획 실행 알림
binding.myAlertCarryingPlanEt.setOnClickListener {
// 알림 dialog
val dialog = AlertPickerDialog(this@AlertLoginFragment, 1, actionMeridiem, actionHour, actionMinute)
activity?.supportFragmentManager?.let { it1 -> dialog.show(it1, dialog.tag) }
}
계획 세우기는 태그를 0으로, 계획 시간은 1로 넘겨 넘버피커에서 둘을 구별할 수 있게 하였다.
class AlertLoginFragment : BaseFragment<FragmentLoginAlertBinding> (FragmentLoginAlertBinding::bind, R.layout.fragment_login_alert),
AlertPickerDialogInterface {
override fun onClickDoneButton(id: Int, meridiem: Int, hour: Int, minute: Int) {
val ampm = if(meridiem == 0) "오전" else "오후"
val min = if(minute % 2 == 0) "00" else "30"
if (id == 0) { // 계획 세우기 알림
// 선택된 value 저장
planMeridiem = meridiem
planHour = hour
planMinute = minute
// 텍스트 반영
binding.myAlertMakingPlanEt.setText("${ampm} ${hour}시 ${min}분")
} else { // 실천 확인 알림
actionMeridiem = meridiem
actionHour = hour
actionMinute = minute
binding.myAlertCarryingPlanEt.setText("${ampm} ${hour}시 ${min}분")
}
}
}
앞서 전역변수에 썼던 계획 세우기 알림, 실천 확인 알림 변수에 각각 넘버피커의 결과로 받아온 값(오전/오후, 시간, 분)을 넣어준다. 이렇게 다시 받아온 값을 통해, 다시 넘버피커를 열더라도 이전에 선택했던 시간으로 보여줄 수 있다.
인터페이스로부터 넘겨받은 id가 0이면 계획 세우기 알림이므로 해당 EditText를 업데이트하고, 아니라면 실천 확인 알림 EditText를 업데이트한다.
{
"planTime": "string", // 계획 세우기 알림 시간(hh:mm:00 24h)
"actionTime": "string"// 실전 확인 알림 시간(hh:mm:00 24h)
}
private fun putUserAlert() {
// 조합
var putPlanHour = if (planMeridiem == 1) planHour + 12 else planHour
if (planHour == 12) { // 정오, 자정 처리
putPlanHour -= 12
}
val planHourStr = if (putPlanHour < 10) "0$putPlanHour" else putPlanHour.toString()
val planMinStr = if (planMinute == 0) "00" else "30"
var putActionHour = if (actionMeridiem == 1) actionHour + 12 else actionHour
if (actionHour == 12) {
putActionHour -= 12
}
val actionHourStr = if (putActionHour < 10) "0$putActionHour" else putActionHour.toString()
val actionMinStr = if (actionMinute == 0) "00" else "30"
planTime = "${planHourStr}:${planMinStr}:00"
actionTime = "${actionHourStr}:${actionMinStr}:00"
alertTime = AlertBody(planTime, actionTime)
Log.d("alertTime", alertTime.toString())
}
class AlertLoginFragment : BaseFragment<FragmentLoginAlertBinding> (FragmentLoginAlertBinding::bind, R.layout.fragment_login_alert),
AlertPickerDialogInterface {
// 계획 세우기
private var planMeridiem: Int = 0
private var planHour: Int = 9
private var planMinute: Int = 0
// 계획 실행
private var actionMeridiem: Int = 1
private var actionHour: Int = 9
private var actionMinute: Int = 0
// 서버에 보낼 값
private lateinit var alertTime: AlertBody
private var planTime = ""
private var actionTime = ""
@RequiresApi(Build.VERSION_CODES.O)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
setOnClickListeners()
// 푸시 알림 권한 여부 확인
checkPermission()
}
private fun putUserAlert() {
// 조합
var putPlanHour = if (planMeridiem == 1) planHour + 12 else planHour
if (planHour == 12) { // 정오, 자정 처리
putPlanHour -= 12
}
val planHourStr = if (putPlanHour < 10) "0$putPlanHour" else putPlanHour.toString()
val planMinStr = if (planMinute == 0) "00" else "30"
var putActionHour = if (actionMeridiem == 1) actionHour + 12 else actionHour
if (actionHour == 12) {
putActionHour -= 12
}
val actionHourStr = if (putActionHour < 10) "0$putActionHour" else putActionHour.toString()
val actionMinStr = if (actionMinute == 0) "00" else "30"
planTime = "${planHourStr}:${planMinStr}:00"
actionTime = "${actionHourStr}:${actionMinStr}:00"
alertTime = AlertBody(planTime, actionTime)
Log.d("alertTime", alertTime.toString())
}
@RequiresApi(Build.VERSION_CODES.O)
private fun setOnClickListeners() {
// 뒤로가기
...
// 다음 버튼
...
// 계획 세우기 알림
binding.myAlertMakingPlanEt.setOnClickListener {
// 알림 dialog
val dialog = AlertPickerDialog(this@AlertLoginFragment, 0, planMeridiem, planHour, planMinute)
activity?.supportFragmentManager?.let { it1 -> dialog.show(it1, dialog.tag) }
}
// 계획 실행 알림
binding.myAlertCarryingPlanEt.setOnClickListener {
// 알림 dialog
val dialog = AlertPickerDialog(this@AlertLoginFragment, 1, actionMeridiem, actionHour, actionMinute)
activity?.supportFragmentManager?.let { it1 -> dialog.show(it1, dialog.tag) }
}
}
private fun activateRegisterBtn() {
with(binding) {
val planAlert = myAlertMakingPlanEt.text?.count()
val actionAlert = myAlertCarryingPlanEt.text?.count()
if (planAlert!! > 0 && actionAlert!! > 0) {
nextButtonEnabled(true)
} else nextButtonEnabled(false)
}
}
private fun editTextChangedListener() {
with(binding) {
val watcher = MyEditWatcher()
// 계획 세우기 알림
myAlertMakingPlanEt.addTextChangedListener(watcher)
// 실천 확인 알림
myAlertCarryingPlanEt.addTextChangedListener(watcher)
}
}
서버 통신과 넘버피커와 직접적인 관련이 없는 부분은 빼고 첨부한다.
정말 재미있었다. 생각할 부분이 많긴 했지만, 그리고 전역 변수가 많아 그다지 효율적인 코드라고 생각하지는 않지만 그래도 여러 조건을 생각하고 값을 원하는 형태로 바꿔 보여주면서 꽤 많은 생각을 하게 되었다. 푸시 알림도, 넘버 피커도 처음이었지만 그래서 더 의미있던 경험이었다.