Android SearchView in Room Database - GeeksforGeeks

18 Oct, 2021
SearchView in databases is a pretty basic functionality that you can see in most of the applications in your day-to-day life, then why not, you should learn and implement it. it is not too much complicated to implement in your application. There are a lot of easier ways that are how you can implement it. The tasks that you should use to implement for implementing searchView in-app are:-
You need to create a menu item setting actionViewClass to SearchView and set other action view attributesThen inflate the menu in the onCreateOptionsMenu() method of the action where you want the search feature.Then get the SearchView object from the menu and add SearchView.OnQueryTextListener to it by calling setOnQueryTextListener method.SearchView.OnQueryTextListener has two callback methods onQueryTextSubmit and onQueryTextChangeMethod onQueryTextSubmit gets called when the user submits a search by hitting enter button or clicking submit button on the search widget.
You need to create a menu item setting actionViewClass to SearchView and set other action view attributes
Then inflate the menu in the onCreateOptionsMenu() method of the action where you want the search feature.
Then get the SearchView object from the menu and add SearchView.OnQueryTextListener to it by calling setOnQueryTextListener method.
SearchView.OnQueryTextListener has two callback methods onQueryTextSubmit and onQueryTextChange
Method onQueryTextSubmit gets called when the user submits a search by hitting enter button or clicking submit button on the search widget.
In this method, database search can be performed using the text entered into the search view widget. You can enable the search button in the search view widget by calling the setSubmitButtonEnabled method and passing the boolean value of true. it’s a basic idea that how it works. But we are going to discuss the most efficient way for searching data from the room database, for that, you should know about some concepts like:
Prerequisites: 
How to show data in recyclerView using room DB (what is adapter )
What is ROOM Database (library)
Familiar with Model–View–ViewModel architecture (MVVM)
The most important one is dependency injection (dagger hilt)
We are not going to create an app from scratch apart from that, we are providing the source code for initial things like 
Updating data in RecyclerView by database
Using dependency injection
and created some extra classes as well
We are giving you a quick overview of the project, then you will understand it very easily. Download the project by Click Here.
Step 1: Download the project from GitHub and Add it to your android studio
We hope you all know that how to import existing projects to the android studio if don’t then go to file > open > select downloaded project > then wait... for finish project build
Step 2: build.Gradle files
Navigate to the app > Gradle Scripts > build.Gradle file and add the below dependencies in the dependencies section.
We have added some dependencies and plugins
and enable view binding
plugins {
   id 'com.android.application'
   id 'kotlin-android'
   id 'kotlin-kapt'     // dagger hilt plugin 
   id 'dagger.hilt.android.plugin'
}

android {

   compileSdkVersion 30
   buildToolsVersion "30.0.2"
   defaultConfig {
       applicationId "com.example.searchViewInRoom"
       minSdkVersion 21
       targetSdkVersion 30
       versionCode 1
       versionName "1.0"
       testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }

   // here we have enabled viewBinding
   buildFeatures {
       viewBinding true
   }

   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }

   compileOptions {
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
   }

   kotlinOptions {
       jvmTarget = '1.8'
   }

}

