I\'m trying to delete an row of recyclerview using room.im doing swipe to delete a particular row....
Here is my Address table-->
@Entity(tableName
Could you try this:
viewHolder.tvDelete.setOnClickListener(View.OnClickListener { view ->
// this code is to have access to the database inside adapter you should find another way to do it,
// like pass database in contructor for ex...
val application = application as CustomApplication
Database = application.database.AddressDao()
// main code to delete in db
GlobalScope.launch(Dispatchers.IO) {
Database.delete(addresses[position])
}
//
mItemManger.removeShownLayouts(viewHolder.swipelayout)
addresses.removeAt(position)
....
In Address DAO
@Query("DELETE FROM address WHERE id=:id")
fun deleteAddress(id: Int):Int
then call it from the position where your swipe operation is done.
Update:
Create an Interface like
interface ListItemClick{
fun onClick(int id)
}
implement on your Address activity class and pass this interface to the Adapter.
fun updateData(addresses:MutableList<Address>, listener:ListItemClick) {
this.addresses = addresses
this.listener = listener
notifyDataSetChanged()
}
then, where you implemented swipe to delete, call
listener.onClick(item_id)
on AddressActivity inside onClick of ListItemClick interface
val application = application as CustomApplication
data = application.database.AddressDao().deleteAddress(id)
I wrote it here, so some spelling mistake may remain. But,I think you got the basic idea.
As some of the other responses have said, your main error is that you are not calling delete on your db. Removing an item from your adapter isn't going to be effective if your data set is based on a room db, you have to delete the actual entry.
You don't want to make calls to your db from all over your code, so you'll want to create an interface that will act as a callback to your AddressActivity, which will then make the delete call for you. Define the interface itself in the adapter, as any activity that makes use of that adapter should reference and implement the interface.
In AddressAdapter:
interface OnItemSwipeListener {
fun onItemSwipe(address: Address)
}
Then in AdapterActivity, implement the interface and the function
class AddressActivity : AppCompatActivity(), AddressAdapter.OnItemSwipeListener {
...
override fun onItemSwipe(address: Address) {
application.database.AddressDao().delete(address)
}
With this code in place, you need to do two things to connect the implementation in the AddressActivity to the AddressAdapter: 1) Pass the AddressActivity to the AddressAdapter and 2) Call the listener on the swipe events.
In AddressActivity, move the instantiation of the Adapter into the onCreate(...) method
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.address)
val adapter = AddressAdapter(this)
Lastly, in AddressAdapter, reconfigure the constructor to take a listener and instead of removing the item from the adapter call the listener
class AddressAdapter(val onItemSwipeListener: OnItemSwipeListener): RecyclerSwipeAdapter<AddressAdapter.ViewHolder>() {
...
// addresses.removeAt(position)
onItemSwipeListener.onItemSwipe(address.get(position))
That should work for you.
Something that would help you is to give your project a better structure. Google recommends the MVVM architecture with a repository for their projects. It's good that you already have room and livedata in your project, but added structure will help you facilitate changes to your data much more effectively and in a way that's easier to understand. You don't want to be making calls to your db from all over in your code, so this will help keep your db access decreased as well.
UPDATE: I noticed you said you were using swipe to delete the rows, but it looks like from your code that you are actually using a click event. The language for my solution may not be consistent for what you want to name things, but it should work all the same.
Something else to consider would be to extend ListAdapter instead of the swipe adapter you are currently using. Then, instead of needing to implement swipe logic in your adapter, you can set a swipe handler in your Activity where you initialize your adapter and recycler view.
In AddressActivity (I know how to do this in Java, you might have to play with the Kotlin syntax, since this looks funny)
val itemTouchHelper = ItemTouchHelper( object : ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
// do nothing
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
application.database.AddressDao().delete(adapter.getAddressAt(viewHolder.getAdapterPosition())
}
}).attachToRecyclerView(mPostRecyclerView)
You will have to create the getAddressAt(pos: Int) function in your adapter, or use whatever method you like for getting the right address object from the adapter.
The issue is that you are only deleting the row in Kotlin/Java but not in your SQLite room DB. I guess you should be able to solve your problem by simply adding a line that deletes an address in your DB. here an example of how i would do this for your tvDelete
clickable view:
val application = application as CustomApplication
viewHolder.tvDelete.setOnClickListener(View.OnClickListener { view ->
mItemManger.removeShownLayouts(viewHolder.swipelayout)
addresses.removeAt(position)
//remember that in onBind you already saved the current Address as "fl"
application.database.AddressDao().delete(fl)//here you delete from DB so its gone for good
//notifyDataSetChanged() dont do this as it will reexecute onbindviewholder and skip a nice animation provided by android
notifyItemRemoved(position)
//notifyItemRemoved(position) execute only once
notifyItemRangeChanged(position, addresses.size)//i dont think you will need this
mItemManger.closeAllItems()
Toast.makeText(
view.context,
"Deleted " + viewHolder.tv.getText().toString(),
Toast.LENGTH_SHORT
).show()
})
so i guess you were quite close already. please mind that i code in java so im not super confident all my code is correct but you get the idea.
Follow my simple steps to solve your issue,
Step 1:
Check once CustomApplication
name mentioned or not in AndroidManifest.xml
,
<application
android:name=".CustomApplication"
otherwise you get this issue
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.myapplication/com.example.myapplication.AddressActivity}: java.lang.ClassCastException: android.app.Application cannot be cast to com.example.myapplication.CustomApplication
Step 2:
Check your module level build.gradle
file
apply this changes
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
check dependencies -- For Kotlin use kapt instead of annotationProcessor
implementation "androidx.room:room-runtime:2.2.5"
kapt "androidx.room:room-compiler:2.2.5"
otherwise you get this issue
java.lang.RuntimeException: cannot find implementation for com.example.myapplication.Database. Database_Impl does not exist
Step 3:
check your AddressDao
interface, add this function
@Delete
suspend fun deleteAddress(address: Address)
Step 4:
in AddressAdapter
class, add this listener,
interface ItemListener {
fun onItemClicked(address: Address, position: Int)
}
add listener variable and setListener function
private lateinit var listener: ItemListener
interface ItemListener {
fun onItemClicked(address: Address, position: Int)
}
fun setListener(listener: ItemListener) {
this.listener = listener;
}
then update your code in tvDelete.setOnClickListener method
viewHolder.tvDelete.setOnClickListener(View.OnClickListener { view ->
mItemManger.removeShownLayouts(viewHolder.swipelayout)
addresses.removeAt(position)
listener.onItemClicked(fl, position)
notifyDataSetChanged()
// notifyItemRemoved(position)
// notifyItemRangeChanged(position, addresses.size)
mItemManger.closeAllItems()
Toast.makeText(
view.context,
"Deleted " + viewHolder.tv.getText().toString(),
Toast.LENGTH_SHORT
).show()
})
Step 5:
In AddressActivity
class, do this changes
Implement listener here,
class AddressActivity : AppCompatActivity(), AddressAdapter.ItemListener {
then override method
override fun onItemClicked(address: Address, position: Int) {
}
then set listener for adapter
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = adapter
adapter.setListener(this)
then update code in override method
override fun onItemClicked(address: Address, position: Int) {
lifecycleScope.launch {
val application = application as CustomApplication
application.database.AddressDao().deleteAddress(address)
}
}
here I used coroutine otherwise you can use AsycTask also
for coroutine add this dependencies in your module build.gradle
file
implementation "android.arch.lifecycle:extensions:1.1.1"
kapt "android.arch.lifecycle:compiler:1.1.1"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'
if you directly called deleteAddress method in UI class, you get this issue
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
so use such methods in background thread,
If you really want to execute in main UI thread, do this changes in your code
in AddressDao
interface,
@Delete
fun deleteAddress(address: Address)
in CustomApplication
class, add allowMainThreadQueries()
class CustomApplication : Application() {
lateinit var database: Database
private set
lateinit var addressDao: AddressDao
private set
override fun onCreate() {
super.onCreate()
this.database = Room.databaseBuilder<Database>(
applicationContext,
Database::class.java, "database"
).allowMainThreadQueries().build()
addressDao = database.AddressDao()
}
}