How do I remove a row from recyclerview using room?

后端 未结 5 861
执笔经年
执笔经年 2020-12-03 23:09

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          


        
相关标签:
5条回答
  • 2020-12-03 23:52

    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)
    ....
    
    0 讨论(0)
  • 2020-12-04 00:00

    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.

    0 讨论(0)
  • 2020-12-04 00:03

    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.

    0 讨论(0)
  • 2020-12-04 00:03

    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.

    0 讨论(0)
  • 2020-12-04 00:10

    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()
        }
    }
    
    0 讨论(0)
提交回复
热议问题