今天我们要新增一个搜寻排序的功能! 还记得我们之前把从datePicker拿到的资料转成String,再把它传上去Firestore,而update_time则是直接用System.currentTimeMillis()传入Firestore。但是我们Firestore排序的话,我们需要排序的是时间,所以我们要修改我们传时间的方法!
## 1.1.修改Invitation
data class Invitation(
val id: String = "",
val user_id: String = "",
val pet_image: String = "",
val pet_type: String = "",
val pet_type_description: String = "",
val area: String = "",
val date_place: String = "",
val date_time: Timestamp? = null,
val note: String = "",
//我们可以透过Firebase的方法拿到现在时间,但是是Map的格式
val update_time: Timestamp? = null ,
)
我们在setUpDatePickerDialog()修改
datePicker = DatePickerDialog(requireContext(),
{ _, year, month, dayOfMonth ->
val mFormat = "yyyy-MM-dd"
val sdf = SimpleDateFormat(mFormat, Locale.getDefault())
calendar.set(year,month,dayOfMonth)
//直接现在UI先设定时间
binding.edAddInvitationDateTime.setText(sdf.format(calendar.time))
//我们再把现在的时间转换成Long格式的毫秒
val cal = calendar.timeInMillis
//建立Timestamp,把刚刚的Long丢进去
val timeStamp = Timestamp(Date(cal))
selectedDate = timeStamp
},calendar.get(Calendar.YEAR),calendar.get(Calendar.MONTH),calendar.get(Calendar.DAY_OF_MONTH))
这时候印出来的 timeStamp会是Map的格式
Timestamp(seconds=1634196413, nanoseconds=302000000)
虽然我们在Firestore我们会看到的是以下的格式
但是透过binding拿到的资料还是一样是Map的格式喔,所以我们等等会提到怎麽把它改成自定义的格式!
直接在我们的binding.btnAddInvitationFragmentSubmit的Click事件里面的val invitation进行修改
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(),
//透过System.currentTimeMillis来拿到现在的时间,并且传进去给Date的Class得到Date,再传进Timestamp,得到StampTime的格式
update_time = Timestamp(Date(System.currentTimeMillis()))
)
我们要修改我们会看到invitation的地方,总共有三个,一个是在InvitationDetailFragment,另外两个是用在Recyclerview的Layout
我们原本date_time的格式是String,所以我们才可以直接在xml进行绑定,但是现在我们要把Timestamp的格式先改成String,才可以显示在UI,所以我们在kt档用观察的方式写,也记得要把所有的xml绑定的资料都删掉喔!
matchingViewModel.selectedInvitation.observe(viewLifecycleOwner, Observer {
//我们要的格式
val mFormat = "yyyy-MM-dd"
val sdf = SimpleDateFormat(mFormat, Locale.getDefault())
val displayTime = sdf.format(it.date_time?.toDate()?.time)
binding.tvInvitationDetailDateTime.text = displayTime
})
就跟我们从Long的格式转成Timestamp的方式一样
Long转成Timestamp: Long→Date→Timestamp
而Timestamp转成Long则是: Timestamp→Date→Long
这样就可以啦!
我们来到 DashboardViewHolder的bind()来设定,基本上跟刚刚做的事情一样,只是我们换地方写而已
fun bind(item: Invitation){
binding.invitation = item
//一样格式
val format = "yyyy-MM-dd"
val sdf = SimpleDateFormat(format, Locale.getDefault())
val dateTime = sdf.format(item.date_time?.toDate()?.time)
val time = "时间: $dateTime"
binding.tvDashboardInvitationItemListDateTime.text = time
binding.executePendingBindings()
Constant.loadPetImage(item.pet_image,binding.ivDashboardInvitationItemListImage)
}
一样在ViewHolder里面的bind写入
fun bind(item: Invitation){
binding.invitation = item
val mFormat = "yyyy-MM-dd"
val sdf = SimpleDateFormat(mFormat, Locale.getDefault())
val stampToTime = item.date_time?.toDate()?.time
val time = "时间: " + sdf.format(stampToTime)
binding.tvHomeInvitationItemListDateTime.text = time
Constant.loadPetImage(item.pet_image,binding.ivHomeInvitationItemListImage)
binding.executePendingBindings()
}
这时候我们点Run~
就会出现问题,原因则是因为我们之前的时间格式有的用String,有的用Long,所以我们要忍痛到Firestore把invitation的集合删除(哭哭
好的! 那这样就可以打开啦! 对了,资料你要先自己新增喔~
Firestore有提供很简单的方式让我们搜寻
我们这边强迫使用者用单选,我们用radioButton,且一定要选择,不选不能搜寻,layout这边我就不贴罗
我们先来到SearchFragment来新增
//确认我们刚刚设定的rb是否都没有被点跟选择的数量不能超过10(Firestore的限制,不然会报错)
private fun validDataForm(): Boolean{
return when{
!binding.rbResultSortInvitationTime.isChecked && !binding.rbResultSortUpdateTime.isChecked -> {
showSnackBar(resources.getString(R.string.msg_choose_your_result_sort),true)
false
}
areaCheckedList.size >10 -> {
showSnackBar("最多只能选择10个地区",true)
false
}
petTypeCheckedList.size > 10 -> {
showSnackBar("最多只能选择10种宠物",true)
false
}
else -> true
}
}
并把它新增在onClick事件
binding.btnSearchSubmit.setOnClickListener {
getAreaCheckedList()
getPetTypeCheckedList()
if (validDataForm()){
//设定当哪个排序的哪个按钮被按,就把它传给viewmodel
var result_sort: String = ""
if (rb_result_sort_invitation_time.isChecked){
result_sort = Constant.RESULT_SORT_INVITATION_DAY
}else{
result_sort = Constant.RESULT_SORT_UPDATE_DAY
}
matchingViewModel.searchInvitation(accountViewModel.getCurrentUID()!!,areaCheckedList,petTypeCheckedList,result_sort,this)
}
}
所以我们也要在Constant新增
const val RESULT_SORT_UPDATE_DAY = "result_sort_update_day"
const val RESULT_SORT_INVITATION_DAY = "result_sort_invitation_day"
接下来就是来到我们的matchingViewModel修改成,首先要把参数的地方新增搜寻的方式跟传入当前user的ID,因为我们同样不希望搜寻到自己的资料,并且传入排序方式
fun searchInvitation(currentUserId: String,areaList: MutableList<String>,petTypeList: MutableList<String>,result_sort: String,fragment: SearchFragment){}
再过来里面最外层首先会用 if来判断 result_sort的参数是什麽
if (result_sort == Constant.RESULT_SORT_UPDATE_DAY){
...
}else{
...
}
接下来重点是,我们在如果今天排序方式是Constant.RESULT_SORT_UPDATE_DAY的话(已更新日期排序),
我们在whereIn的下面,get()的上面新增
.orderBy(Constant.UPDATE_TIME,Query.Direction.DESCENDING)
第一个参数是要比较的栏位,第二个则是你要递增或递减
asc 递增(由小到大); desc 递减(由大到小)
而我们希望我们看到的顺序是越靠近我们(越近更新的),我们越早看到,所以我们要用递减
先以单选地区的搜寻当例子
areaList.isNotEmpty() && petTypeList.isEmpty() ->{
Firebase.firestore.collection(Constant.INVITATION)
.whereIn(Constant.AREA,areaList)
//新增这一行
.orderBy(Constant.UPDATE_TIME,Query.Direction.DESCENDING)
.get()
.addOnSuccessListener {
val list = mutableListOf<Invitation>()
for (i in it.documents){
val model = i.toObject(Invitation::class.java)
//这边判断是否邀约的userId是否为本人
model?.let {
if (model.user_id != currentUserId){
list.add(it)
}
}
Timber.d("model: $model")
}
_dashboardInvitationList.postValue(list)
fragment.searchInvitationSuccess(list.size)
}
.addOnFailureListener {
Timber.d("搜寻fail $it")
fragment.searchInvitationFail(it.toString())
}
}
而反之如果 result_sort == Constant.RESULT_SORT_INVITATION_DAY,也就是我们要看离我们最近的我们越想看到,太远的东西不用去管它。所以我们就要用递增
新增以下
.orderBy(Constant.DATE_TIME,Query.Direction.ASCENDING)
一样拿只选地区为范例
areaList.isNotEmpty() && petTypeList.isEmpty() ->{
Firebase.firestore.collection(Constant.INVITATION)
.whereIn(Constant.AREA,areaList)
.orderBy(Constant.DATE_TIME,Query.Direction.ASCENDING)
.get()
.addOnSuccessListener {
val list = mutableListOf<Invitation>()
for (i in it.documents){
val model = i.toObject(Invitation::class.java)
model?.let {
if (model.user_id != currentUserId){
list.add(it)
}
}
}
_dashboardInvitationList.postValue(list)
fragment.searchInvitationSuccess(list.size)
}
.addOnFailureListener {
fragment.searchInvitationFail(it.toString())
}
}
接下来我们Run了之後,发现会出现错误!!
description=The query requires an index. You can create it here:一大串网址
它会希望我们去建立复合式索引,我们直接给它提供的链结,就可以创建罗!,如下图
按建立索引,就可以建立了,等它建立完後,我们就可以搜寻啦!
成品如下!!
网页元件中 , 常会使用 Modal 这种类型的元件 如果我们将其制作成一个 <Modal&g...
现在使用智慧型手机比率最高,手机画面很小,所以在制作网页时应注意以下细节 只显示重要的资讯及减少栏位...
Chart function 身为一个键盘柯南,最重要的技能之一就是储存和下载分析後的结果。另外c...
写在前面 still placeholder still placeholder still pla...
什麽是 Service Workers Service Workers 的角色是位於 Web App...