Android Filterable實現Recyclerview篩選功能的示例代碼

原先碰到篩選這種功能時,後端的接口都會讓上傳一個字段,根據字段來返回相應的數據。後來一次和別人對接時,接口直接返回全部數據,而且還要實現篩選功能。我…我說不就是一條sql語句的事,改接口多方便,我苦心勸導,然後被懟回來,切,不就是篩選嘛,求人不如自己搞。

1. 效果圖

2. 思路

既然是篩選,那就少不瞭比較。也沒有什麼好的辦法,無非就是循環對比,然後將適配器進行數據更新。頁面刷新即可。但篩選的調用要方便,怎麼比較才方便我們調用呢?偶然間看到瞭Filterable,使Adapter繼承自該接口,實現getFilter()方法,在該方法裡實現具體的過濾邏輯即可。

3. 實現步驟

3.1 數據Bean類

class MyBean(var type:String,var name:String,var deliverType:String)

這裡我們簡單的創建個數據Bean類,後面我們的篩選字段是根據type和deliverType來進行篩選。

3.2 創建適配器

class MyAdapter(data: MutableList<MyBean>) :
 RecyclerView.Adapter<MyAdapter.MyViewHolder>(), Filterable {

 //存放原數據
 private var mSourceList = mutableListOf<MyBean>()

 //存放過濾後的數據
 private var mFilterList = mutableListOf<MyBean>()

 init {
  mSourceList = data
 }

 override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
  holder.name.text = mFilterList[position].name
  holder.deliverType.text = mFilterList[position].deliverType
 }

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
  var view =
   LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
  return MyViewHolder(view)
 }

 /**
  * 註意:這裡返回過濾後的集合大小
  */
 override fun getItemCount(): Int {
  return mFilterList.size
 }

 inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
  //商品名稱
  var name: TextView = itemView.findViewById(R.id.tvName)
  //配送方式
  var deliverType: TextView = itemView.findViewById(R.id.tvDeliverType)
 }
}

和我們平時創建的Adapter沒什麼兩樣。但要註意以下幾點

1、這裡我們創建瞭兩個集合mSourceList和mFilterList,mFilterList主要是用來存放過濾後的數據,而mSourceList主要是用來在篩選後數據恢復時使用,使得不用再去請求一次數據。

2、getItemCount()方法返回過濾後的集合的大小。

有個疑問:
假如我們沒有進行過濾,而因為我們的mFilterList默認為空,且getItemCount()返回的是它的大小0,那我們默認是不是就顯示不出數據?

3.3 繼承Filterable接口

1、繼承Filterable接口後,實現其getFilter()方法,該方法需要我們返回一個Filter過濾器對象。

2、我們重寫Filter的performFiltering()方法和publishResults()方法,performFiltering()用來實現我們具體過濾的邏輯操作,publishResults()用來將我們過濾後的數據進行更新。

3、因為performFiltering()傳來的過濾條件是一段字符串,而我們的過濾條件有兩個,所以我們將過濾的條件轉化為Json對象傳過來,那樣就可以得到多個過濾條件的字符串瞭。

4、這裡我們的具體過濾操作是使用Collection的filter()方法進行過濾

(1)當condition1和condition2為空時,返回原數據mSourceList

(2)否則使用filter()方法按條件進行過濾,最後將過濾後的集合賦值給FilterResults()對象的value字段,並將其返回

5、publishResults(charSequence: CharSequence,filterResults: FilterResults)方法中filterResults對象內的value字段是我們performFiltering()方法返回的過濾後的集合,在這裡我們將RecyclerView進行更新。

具體實現見以下代碼:

