【day15】DashboardFragment X Firestore搜寻

今天要来带大家看一下搜寻资料,Firestore最简单的方式就是直接透过get()来拿到资料,但是如果我们今天需要增加一些筛选条件呢? 如我们要选取地区,我们总不希望我们看到的资料都是离我们很远的地方吧!! Let go~

1.layout

这边layout就不贴罗,因为贴了的话太占版面了,我怕碍了大家的眼睛!
LinearLayout里面会塞checkBox,然後我们的间距跟string内容这次就不贴罗!

day15.layou

并且我们要新增checkBox的background,让它被点击後是有不同样式的
新增後一样到 checkBox的background来指定!

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_checked="true">
        <shape android:shape="rectangle">
            <corners android:radius="10dp"/>
            <gradient android:startColor="#84C1FF"
                android:endColor="#4EFEB3"/>

        </shape>

    </item>

    <item android:state_checked="false">
        <shape android:shape="rectangle">
            <corners android:radius="10dp"/>
            <gradient android:startColor="@color/light_pewter_blue"
                android:endColor="@color/pewter_blue"/>

        </shape>

    </item>
</selector>

2.拿到checkBox的资料,并加入到list

首先你要去xml,把每一个checkBox新增id,并且把它加入到list,而这个list里面的type会是checkBox,所以我们可以透过for回圈来判断哪一些checkBox有被点击。

来到SearchFragment来新增以下的funtion

//我们在class新增
private lateinit var areaCheckedList: MutableList<String>

private fun getAreaCheckedList(){
		//list里面放所有的checkBox的id
        val list = listOf<CheckBox>(binding.cbCityKeelung,binding.cbCityTaipei,binding.cbCityNewTaipei,binding.cbCityTaoyuan,binding.cbCityXinzhu,binding.cbCityXinzhuCounty
        ,binding.cbCityMiaoli,binding.cbCityMiaoliCounty,binding.cbCityTaichung,binding.cbCityZhanghua,binding.cbCityZhanghuaCounty,binding.cbCityNantou,binding.cbCityNantouCounty
        ,binding.cbCityYunlinCounty,binding.cbCityJiayi,binding.cbCityJaiyiCounty,binding.cbCityTainan,binding.cbCityGaoxiong,binding.cbCityPingtung,binding.cbCityPingtungCounty
        ,binding.cbCityYilan,binding.cbCityYilanCounty,binding.cbCityHualien,binding.cbCityHualienCounty,binding.cbCityTaidong,binding.cbCityTaidongCounty,binding.cbCityPenghuCounty
        ,binding.cbCityLudao,binding.cbCityLanyu,binding.cbCityJinmen,binding.cbCityMatus,binding.cbCityLianjiang)
	      
		//建立一个空的list,该list宣告在class下方,让我们可以等等直接传给viewModel 
		areaCheckedList = mutableListOf<String>()
        for (i in list){
            if (i.isChecked){
				//透过i.text.toString()来拿到我们在xml里面设定的text
                areaCheckedList.add(i.text.toString())
            }
        }
    }

petType的方式也是同理

private lateinit var petTypeCheckedList: MutableList<String>
private fun getPetTypeCheckedList(){
        val list = listOf<CheckBox>(binding.cbPetTypeDog,binding.cbPetTypeCat,binding.cbPetTypeRabbit,binding.cbPetTypeBird,binding.cbPetTypePig,binding.cbPetTypeFish,binding.cbPetTypeOther)
        petTypeCheckedList = mutableListOf<String>()
        for (i in list){
            if (i.isChecked){
                petTypeCheckedList.add(i.text.toString())
            }
        }
    }

3.把资料传到我们的viewModel

binding.btnSearchSubmit.setOnClickListener {
               getAreaCheckedList()
               getPetTypeCheckedList()
               matchingViewModel.searchInvitation(areaCheckedList,petTypeCheckedList,this)
           }

4.viewModel来进行搜寻

我们透过when来判断我们的使用者是否有点击checkBox



//livedata,我们分两个livedata,这个是搜寻过後的livedata
private val _dashboardInvitationList = MutableLiveData<List<Invitation>>()
 val dashboardInvitationList: LiveData<List<Invitation>>
 get() = _dashboardInvitationList



