Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
196 views
in Technique[技术] by (71.8m points)

android - How do I remove a row from recyclerview using room?

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 = "address")
 class Address {
@PrimaryKey(autoGenerate = true)
var id = 0

@ColumnInfo(name = "address")
var address: String? = null
 }

AddressDao:

@Dao
interface AddressDao {
@Insert
suspend fun addData(address: Address)

@Query("select * from address")
fun getAddressesWithChanges() :LiveData<MutableList<Address>>

@Query("SELECT EXISTS (SELECT 1 FROM address WHERE id=:id)")
suspend fun isAddressAdded(id: Int): Int

@Delete
suspend fun delete(address: Address)
  }

Database:

@Database(entities = [Address::class], version = 1)
abstract class Database : RoomDatabase() {
abstract fun AddressDao(): AddressDao
 } 

AddressActivity:

class AddressActivity : AppCompatActivity() {
    private val adapter = AddressAdapter()

    private lateinit var data: LiveData<MutableList<Address>>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.address)

        addbutton.findViewById<View>(R.id.addbutton).setOnClickListener {
            val intent = Intent(this, AddAddressActivity::class.java)
            startActivity(intent)
        }

        val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
        recyclerView.setHasFixedSize(true)
        recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        recyclerView.adapter = adapter
        recyclerView.addItemDecoration(DividerItemDecorator(resources.getDrawable(R.drawable.divider)))
        recyclerView.addOnScrollListener(object :
            RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                Log.e("RecyclerView", "onScrollStateChanged")
            }

            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
            }
        })


        val application = application as CustomApplication
        data = application.database.AddressDao(). getAddressesWithChanges()
        data.observe(this, Observer { words1 ->
            // Update the cached copy of the words in the adapter.
            words1?.let { adapter.updateData(it) }})


    }
}

Adapter:

class AddressAdapter: RecyclerSwipeAdapter<AddressAdapter.ViewHolder>() {
    private var addresses: MutableList<Address> = Collections.emptyList()
    lateinit var  Database:Database

    override fun onCreateViewHolder(viewGroup: ViewGroup, itemViewType: Int): ViewHolder =
        ViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.address_item, viewGroup, false))

    override fun getSwipeLayoutResourceId(position: Int): Int {
        return R.id.swipe;
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        val fl: Address = addresses[position]
        viewHolder.tv.setText(fl.address)
        viewHolder.swipelayout.setShowMode(SwipeLayout.ShowMode.PullOut)
        // Drag From Right

        // Drag From Right
        viewHolder.swipelayout.addDrag(
            SwipeLayout.DragEdge.Right,
            viewHolder.swipelayout.findViewById(R.id.bottom_wrapper)
        )
        // Handling different events when swiping
        viewHolder.swipelayout.addSwipeListener(object : SwipeLayout.SwipeListener {
            override fun onClose(layout: SwipeLayout) {
                //when the SurfaceView totally cover the BottomView.
            }

            override fun onUpdate(layout: SwipeLayout, leftOffset: Int, topOffset: Int) {
                //you are swiping.
            }

            override fun onStartOpen(layout: SwipeLayout) {}
            override fun onOpen(layout: SwipeLayout) {
            }

            override fun onStartClose(layout: SwipeLayout) {}
            override fun onHandRelease(
                layout: SwipeLayout,
                xvel: Float,
                yvel: Float
            ) {
            }
        })


        viewHolder.tvDelete.setOnClickListener(View.OnClickListener { view ->
            mItemManger.removeShownLayouts(viewHolder.swipelayout)
            addresses.removeAt(position)

           //What should i do here??????????????????????????
           // val address = Address()

           // Database.AddressDao().delete(address)
            notifyDataSetChanged()
            notifyItemRemoved(position)
            notifyItemRemoved(position)
            notifyItemRangeChanged(position, addresses.size)
            mItemManger.closeAllItems()
            Toast.makeText(
                view.context,
                "Deleted " + viewHolder.tv.getText().toString(),
                Toast.LENGTH_SHORT
            ).show()
        })


        // mItemManger is member in RecyclerSwipeAdapter Class


        // mItemManger is member in RecyclerSwipeAdapter Class
        mItemManger.bindView(viewHolder.itemView, position)
    }


    override fun getItemCount(): Int = addresses.size

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var tv: TextView
        val swipelayout: SwipeLayout
        val tvDelete:TextView



        init {
            tvDelete=itemView.findViewById(R.id.tvDelete)

            tv = itemView.findViewById(R.id.ftv_name)
            swipelayout=itemView.findViewById(R.id.swipe)

        }    }

    fun updateData(addresses:
                   MutableList<Address>) {
        this.addresses = addresses
        notifyDataSetChanged() // TODO: use ListAdapter if animations are needed
    }

}

From the above code I'm able delete a row at a moment but when I revisit the activity it shows that deleted row again

I want to know how can I delete a data which is stored in room using in onBindviewHolder

As per latest answer suggested by @quealegriamasalegre

Here is my CustomApplication:--

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"
        ).build()
        addressDao = database.AddressDao()

    }
}

Adapter now:

      viewHolder.tvDelete.setOnClickListener(View.OnClickListener { view ->
        mItemManger.removeShownLayouts(viewHolder.swipelayout)
        addresses.removeAt(position)

        val application = CustomApplication()

        application.database.AddressDao().deleteAddress(position)//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) execute only once


        notifyDataSetChanged()
        notifyItemRemoved(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, addresses.size)
        mItemManger.closeAllItems()
        Toast.makeText(
            view.context,
            "Deleted " + viewHolder.tv.getText().toString(),
            Toast.LENGTH_SHORT
        ).show()
    })

Now crashing with kotlin.UninitializedPropertyAccessException: lateinit property database has not been initialized

Really need help....

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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()
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...