dependencies {
   implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
   implementation 'androidx.core:core-ktx:1.3.2'
   implementation 'androidx.appcompat:appcompat:1.2.0'
   implementation 'com.google.android.material:material:1.2.1'
   implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
   testImplementation 'junit:junit:4.13.1'
   androidTestImplementation 'androidx.test.ext:junit:1.1.2'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

   // Navigation Component
   implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'

   // Room components
   implementation "androidx.room:room-runtime:2.2.6"

   kapt "androidx.room:room-compiler:2.2.6"
   implementation "androidx.room:room-ktx:2.2.6"

   androidTestImplementation "androidx.room:room-testing:2.2.6"

   // Dagger - Hilt
   implementation "com.google.dagger:hilt-android:2.28.3-alpha"

   kapt "com.google.dagger:hilt-android-compiler:2.28.3-alpha"
   implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02"
   kapt "androidx.hilt:hilt-compiler:1.0.0-alpha02"

   // Lifecycle
   implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
   implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
   implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
}
Step 3: Creating Entity/Table
Go to app > main > searchViewInRoom > data > person.kt. This is how we can create an entity/table in SQLite database using room
Kotlin
package com.example.searchViewInRoom.data  import androidx.room.Entityimport androidx.room.PrimaryKey  // it's our data class that we have// annotate @Entity to create it as table@Entity(tableName = "person_table")data class Person(    // table is basically       // having 3 fields as follow....    var firstName: String,    var lastName: String,    var age: Int){    @PrimaryKey(autoGenerate = true)    // we make id as primary       // key and it will autogenerate    var id: Int = 0}
Step 4: Create a database 
Go to app > main > searchViewInRoom>data > personDatabase.kt. This is how you can create a database. Please read the comments for a better understanding.
Kotlin
package com.example.searchViewInRoom.data  import androidx.room.Databaseimport androidx.room.RoomDatabase// it's our database and here we // specify entities, our version and exportSchema@Database(    entities = [Person::class],    version = 1,    exportSchema = false)// in our database we have just extended RoomDatabase classabstract class PersonDatabase: RoomDatabase() {    // as you can see it is our abstract fun and     // it represent our Data Access Object (dao)    abstract fun personDao(): PersonDao}
Step 5: Database module class
Go to app > main > searchViewInRoom > di > DatabaseModule.kt. Here we use dependency injection
Kotlin
package com.example.searchViewInRoom.di  import android.content.Contextimport androidx.room.Roomimport com.example.searchViewInRoom.data.PersonDatabaseimport dagger.Moduleimport dagger.Providesimport dagger.hilt.InstallInimport dagger.hilt.android.components.ApplicationComponentimport dagger.hilt.android.qualifiers.ApplicationContextimport javax.inject.Singleton  // this is a very important step actually// here we used dependency injection library dagger hilt// it actually provide our database object and dao  @Module@InstallIn(ApplicationComponent::class)object DatabaseModule {      @Singleton    @Provides    fun provideDatabase(        @ApplicationContext context: Context    ) = Room.databaseBuilder(        context,        PersonDatabase::class.java,        "person_database"    ).build()      @Singleton    @Provides    fun provideDao(database: PersonDatabase) = database.personDao()  }
Step 6: Dao (Data Access Object) Class
Go to app > main > searchViewInRoom>data > personDao.kt
Kotlin
package com.example.searchViewInRoom.data  import androidx.room.Daoimport androidx.room.Insertimport androidx.room.OnConflictStrategyimport androidx.room.Queryimport kotlinx.coroutines.flow.Flow  // here we have our Dao having three// different queries as follows.....@Daointerface PersonDao {      // it's basically for reading our database    @Query("SELECT * FROM person_table ORDER BY id ASC")    fun readData(): Flow<List<Person>>      // it's for inserting person object to our database    @Insert(onConflict = OnConflictStrategy.REPLACE)    suspend fun insertData(person: Person)      // it's very important one for this article because    //it's a query for searching our database    /*    so here we have written basic sql query for searching our database    basically will search from our person table or entity    where our first name and last name contains some characters from our searchquery     */    @Query("SELECT * FROM person_table WHERE firstName LIKE :searchQuery OR lastName LIKE :searchQuery")    // and then search query will be passed through     // the perimeter of this function    // and then function will return the flow of list of person    fun searchDatabase(searchQuery: String): Flow<List<Person>>  }
Step 7: Creating a Repository Class
Go to app > main > searchViewInRoom > data > Repository.kt
Kotlin
package com.example.searchViewInRoom.data  import kotlinx.coroutines.flow.Flowimport javax.inject.Inject// it's repository  /*here we have basically injected our person Dao for our dao interfaceand having three function as well........ */class Repository @Inject constructor(    private val personDao: PersonDao) {      fun readData(): Flow<List<Person>> {        return personDao.readData()    }      suspend fun insertData(person: Person) {        personDao.insertData(person)    }      fun searchDatabase(searchQuery: String): Flow<List<Person>> {        return personDao.searchDatabase(searchQuery)    }  }
Step 8: MainViewModel
Go to app > main > searchViewInRoom>viewmodel > MainViewModel.kt. We are going to use this module to receive and observe data from the database.
Kotlin
package com.example.searchViewInRoom.viewmodel  import androidx.hilt.lifecycle.ViewModelInjectimport androidx.lifecycle.LiveDataimport androidx.lifecycle.ViewModelimport androidx.lifecycle.asLiveDataimport androidx.lifecycle.viewModelScopeimport com.example.searchViewInRoom.data.Personimport com.example.searchViewInRoom.data.Repositoryimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.launch  // here we have our viewModel  // and we are going to use it for receiving// and observing data from our databaseclass MainViewModel @ViewModelInject constructor(    private val repository: Repository) : ViewModel() {      val readData = repository.readData().asLiveData()      fun insertData(person: Person){        viewModelScope.launch(Dispatchers.IO) {            repository.insertData(person)        }    }      fun searchDatabase(searchQuery: String): LiveData<List<Person>> {        return repository.searchDatabase(searchQuery).asLiveData()    }  }
These all the classes mentioned above are already in the project that you have downloaded, and it’s a quick overview. Now let’s move to the XML part of the given project ...
activity_main.xml file 
Now we have to navigate to app > res > activity_main.xml and Understand the below code.
XML
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout     xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity">        <!--recycler view for displaying all data -->    <androidx.recyclerview.widget.RecyclerView        android:id="@+id/recyclerView"        android:layout_width="match_parent"        android:layout_height="match_parent"        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintHorizontal_bias="0.0"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toTopOf="parent"        app:layout_constraintVertical_bias="0.0"        tools:listitem="@layout/row_layout">      </androidx.recyclerview.widget.RecyclerView>        <!--fab for adding dummy data -->    <com.google.android.material.floatingactionbutton.FloatingActionButton        android:id="@+id/floatingActionButton"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:clickable="true"        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintHorizontal_bias="0.954"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toTopOf="parent"        app:layout_constraintVertical_bias="0.976"        android:backgroundTint="#098043"        app:srcCompat="@android:drawable/ic_input_add"        tools:ignore="SpeakableTextPresentCheck,ImageContrastCheck"        android:focusable="true" />  </androidx.constraintlayout.widget.ConstraintLayout>
row_layout.xml file
It’s an item view, that we have to add in RecyclerView
XML
<?xml version="1.0" encoding="utf-8"?><androidx.cardview.widget.CardView    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="wrap_content"    app:cardCornerRadius="5dp"    android:padding="5dp"    android:layout_margin="5dp"    app:cardBackgroundColor="#B9F6CA">  <RelativeLayout    android:layout_width="wrap_content"    android:layout_height="wrap_content">      <TextView        android:id="@+id/firstName_textView"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginStart="10dp"        android:layout_marginTop="10dp"        android:layout_marginEnd="10dp"          android:layout_marginBottom="10dp"        android:fontFamily="@font/jockey_one"        android:text="krish"        android:textColor="@color/black"        android:textSize="24sp" />      <TextView        android:id="@+id/lastName_textView"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="10dp"        android:fontFamily="@font/jockey_one"        android:layout_marginBottom="10dp"        android:layout_toEndOf="@+id/firstName_textView"        android:text="moris"        android:textColor="@color/black"        android:textSize="24sp" />      <TextView        android:id="@+id/age_textView"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentEnd="true"        android:layout_marginTop="10dp"        android:layout_marginRight="30dp"        android:text="20"        android:textColor="@color/black"        android:textSize="24sp"        android:textStyle="bold" />  </RelativeLayout></androidx.cardview.widget.CardView>
that’s it for the whole project that I have given to you, After that your app should look like this ......
But if the given list is shown then well and good otherwise don’t worry, we will manage it. now let’s start the real part of the article (implementation of SearchView). There are only three steps to implement your search functionality, which are as follows...
add search icon anywhere on the screen
override onCreateOptionsMenu() method
then implement onQueryTextListner in our mainActivity
that’s it. so let’s do them.
Step 1: Add search icon anywhere on the screen
We are adding it at the top right corner of the app. For that, you have to create a menu item, by resource manager > menu > add menu resource file
XML
<?xml version="1.0" encoding="utf-8"?><menu xmlns:tools="http://schemas.android.com/tools"    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto">      <item android:id="@+id/menu_search"        android:title="@string/search"        android:icon="@drawable/ic_search"        android:iconTint="@color/white"        app:showAsAction="ifRoom"        app:actionViewClass="androidx.appcompat.widget.SearchView"/>  </menu>
Step 2: override onCreateOptionsMenu() method
Go to MainActivity.kt. It has some code already, but read the comments, I mentioned there and you can easily understand it as well.
Kotlin
package com.example.searchViewInRoom  import androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport android.view.Menuimport android.widget.Toastimport androidx.activity.viewModelsimport androidx.appcompat.widget.SearchViewimport androidx.recyclerview.widget.LinearLayoutManagerimport com.example.searchViewInRoom.adapter.MyAdapterimport com.example.searchViewInRoom.data.Personimport com.example.searchViewInRoom.databinding.ActivityMainBindingimport com.example.searchViewInRoom.viewmodel.MainViewModelimport dagger.hilt.android.AndroidEntryPoint  @AndroidEntryPointclass MainActivity : AppCompatActivity(){      // as you can see this is my main activity and    // here we have created view binding for binding our views    private lateinit var binding: ActivityMainBinding      // here we have initialized our mainViewModel    private val mainViewModel: MainViewModel by viewModels()          // and recycler view adapter    private val myAdapter: MyAdapter by lazy { MyAdapter() }      override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        binding = ActivityMainBinding.inflate(layoutInflater)        setContentView(binding.root)          // here we are using linear layout manager for our recyclerView        binding.recyclerView.layoutManager = LinearLayoutManager(this)                  // here we are setting my recyclerView         // to custom adapter that we have already made        binding.recyclerView.adapter = myAdapter          // here we are observing data by mainViewModel         // using readData variable        mainViewModel.readData.observe(this, {            // using custom recyclerView adapter we have             // set the data to our recycler view            myAdapter.setData(it)        })      }  }
After understanding the above code, just get the override onCreateOptionsMenu()
for getting just press ctrl+o and search this function then press enter
we have to override this function so that we can add our menu here as shown below.
Kotlin
override fun onCreateOptionsMenu(menu: Menu?): Boolean {    menuInflater.inflate(R.menu.main_menu, menu)     val search = menu?.findItem(R.id.menu_search)    val searchView = search?.actionView as? SearchView    searchView?.isSubmitButtonEnabled = true      // here you get error but don't worry    searchView?.setOnQueryTextListener(this)     return true}
After step 2 your main activity will look like this...
Kotlin
package com.example.searchViewInRoom  import androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport android.view.Menuimport android.widget.Toastimport androidx.activity.viewModelsimport androidx.appcompat.widget.SearchViewimport androidx.recyclerview.widget.LinearLayoutManagerimport com.example.searchViewInRoom.adapter.MyAdapterimport com.example.searchViewInRoom.data.Personimport com.example.searchViewInRoom.databinding.ActivityMainBindingimport com.example.searchViewInRoom.viewmodel.MainViewModelimport dagger.hilt.android.AndroidEntryPoint  @AndroidEntryPointclass MainActivity : AppCompatActivity(), SearchView.OnQueryTextListener {      // as you can see this is my main activity and    // here we have created view binding for binding our views    private lateinit var binding: ActivityMainBinding      // here we have initialized our mainViewModel    private val mainViewModel: MainViewModel by viewModels()          // and recycler view adapter    private val myAdapter: MyAdapter by lazy { MyAdapter() }      override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        binding = ActivityMainBinding.inflate(layoutInflater)        setContentView(binding.root)          // here we are using linear layout manager for our recyclerView        binding.recyclerView.layoutManager = LinearLayoutManager(this)                  // here we are setting my recyclerView to         // custom adapter that i have already made        binding.recyclerView.adapter = myAdapter          // here we are observing data by mainViewModel         // using readData variable        mainViewModel.readData.observe(this, {            // using custom recyclerView adapter             // we have set the data to our recycler view            myAdapter.setData(it)        })      }    override fun onCreateOptionsMenu(menu: Menu?): Boolean {        menuInflater.inflate(R.menu.main_menu, menu)          val search = menu?.findItem(R.id.menu_search)        val searchView = search?.actionView as? SearchView        searchView?.isSubmitButtonEnabled = true        searchView?.setOnQueryTextListener(this)        return true    }
Step 3: Implement onQueryTextListner in MainActivity.kt
To implement your setOnQueryTextListener in main activity for pass ‘this’ as perimeter
for implementation just go above and add, SearchView.OnQueryTextListener after class MainActivity : AppCompatActivity() like.... class MainActivity : AppCompatActivity(), SearchView.OnQueryTextListener
after another error will show, just move the cursor on error and press alt+Enter
then you will have two functions named onQueryTextChange() , onQueryTextChange()
then just implement these two functions as
Kotlin
override fun onQueryTextSubmit(query: String?): Boolean {       // it will triggered when        // we submit the written test       return true   }   // this function will triggered when we    // write even a single char in search view   override fun onQueryTextChange(query: String?): Boolean {       if(query != null){           searchDatabase(query)       }       return true   }   // We have just created this function for searching our database   private fun searchDatabase(query: String) {       // %" "% because our custom sql query will require that       val searchQuery = "%$query%"        mainViewModel.searchDatabase(searchQuery).observe(this, { list ->           list.let {               myAdapter.setData(it)           }       })
yeah! we have successfully implemented a search feature. you can download the final source code by click here.
Now run your app and see. Is your app showing dummy data in recycler view? if yes then you have done it now you can search for something and it will show you results, 
But if don’t then add some dummy data using some steps as...
create a function like “fun additem()” inside your MainActivity.kt class
then insert data using mainViewModel.insertData(Person(“string : name”, “string: lastname”, int :age)) as shown below in code
now just call additem() function when the floating action button is clicked or however, you want.
Kotlin
fun additem(view: android.view.View) {    mainViewModel.insertData(Person("Krish", "joshi", 18))    mainViewModel.insertData(Person("Sukant", "desai", 38))    mainViewModel.insertData(Person("Anye", "jems", 40))    mainViewModel.insertData(Person("Geek", "geek", 76))    mainViewModel.insertData(Person("Alok", "pro", 45))    mainViewModel.insertData(Person("Kushi", "singh", 34))    mainViewModel.insertData(Person("Final", "step", 23))    mainViewModel.insertData(Person("Vidyut", "sharma", 20))    mainViewModel.insertData(Person("Ankit", "chaudhary", 19))    mainViewModel.insertData(Person("Abhay", "yadav", 16))}
if you want to insert data using the floating action button
then just add this line to your floating action button code
android:onClick=”additem”
That’s it your data will also be shown in your app and now you can also check for search.......finally, the app is working like
Output:
Picked
Android
Kotlin
TrueGeek
Android
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Resource Raw Folder in Android Studio
Flutter - Custom Bottom Navigation Bar
How to Read Data from SQLite Database in Android?
How to Post Data to API using Retrofit in Android?
Retrofit with Kotlin Coroutine in Android
Android UI Layouts
Kotlin Array
Retrofit with Kotlin Coroutine in Android
How to Get Current Location in Android?
Kotlin Setters and Getters