接下上集!!,我們已經完成layout,還有上傳照片了。那麼接下來我們要做的就是把選取時間的日曆叫出來,好讓我們的user選擇邀約的時間!我們主要是透過 datePicker,然後讓使用者按下date_time的edText的時候,會跳出日曆讓我們選取。
來看上一篇:https://ithelp.ithome.com.tw/articles/10271280
我們就直接建立setUpDatePickerDialog的funtion。
private fun setUpDatePickerDialog(){
//建立Calendar,好讓我們可以把DatePicker拿到的資料放到Calendar,並且顯示
val calendar = Calendar.getInstance()
//我們在這邊先給它創立後,然後只要在onClick那邊用show()把它叫出來
datePicker = DatePickerDialog(requireContext(),
{ _, year, month, dayOfMonth ->
//指定格式
val mFormat = "yyyy-MM-dd"
val sdf = SimpleDateFormat(mFormat, Locale.getDefault())
calendar.set(year,month,dayOfMonth)
//賦值給selectedDate,好讓我們晚點傳給Firestore
selectedDate = sdf.format(calendar.time)
binding.edAddInvitationDateTime.setText(sdf.format(calendar.time)) },calendar.get(Calendar.YEAR),calendar.get(Calendar.MONTH),calendar.get(Calendar.DAY_OF_MONTH))
}
在class下新增
private lateinit var datePicker: DatePickerDialog
private var selectedDate: String? = null
DatePickerDialog的參數
★我們用Calendar.getInstance()初始化的calendar會得到當下的年月份日期,所以我們這邊用的get拿到的Calendar也是拿到當下的時間。
★記得不要直接把listener拿到的年月日期改成String顯示喔! 因為它的月份是會-1的,譬如使用者選擇的是1月,它的Month會拿到0。
然後在onCreateView呼叫
setUpDatePickerDialog()
//順便新增EdText的listener
binding.edAddInvitationDateTime.setOnClickListener(this)
再onClick新增
binding.edAddInvitationDateTime -> {
datePicker.show()
}
這時候,我們發現
它竟然要點兩次!! 這時候我們就要去layout的ed_add_invitation_date_time 新增一行
//預設是當EditText被觸碰時,會得到focus,然後進到可編輯模式
//把它改成false時,它就不會得到focus,並且只響應onClick事件
android:focusableInTouchMode="false"
最後,我們發現,選擇時間的時候,竟然可以讓我們選擇過去的時間!! 這就有點不符合邏輯啦,怎麼可以約昨天呢!!? 於是這邊我們可以用設定minDate的方式來為我們強迫user只能往前選擇! 依據就是今天的日期!
我們直接在setUpDatePickerDialog()新增
//直接把datePicker的最小日期設定成當前時間
datePicker.datePicker.minDate = System.currentTimeMillis()
可能有些人不太懂最小/最大時間是什麼意思
以下為DatePicker的範例!! 不用寫在本App呦
那如果你要設定其他日期,概念上是都會有一個picker要用的calendar(也就是要set我們listener監聽到的日期),另外一個calendar則是我們想要限制的最大/小的日期。
我們以設定最晚日期maxDate為例子,一樣我們在setUpDatePickerDialog()裡面新增
//新增一個顯示最大日期的Calendar
val maxDateCalendar = Calendar.getInstance()
//把它設置時間,(year,month-1,day),記得月份要-1喔!
maxDateCalendar.set(2021,8,26)
//再把datePicker的最大日期設定成maxDateCalendar的時間就好啦
datePicker.datePicker.maxDate = maxDateCalendar.timeInMillis
如果要設定當下時間過多久後可以用
//新增一個顯示最大日期的Calendar
val maxDateCalendar = Calendar.getInstance()
//把它設置時間,第一個參數填單位,第二個則是填Int
maxDateCalendar.add(Calendar.DAY_OF_MONTH,10)
//再把datePicker的最大日期設定成maxDateCalendar的時間就好啦
datePicker.datePicker.maxDate = maxDateCalendar.timeInMillis
Firestore的 add會自動產生新的id,set則是在寫得時候就要填入id,而我們這次的做法,就是把透過add新增的文件(它就會有自動生成的id),把它update到我們資料原本裡面的id,因為原本預設是""
我們先去MatchingViewModel新增以下
fun addInvitationToFireStore(fragment:AddInvitationFragment,invitation: Invitation){
Firebase.firestore.collection(Constant.INVITATION)
.add(invitation)
.addOnSuccessListener {
//我們透過HashMap來新增值
val mHashMap = HashMap<String,Any>()
mHashMap[Constant.ID] = it.id
it.update(mHashMap)
.addOnSuccessListener {
fragment.addInvitationSuccess()
}
.addOnFailureListener {
fragment.addInvitationFail(it.toString())
}
}
.addOnFailureListener {
fragment.addInvitationFail(it.toString())
}
}
我們希望文件id跟資料的id是保持一致,這樣我們才可以快速找到想要的資料
新增完後,我們發現我們的Constant還需要新增一些資料
//新增collection的名稱
const val INVITATION: String = "invitation"
//Invitation內id欄位
const val ID: String = "id"
最後在來到Fragment新增成功/失敗的funtion
fun addInvitationSuccess(){
hideDialog()
showSnackBar(resources.getString(R.string.add_invitation_successful),false)
}
fun addInvitationFail(e: String){
hideDialog()
showSnackBar(e,true)
}
string
<string name="add_invitation_successful">上傳邀約成功!</string>
既然我們都有許多editText,那我們當然要確認值
private fun validDataForm(): Boolean{
return when{
mUri.isNullOrBlank() ->{
showSnackBar(resources.getString(R.string.hint_select_your_image),true)
false
}
selectedPetType.isNullOrBlank() -> {
showSnackBar(resources.getString(R.string.hint_enter_pet_type),true)
false
} TextUtils.isEmpty(binding.edAddInvitationPetTypeDescription.text.toString().trim()) -> { showSnackBar(resources.getString(R.string.hint_enter_pet_type_description),true)
false
}
selectedArea.isNullOrBlank() -> {
showSnackBar(resources.getString(R.string.hint_enter_your_area),true)
false
}
TextUtils.isEmpty(binding.edAddInvitationDatePlace.text.toString().trim()) -> {
showSnackBar(resources.getString(R.string.hint_enter_date_place),true)
false
}
selectedDate.isNullOrBlank() ->{
showSnackBar(resources.getString(R.string.hint_enter_date_time),true)
false
}
TextUtils.isEmpty(binding.edAddInvitationNote.text.toString().trim()) ->{
showSnackBar(resources.getString(R.string.hint_enter_date_note),true)
false
}
else -> true
}
}
接下來回到 onClick
binding.btnAddInvitationFragmentSubmit ->{
showDialog(resources.getString(R.string.please_wait))
if (validDataForm()){
val invitation = Invitation(
user_id = accountViewModel.userDetail.value!!.id,
pet_image = mUri!!,
pet_type = selectedPetType!!,
pet_type_description = binding.edAddInvitationPetTypeDescription.text.toString().trim(),
area = selectedArea!!,
date_place = binding.edAddInvitationDatePlace.text.toString().trim(),
date_time = selectedDate!!,
note = binding.edAddInvitationNote.text.toString().trim()
)
matchingViewModel.addInvitationToFireStore(this,invitation)
}
}
再過來別忘了在onCreateView新增
binding.btnAddInvitationFragmentSubmit.setOnClickListener(this)
!!! 這時候我們發現一個錯誤,就是userDetail沒有資料,儘管我們在AccountViewModel的地方是把getUserDetail的funtion放在init{}裡面,但是有沒有可能使用者根本還沒有用到需要進到AccountViewModel的時候,就進來我們的AddInvitationFragment了呢? 所以我們就要在進到這個MatchingActivity的第一個Fragment的地方,就先把userDetail放到livedata啦!
到DashboardFragment的onCreateView來新增
accountViewModel.getUserDetail()
也別忘了在class下面新增
private val accountViewModel: AccountViewModel by sharedViewModel()
binding.toolbarAddInvitationFragment.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
binding.toolbarAddInvitationFragment.setNavigationOnClickListener {
requireActivity().onBackPressed()
}
private fun dismissActivityActionBarAndBottomNavigationView(){
val activityInstance = this.activity as MatchingActivity
activityInstance.supportActionBar?.hide()
activityInstance.findViewById<BottomNavigationView>(R.id.nav_view).visibility = View.GONE
}
然後在 onCreateView呼叫就可以啦!!
大功告成!!!! 明天預計會在我們的HomeFragment新增我們有發成功的資料!!