class MyAdapter(data: MutableList<MyBean>) :
 RecyclerView.Adapter<MyAdapter.MyViewHolder>(), Filterable {
  /**
  * 具體的執行過濾的操作
  * 創建適配器後會默認的執行一次
  */
 override fun getFilter(): Filter {
  return object : Filter() {
   //執行過濾操作
   override fun performFiltering(charSequence: CharSequence): FilterResults {
    val charString = charSequence.toString()
    Log.i(TAG, "performFiltering: 執行過濾操作,過濾字段為:$charString")

    val jsonObject = JSONObject(charString)
    //篩選條件一
    var condition1 = jsonObject.getString("condition1")
    //篩選條件二
    var condition2 = jsonObject.getString("condition2")

    //存放已過濾的數據
    var theFilterList = if (condition1.isEmpty() && condition2.isEmpty()) {
     //沒有過濾的內容,則使用源數據
     mSourceList
    } else if (condition2.isEmpty()) {
     mSourceList.filter { it.type == condition1 }
    } else if (condition1.isEmpty()) {
     mSourceList.filter { it.deliverType == condition2 }
    } else {
     mSourceList.filter { it.type == condition1 && it.deliverType == condition2 }
    }
    val filterResults = FilterResults()
    filterResults.values = theFilterList
    return filterResults
   }

   //把過濾後的值返回出來並進行更新
   override fun publishResults(
    charSequence: CharSequence,
    filterResults: FilterResults
   ) {
    mFilterList = filterResults.values as MutableList<MyBean>
    notifyDataSetChanged()
   }
  }
 }
}

3.4 過濾調用

class MainActivity : AppCompatActivity() {
 
 //過濾條件1
 var condition1 = ""
 //過濾條件2
 var condition2 = ""
 //總的過濾條件
 var jsonObject = JSONObject()
 private var dataList = mutableListOf<MyBean>()
 var myAdapter = MyAdapter(dataList)
 
 override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)
  
  mRecyclerView.layoutManager = LinearLayoutManager(this)
  mRecyclerView.adapter = myAdapter

  jsonObject.put("condition1","過濾條件一")
  jsonObject.put("condition2","過濾條件二")
  myAdapter.filter.filter(jsonObject.toString())  
 }
}

如果想恢復數據不篩選,直接將jsonObject對象內的condition1和condition2字段設為空,然後調用myAdapter.filter.filter(jsonObject.toString())即可。

具體見代碼

4. 優化

其實我們getFilter()內的過濾操作還可以優化下

var theFilterList = if (condition1.isEmpty() && condition2.isEmpty()) {
 //沒有過濾的內容,則使用源數據
 mSourceList
} else if (condition2.isEmpty()) {
 mSourceList.filter { it.type == condition1 }
} else if (condition1.isEmpty()) {
 mSourceList.filter { it.deliverType == condition2 }
} else {
 mSourceList.filter { it.type == condition1 && it.deliverType == condition2 }
}

可以看到else{}下是當condition1和condition2都不為空的情況下進行的篩選,但是如果我們使用下拉框進行篩選時,選擇第一個條件condition1後就已經進行瞭一次篩選,即condition1不為空condition2為空,在 else if (condition2.isEmpty()){} 裡對源數據進行瞭篩選;再選擇第二個條件時,又進行瞭一次篩選,即condition1不為空condition2不為空,在else{}裡又是對源數據進行篩選,其實我們應該是在第一次的結果下進行篩選是最優的辦法。

想法很好,但實現起來困難挺多,在兩個條件都不為空時,我們需要判斷第一次刪選下來的數據是以哪個篩選條件為依據的,在兩個條件都不為空篩選後,再次更改其中一個篩選條件,我們需要先將另外一個篩選條件下的數據給篩選出來,越來越麻煩,暫時不考慮瞭,有好的方案的麻煩給個思路。

5. 註意

因為Adapter默認返回的大小是篩選後的尺寸,而我們默認是沒有篩選的,導致上來會沒有數據,所以我們需要設置適配器後,人為的調用一下篩選才好:myAdapter.filter.filter(jsonObject.toString())。而我在項目中沒有寫因為AppCompatSpinner會默認的選擇第0項,我在其onItemSelected()回調裡調用瞭篩選功能。

6. 總結

總的來說並不難,還是更新數據更新佈局的那一套,不同的是用瞭Filterable接口實現,使得篩選調用的方式更簡單。但是這種實現更多的是適用於數據量小或者固定的數據,如果數據量大,或者數據會一直上拉加載擴充,使用這種方式隻會讓效率隨著數據量的增大而越來越低,顯然不合適,下次後端還強硬不改,那就隻能開懟瞭。
Github項目地址 https://github.com/myfittinglife/RecyclerViewFilterable

7. 參考文章

集合過濾操作
Google文檔

到此這篇關於Android Filterable實現Recyclerview篩選功能的示例代碼的文章就介紹到這瞭,更多相關Android Recyclerview篩選內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!