iT邦幫忙

2021 iThome 鐵人賽

DAY 15
0
Mobile Development

30天建立寵物約散App-Android新手篇系列 第 15

【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


上一篇
【day14】 Fab X 跳頁Animation
下一篇
【day16】Realtime Database
系列文
30天建立寵物約散App-Android新手篇30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言