Android SearchView in Room Database
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 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 by Step Implementation
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.Entity import 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.Database import 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 class abstract 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.Context import androidx.room.Room import com.example.searchViewInRoom.data.PersonDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ApplicationComponent import dagger.hilt.android.qualifiers.ApplicationContext import 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.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import kotlinx.coroutines.flow.Flow // here we have our Dao having three // different queries as follows..... @Dao interface 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.Flow import javax.inject.Inject // it's repository /* here we have basically injected our person Dao for our dao interface and 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.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.example.searchViewInRoom.data.Person import com.example.searchViewInRoom.data.Repository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch // here we have our viewModel // and we are going to use it for receiving // and observing data from our database class 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 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 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" ?> < 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.AppCompatActivity import android.os.Bundle import android.view.Menu import android.widget.Toast import androidx.activity.viewModels import androidx.appcompat.widget.SearchView import androidx.recyclerview.widget.LinearLayoutManager import com.example.searchViewInRoom.adapter.MyAdapter import com.example.searchViewInRoom.data.Person import com.example.searchViewInRoom.databinding.ActivityMainBinding import com.example.searchViewInRoom.viewmodel.MainViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class 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.AppCompatActivity import android.os.Bundle import android.view.Menu import android.widget.Toast import androidx.activity.viewModels import androidx.appcompat.widget.SearchView import androidx.recyclerview.widget.LinearLayoutManager import com.example.searchViewInRoom.adapter.MyAdapter import com.example.searchViewInRoom.data.Person import com.example.searchViewInRoom.databinding.ActivityMainBinding import com.example.searchViewInRoom.viewmodel.MainViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class 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:
Please Login to comment...