I'm trying to implement a search function to search in the SQLite database by a keyword by following this example. Here are my codes:
AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable"/>
</activity>
<activity android:name=".AddCarActivity">
</activity>
<activity android:name=".ViewActivity">
</activity>
</application>
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var cars: ListView
lateinit var car_adapter: CarAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
cars = findViewById(R.id.cars)
val add: FloatingActionButton = findViewById(R.id.add)
add.setOnClickListener {
startActivityForResult(Intent(this, AddCarActivity::class.java), 1000)
}
cars.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ ->
val cursor = CarDatabase(this).getCarList()
cursor.moveToPosition(i)
val intent = Intent(this, ViewActivity::class.java)
intent.putExtra("car", cursor.getString(1))
startActivity(intent)
}
getCars()
}
fun getCars() {
car_adapter = CarAdapter(this, CarDatabase(this).getCarList())
cars.adapter = car_adapter
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu, menu)
val manager: SearchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
val search = menu!!.findItem(R.id.search).actionView as SearchView
search.setSearchableInfo(manager.getSearchableInfo(componentName))
val context: Context = this
search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
if (query!!.isNotEmpty()) {
val cursor: Cursor = CarDatabase(context).getCarListByKeyword(query)!!
Toast.makeText(context, "${cursor.count} record(s) found!", Toast.LENGTH_LONG).show()
car_adapter.swapCursor(cursor)
}
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
if (newText!!.isNotEmpty()) {
val cursor: Cursor = CarDatabase(context).getCarListByKeyword(newText)!!
car_adapter.swapCursor(cursor)
} else
getCars()
return false
}
})
return true
}
}
AddCarActivity.kt
class AddCarActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_car)
val add: ImageButton = findViewById(R.id.add_car)
add.setOnClickListener {
val car: EditText = findViewById(R.id.car_et)
if (CarDatabase(this).addCar(car.text.toString())) {
Toast.makeText(this, "Car added!", Toast.LENGTH_SHORT).show()
setResult(1000)
finish()
} else
Toast.makeText(this, "Please try again!", Toast.LENGTH_SHORT).show()
}
}
}
ViewActivity.kt
class ViewActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view)
val car: TextView = findViewById(R.id.car_v)
car.text = intent.getStringExtra("car")
}
}
CarAdapter.kt
class CarAdapter(context: Context, cursor: Cursor?) :
CursorAdapter(context, cursor, 0) {
private var mInflater: LayoutInflater? = null
override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
val view = mInflater!!.inflate(R.layout.cars_list, parent, false)
val holder = ViewHolder()
holder.car = view.findViewById(R.id.car)
view.tag = holder
return view
}
override fun bindView(view: View, context: Context, cursor: Cursor) {
val holder = view.tag as ViewHolder
holder.car!!.text = cursor.getString(cursor.getColumnIndex(CarDatabase.CAR))
}
internal class ViewHolder {
var car: TextView? = null
}
init {
mInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
}
}
CarDatabase.kt
class CarDatabase(context: Context) : SQLiteOpenHelper(context, NAME, null, VERSION) {
companion object {
const val NAME = "Cars.db"
const val VERSION = 1
const val TABLE = "Cars"
const val ID = "_id"
const val CAR = "Car"
}
private var db: SQLiteDatabase = this.writableDatabase
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE $TABLE ($ID INTEGER PRIMARY KEY AUTOINCREMENT, $CAR TEXT);")
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS $TABLE")
onCreate(db)
}
fun getCarList(): Cursor {
val cursor = db.rawQuery("SELECT * FROM $TABLE", null)
if (!cursor.moveToFirst())
cursor.close()
return cursor
}
fun addCar(car: String): Boolean {
val values = ContentValues()
values.put(CAR, car)
return db.insert(TABLE, null, values) > 0
}
fun getCarListByKeyword(search: String): Cursor? {
val cursor = db.rawQuery("SELECT rowid as $ID, $CAR FROM $TABLE WHERE $CAR LIKE '%$search%'", null)
if (cursor == null) {
return null
} else if (!cursor.moveToFirst()) {
cursor.close()
return null
}
return cursor
}
}
Here is my example list I created after the app execution on a test device like this. This sample app does the following: By clicking an item opens the View activity and appears the selected item like this. For example, by selecting Audi you will see Audi.
The Problem is in the search function. For example, if I search for Mercedes Benz and then click on it, in the ViewActivity appears Audi like this. Or Toyota -> Mercedes Benz or Audi Q5 -> Lexus, etc. It seems, after searching there will be selected wrong positions in the ListView. How can I solve this problem? Please let me know if I could not explain something understandably.
It seems that even though you swapping the DB cursor for the search view in your adapter, you don't use it to actually get the item from your database: ( in your onItemClickListener )
val cursor = CarDatabase(this).getCarList()
cursor.moveToPosition(i)
You should get the current cursor from the adapter instead of CarDatabase(this).getCarList() and then use it, i.e.
val cursor = car_adapter.cursor
cursor.moveToPosition(i)
Hope it helps. Cheers.