fun searchInvitation(areaList: MutableList<String>,petTypeList: MutableList<String>,fragment: SearchFragment){

when{

//如果地区的地方有点击但是种类没点击
areaList.isNotEmpty()&& petTypeList.isEmpty()->{
        Firebase.firestore.collection(Constant.INVITATION)
        //透过whereIn来塞入list,来比较资料
        .whereIn(Constant.AREA,areaList)
        .get()
        .addOnSuccessListener{
                    val list =mutableListOf<Invitation>()
                    for(i init.documents){
                            val model = i.toObject(Invitation::class.java)
                            model?.let{
                                list.add(it)
                                    }
                                             }
                        _dashboardInvitationList.postValue(list)
                        fragment.searchInvitationSuccess(list.size)
                    }
        .addOnFailureListener{
                    fragment.searchInvitationFail(it.toString())
                                        }
                        }

//地区没被点,但是宠物有被点
            areaList.isEmpty()&& petTypeList.isNotEmpty()->{
Firebase.firestore.collection(Constant.INVITATION)
            .whereIn(Constant.PET_TYPE,petTypeList)
            .get()
            .addOnSuccessListener{
                    val list =mutableListOf<Invitation>()
                    for(i init.documents){
                        val model = i.toObject(Invitation::class.java)
                        model?.let{
                                list.add(model)
                                                     }
                                            }
                _dashboardInvitationList.postValue(list)
                fragment.searchInvitationSuccess(list.size)
            }
            .addOnFailureListener{
                    fragment.searchInvitationFail(it.toString())
                    }
            }

//如果地区的地方跟种类都有点击
areaList.isNotEmpty()&& petTypeList.isNotEmpty()->{
        val ref = Firebase.firestore.collection(Constant.INVITATION)
        //先搜寻地区,地区符合才进行下一步筛选
        ref.whereIn(Constant.AREA,areaList)
            .get()
            .addOnSuccessListener{
                    val list =mutableListOf<Invitation>()
                    for(eachModel init.documents){
                    val model = eachModel.toObject(Invitation::class.java)
                    //再用for回圈来得到地区符合的这些结果里面,又符合宠物种类的
                    for(type in petTypeList){
                            model?.let{
                                    if(model.pet_type == type){
                                    list.add(model)
                                                    }
                                        }
                        }
                    }
                    _dashboardInvitationList.postValue(list)
                    fragment.searchInvitationSuccess(list.size)
                }
           .addOnFailureListener{
                    fragment.searchInvitationFail(it.toString())
                            }
                }

        //需要加入else,checkBox全部都没有被点击
else ->{
        Firebase.firestore.collection(Constant.INVITATION)
            .get()
            .addOnSuccessListener{
            val list =mutableListOf<Invitation>()
                    for(i init.documents){
                    val model = i.toObject(Invitation::class.java)
                    model?.let{
                            list.add(model)
                            }
                }
                _dashboardInvitationList.postValue(list)
                fragment.searchInvitationSuccess(list.size)
            }
            .addOnFailureListener{
            fragment.searchInvitationFail(it.toString())
            }
        }


    }

}

★我们这边要注意,Firestore搜寻有几个限制

  • 每个查询最多只能使用一个 in、not-in 或 array-contains-any 子句。不能在同一查询中组合这些运算符。
  • in、not-in 和 array-contains-any 最多支持 10 个比较值。

这也是为什麽我们要area区域跟pet_type区域都有点击的时候,用for回圈来新增list。
有兴趣的小夥伴们可以参考:https://firebase.google.com/docs/firestore/query-data/queries?hl=zh-cn

并且回到searchFragment来新增以下的funtion

fun searchInvitationSuccess(resultSize: Int){
//我们从viewModel传回资料,如果回传的资料是0,我们就说没有找到资料,如果有则跳回 DashboardFragment
  if (resultSize != 0 && findNavController().currentDestination?.id == R.id.searchFragment){
            findNavController().navigate(R.id.action_searchFragment_to_navigation_dashboard)
        }else{
            showSnackBar("没有找到符合的资料",false)
        }
    }

    fun searchInvitationFail(e: String){
        showSnackBar(e,true)
    }

5.修改DashboardFragment的观察

因为我们的getAllInvitation(),写在我们的onCreateView,所以当我们的SearchFragment回来的时候,他又会再叫一次getAllInvitation,这样就有点资料又会被覆盖上去,所以这也是为什麽我们需要把searchInvitation的结果放在另一个livedata

改写一下观察


//这个资料是我们透过search得到的资料
matchingViewModel.dashboardInvitationList.observe(viewLifecycleOwner, Observer {
      dashboardAdapter.submitList(it)
    })


//这个则是一开始得到的资料,我们让它观测,如果改变後,并且dashboardInvitationList是null的话,才把资料塞到adapter
    matchingViewModel.allInvitationList.observe(viewLifecycleOwner, Observer {
      if (matchingViewModel.dashboardInvitationList.value == null){
        dashboardAdapter.submitList(it)
      }else{
        Timber.d("dashboardInvitation里面有资料啦 不用更新")
      }
    })

这样就大功告成啦!

day15.finish


<<:  Day18

>>:  Day 15: 范式概述、结构化设计 (待改进中... )

【30天Lua重拾笔记28】进阶议题: Meta Programming

同步发表於个人网站 Meta Programming / 元程序设计 元程序设计(英语:Metap...

[第二十七只羊] 迷雾森林舞会XVI 整理客厅,首页列表介面

天亮了 昨晚是平安夜 关於迷雾森林故事 悍跳 兔兔就这样使出吃奶的力气让大家停下舞步 兔兔暴怒地大喊...

JavaScript 闭包(Closure) 上集

闭包 内部函数总是可以访问其所在的外部函数中声明的参数和变数,即使外部函式已经结束执行了。 看看这个...

[机派X] Day 1 - 纯聊天

引言 不好意思,作者总是有说不完的序言! 「机派X」的由来源自於无人机的机、树莓派的派还有 Linu...

见习村30 - A Chain adding function

30 - A Chain adding function Don't say so much, ju...