There might be requirement in your application where you want to implement the search functionality by having SearchView widget inside the toolbar / actionBar for filtering the items of recyclerview . In the previous series of tutorial we have seen the basics of a RecyclerView .
In this tutorial we will see the logic for filtering the items in RecyclerView Using filterable interface .
1. Build Gradle
Add the following lines to the dependencies section in your project's build.grade file and sync . It includes design support library , recyclerView library .
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' }
2. Menu
Create menu XML layout inside res/menu/ and add SearchView widget as menu item
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_search" android:icon="@drawable/ic_search_24" android:title="@string/app_name" app:actionViewClass="android.widget.SearchView" app:showAsAction="ifRoom|collapseActionView" /> </menu>
3. XML Layout
file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="10dp" />
Create XML Layout file in res/layout and name it row_item.xml , This layout defines the layout for Items of RecyclerView . Here In this example we will have one TextView .
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/txt" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="8dp" android:paddingTop="16dp" android:paddingRight="8dp" android:paddingBottom="16dp" android:textSize="20sp" />
4. Data
To Load Data Into RecyclerView , we will use Locale class to get full list of country and we will also create emoji using country iso code .
package com.tutorialsbuzz.recyclerviewfilter data class Model(val name: String) {}
5. Adapter ViewHolder Filterable
5.1 Adapter
RecyclerView.Adapter has three abstract methods that we must override .
- onCreateViewHolder() :Inside this method we specify the layout that each item of the RecyclerView should use .onCreateViewHolder has return type of RecyclerView.ViewHolder which represent each row of recyclerView. Using Inflator get the view of above defined row_item and pass it to viewholder constructor and then return.
- onBindViewHolder() : Inside this method data will be displayed at the specified position .
- getItemCount() : Returns the total number of items in the data set held by the adapter.
5.2 ViewHolder
Create Inner class ViewHolder inside CustomAdapter class extend it to RecyclerView.ViewHolder , add bind function which takes above defined model data class object .
5.3 Filterable
Implement Filterable interface to CustomAdapter and override and getFilter() , Inside this method create an instance of filter and override the performFiltering and publishResults function
- performFiltering : This function takes constraint as parameter , list is filtered based on constraint the list is filtered , which is then set to FilterResults and then return FilterResults instance .
- publishResults : This function it takes FilterResults as parameter which is returned from performFiltering function , Inside this we will set filtered list to adapter and call notifyDataSetChanged .
file : CustomAdapter.kt
package com.tutorialsbuzz.recyclerviewfilter import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Filter import android.widget.Filterable import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.row_item.view.* import kotlin.collections.ArrayList class CustomAdapter(var countryList: MutableList<Model>, val context: Context) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable { var countryFilterList = ArrayList<Model>() // exampleListFull . exampleList init { countryFilterList = countryList as ArrayList<Model> } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { (holder as ViewHolder).bind(countryFilterList.get(position)); } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val layoutInflater = LayoutInflater.from(parent.context) return ViewHolder(layoutInflater.inflate(R.layout.row_item, parent, false)) } override fun getItemCount(): Int { return countryFilterList.size } inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(model: Model): Unit { itemView.txt.text = model.name } } override fun getFilter(): Filter { return object : Filter() { override fun performFiltering(constraint: CharSequence?): FilterResults { val charSearch = constraint.toString() if (charSearch.isEmpty()) { countryFilterList = countryList as ArrayList<Model> } else { val resultList = ArrayList<Model>() for (row in countryList) { if (row.name.toLowerCase().contains(constraint.toString().toLowerCase())) { resultList.add(row) } } countryFilterList = resultList } val filterResults = FilterResults() filterResults.values = countryFilterList return filterResults } override fun publishResults(constraint: CharSequence?, results: FilterResults?) { countryFilterList = results?.values as ArrayList<Model> notifyDataSetChanged() } } } }
6. MainActivity
- Create a koltin class MainActivity.kt and extend it to AppCompatActivity class .
- Override onCreate function and set the content of this MainActivity with above defined xml layout (activity_main.xml).
- Create full list of country using Locale class ,this list will be set to recyclerView.
- Set Vertical Linear LayoutManager to RecyclerView.
- Create Instance of Adapter and set to RecyclerView .
SearchView Widget :
- Get the reference of menu item and call getView and then type cast it androidx.appcompat.widget.SearchView.
- On SearchView instance call setOnQueryTextListener and override onQueryTextSubmit and onQueryTextChange.
- Inside the onQueryTextChange function get the instance of filter using adapter and call filter function by passing query string .
file : MainActivity.kt
package com.tutorialsbuzz.recyclerviewfilter import android.os.Bundle import android.util.Log import android.view.Menu import android.view.MenuItem import android.widget.SearchView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.activity_main.* import java.util.* class MainActivity : AppCompatActivity() { var adapter: CustomAdapter? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) adapter = CustomAdapter(getCountries(), this) recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false) recyclerView.addItemDecoration(SimpleDividerItemDecoration(this)) recyclerView.adapter = adapter; } override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.menu_main, menu) val item = menu?.findItem(R.id.action_search); val searchView = item?.actionView as SearchView searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(p0: String?): Boolean { return true } override fun onQueryTextChange(query: String?): Boolean { Log.d("onQueryTextChange", "query: " + query) adapter?.filter?.filter(query) return true } }) item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { override fun onMenuItemActionCollapse(p0: MenuItem?): Boolean { adapter?.filter?.filter("") showToast("Action Collapse") return true } override fun onMenuItemActionExpand(p0: MenuItem?): Boolean { showToast("Action Expand") return true } }) return super.onCreateOptionsMenu(menu) } private fun getCountries(): MutableList<Model> { val mdList = mutableListOf<Model>() for (countryISO in Locale.getISOCountries()) { val locale = Locale("", countryISO) if (!locale.displayCountry.isEmpty()) { mdList.add(Model(locale.displayCountry + " " + counrtyFlag(countryISO))) } } return mdList } private fun counrtyFlag(countryCode: String): String { val flagOffset = 0x1F1E6 val asciiOffset = 0x41 val firstChar = Character.codePointAt(countryCode, 0) - asciiOffset + flagOffset val secondChar = Character.codePointAt(countryCode, 1) - asciiOffset + flagOffset val flag = (String(Character.toChars(firstChar)) + String(Character.toChars(secondChar))) return flag } private fun showToast(msg: String) { Toast.makeText(this@MainActivity, msg, Toast.LENGTH_SHORT).show() } }
2 comments:
thank you Pavan Ji,
For This Tutorial, it cleared my two days of Struggle
Good
Post a Comment