【Day10】AddInvitationFragment(下) X DatePickerDialog

接下上集!!,我们已经完成layout,还有上传照片了。那麽接下来我们要做的就是把选取时间的日历叫出来,好让我们的user选择邀约的时间!我们主要是透过 datePicker,然後让使用者按下date_time的edText的时候,会跳出日历让我们选取。

来看上一篇:https://ithelp.ithome.com.tw/articles/10271280

1.取得日期

  • 建立Calendar的实例
  • 建立DatePickerDialog

我们就直接建立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的参数

  • context
  • listener:这部分我们直接用lambda来写,它会监听使用者选择的时间,我们直接在里面setText,让我们的EditText显示时间。
  • year:指定初始年
  • month:指定初始月
  • dayOfMonth:指定初始日期

★我们用Calendar.getInstance()初始化的calendar会得到当下的年月份日期,所以我们这边用的get拿到的Calendar也是拿到当下的时间。

★记得不要直接把listener拿到的年月日期改成String显示喔! 因为它的月份是会-1的,譬如使用者选择的是1月,它的Month会拿到0。

然後在onCreateView呼叫

setUpDatePickerDialog()
//顺便新增EdText的listener
binding.edAddInvitationDateTime.setOnClickListener(this)

再onClick新增

binding.edAddInvitationDateTime -> {
                datePicker.show()
            }

这时候,我们发现

day10.focus

它竟然要点两次!! 这时候我们就要去layout的ed_add_invitation_date_time 新增一行

//预设是当EditText被触碰时,会得到focus,然後进到可编辑模式
//把它改成false时,它就不会得到focus,并且只响应onClick事件
android:focusableInTouchMode="false"

最後,我们发现,选择时间的时候,竟然可以让我们选择过去的时间!! 这就有点不符合逻辑啦,怎麽可以约昨天呢!!? 於是这边我们可以用设定minDate的方式来为我们强迫user只能往前选择! 依据就是今天的日期!

我们直接在setUpDatePickerDialog()新增

//直接把datePicker的最小日期设定成当前时间
datePicker.datePicker.minDate = System.currentTimeMillis()

可能有些人不太懂最小/最大时间是什麽意思

  • minDate,可以选择的最早时间,如果是设定System.currentTimeMills,则就可以从今天开始往未来选择。
  • maxDate,反之,可以选择时间的最晚时间,如设定System.currentTimeMills,则代表可以从过去开始选择,但最後面的时间,只能选择现在。

以下为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

2.上传资料到FireStore

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是保持一致,这样我们才可以快速找到想要的资料

https://ithelp.ithome.com.tw/upload/images/20210925/20138017wZkFyZoQHi.png

新增完後,我们发现我们的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()

3.返回键+删除bottomNavigation跟ActionBar

3.1返回键

				binding.toolbarAddInvitationFragment.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
        binding.toolbarAddInvitationFragment.setNavigationOnClickListener {
            requireActivity().onBackPressed()
        }

3.2.删除bottomNavigation跟ActionBar

private fun dismissActivityActionBarAndBottomNavigationView(){
        val activityInstance = this.activity as MatchingActivity
        activityInstance.supportActionBar?.hide()
        activityInstance.findViewById<BottomNavigationView>(R.id.nav_view).visibility = View.GONE

    }

然後在 onCreateView呼叫就可以啦!!

day10.finish

大功告成!!!! 明天预计会在我们的HomeFragment新增我们有发成功的资料!!


<<:  追求JS小姊姊系列 Day10 -- 如果时间能重来,我不想跟工具人聊天(下)

>>:  聊聊structure concurrency 结构化并发

<Day7>以模拟帐户作示范 — 登入 Shioaji API

● 接下来几章都是先以模拟帐户作登入,尚未使用正式证券户帐户登入 如果尚未有永丰金证券帐户的朋友,但...

Day29- 这是替身攻击!! 替换你的pod Telepresence

在前面我们介绍到了各种建立以及产生pod的方式,但是当你已经建构好一个系统後,写好的程序要更新以及测...

Day 29 - Summary

本文将於赛後同步刊登於笔者部落格 有兴趣学习更多 Kubernetes/DevOps/Linux 相...

Day 8 - 初探Vue Component

在Vue的世界中,是由一个又一个的元件所构成,而我们可以透过自订元件的方式量身打造客制化的元件并进行...

【在厨房想30天的演算法】Day 13 资料结构:堆积 Heap

Aloha~又是我少女人妻 Uerica!今天是教师节啊~大家小时候都会写感谢恩师的卡片吗?记得刚上...