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
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.
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
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
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
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.
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 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 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 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.
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.
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…
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
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.
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: