当前位置: 首页 > 知识库问答 >
问题:

我如何从回收视图中删除一行使用房间?

萧英睿
2023-03-14

我正在尝试删除一行recyclerview using room。我正在滑动以删除特定的行。。。。

这是我的地址表--

@Entity(tableName = "address")
 class Address {
@PrimaryKey(autoGenerate = true)
var id = 0

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

地址:

@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(entities = [Address::class], version = 1)
abstract class Database : RoomDatabase() {
abstract fun AddressDao(): AddressDao
 } 

地址活动:

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


    }
}

适配器:

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
    }

}

从上面的代码中,我可以一次删除一行,但当我重新访问该活动时,它会再次显示已删除的行

我想知道如何使用inonBindviewHolder

根据@quealegriamasalegre建议的最新答案

这是我的申请表:--

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

    }
}

适配器现在:

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

现在和科特林分手了。UninitializedPropertyAccessException:lateinit属性数据库尚未初始化

真的需要帮助。。。。

共有3个答案

邵毅
2023-03-14

你能试试这个吗:

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)
....
韦衡
2023-03-14

正如其他一些回复所说,您的主要错误是没有在db上调用delete。如果您的数据集基于房间数据库,则从适配器中删除项目将不会有效,您必须删除实际的条目。

您不想在代码中对数据库进行调用,所以您需要创建一个接口,作为对AddressActivity的回调,然后为您进行删除调用。在适配器中定义接口本身,因为任何使用该适配器的活动都应该引用并实现该接口。

在地址适配器:

interface OnItemSwipeListener {
    fun onItemSwipe(address: Address)
}

然后在AdapterActive中,实现接口和函数

class AddressActivity : AppCompatActivity(), AddressAdapter.OnItemSwipeListener {

    ...

    override fun onItemSwipe(address: Address) {
        application.database.AddressDao().delete(address)
    }

有了这个代码,您需要做两件事情来将地址活动中的实现连接到地址适配器: 1)将地址活动传递给地址适配器,2)在刷卡事件上调用侦听器。

在AddressActivity中,将适配器的实例化移动到onCreate(…)方法

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.address)
        
        val adapter = AddressAdapter(this)

最后,在AddressAdapter中,重新配置构造函数以获取侦听器,并调用侦听器,而不是从适配器中删除该项

class AddressAdapter(val onItemSwipeListener: OnItemSwipeListener): RecyclerSwipeAdapter<AddressAdapter.ViewHolder>() {
        
        ...
        // addresses.removeAt(position)
        onItemSwipeListener.onItemSwipe(address.get(position))

这应该对你有用。

对你有帮助的是给你的项目一个更好的结构。谷歌推荐MVVM架构,并为其项目提供一个存储库。您的项目中已经有了room和livedata,这很好,但添加的结构将帮助您以更容易理解的方式更有效地促进对数据的更改。你不想在代码中到处调用数据库,所以这也有助于减少数据库访问。

更新:我注意到你说你正在使用滑动来删除行,但是从你的代码中看起来你实际上是在使用单击事件。我的解决方案的语言可能与你想要命名的东西不一致,但是它应该是一样的。

另外要考虑的是扩展ListAdapter而不是当前使用的滑动适配器。然后,不需要在适配器中实现滑动逻辑,您可以在活动中设置一个滑动处理程序,在其中初始化适配器和回收器视图。

在地址活动(我知道如何在Java中做到这一点,你可能不得不玩静态编程语言语法,因为这看起来很有趣)

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)

您必须在适配器中创建getAddressAt(pos: Int)函数,或者使用您喜欢的任何方法从适配器中获取正确的地址对象。

寿意远
2023-03-14

按照我的简单步骤解决你的问题,

步骤1:检查一次CustomApplication是否在AndroidManifest中提到了名称。xml

<application
    android:name=".CustomApplication"

否则你就会遇到这个问题

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

第2步:检查你的模块级别build。gradle文件

应用这些更改

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

检查依赖项——对于Kotlin,请使用kapt而不是annotationProcessor

implementation "androidx.room:room-runtime:2.2.5"
kapt "androidx.room:room-compiler:2.2.5"

否则你就会遇到这个问题

java.lang.RuntimeException: cannot find implementation for com.example.myapplication.Database. Database_Impl does not exist

第三步:检查地址dao界面,添加此功能

 @Delete
 suspend fun deleteAddress(address: Address)

步骤四:

AddressAdapter类中,添加此侦听器,

interface ItemListener {
    fun onItemClicked(address: Address, position: Int)
}

添加listener变量和setListener函数

private lateinit var listener: ItemListener

interface ItemListener {
    fun onItemClicked(address: Address, position: Int)
}

fun setListener(listener: ItemListener) {
    this.listener = listener;
}

然后在tvDelete中更新代码。setOnClickListener方法

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

第5步:在AddressActivity类中,执行以下更改

在这里实现侦听器,

class AddressActivity : AppCompatActivity(), AddressAdapter.ItemListener {

然后重写方法

override fun onItemClicked(address: Address, position: Int) {
    
}

然后为适配器设置侦听器

        recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        recyclerView.adapter = adapter
        adapter.setListener(this)

然后更新覆盖方法中的代码

override fun onItemClicked(address: Address, position: Int) {
    lifecycleScope.launch {
        val application = application as CustomApplication
        application.database.AddressDao().deleteAddress(address)
    }
}

这里我使用了协程,否则你也可以使用AsycWork

对于协同程序,在模块构建中添加此依赖项。gradle文件

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'

如果在UI类中直接调用deleteAddress方法,就会遇到这个问题

java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

所以在后台线程中使用这样的方法,

如果您真的想在主UI线程中执行,请在代码中执行此更改

地址道接口中,

@Delete
fun deleteAddress(address: Address)

在自定义应用程序类中,添加

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()
    }
}
 类似资料:
  • 问题内容: 加入主表时,我的视图工作正常: 但是我需要添加以下联接: 尽管我添加了,但仍然得到了“重复”行。我说“重复”是因为第二行具有不同的值。 但是,如果将更改为,则会丢失具有这些“重复”行的客户端的所有行。 我究竟做错了什么?如何从视图中删除这些“重复的”行? 笔记: 此问题在这种情况下不适用: 如何删除重复的行? 问题答案: 如果行中有任何不同的列,DISTINCT不会为您提供帮助。显然,

  • 我正在使用回收器视图显示具有多个视图的项目。每当我启动应用程序时,就会显示一些空白。正好等于我的列表项。我提供了截图,请浏览一下。 我想我的代码没有问题。下面是我的代码RecycerView 适配器代码 请帮帮我。谢谢。

  • 问题内容: 从视图中删除一行,是否会从创建该视图的基表中删除相应的行?我正在使用MySQL。 问题答案: 是的,它会的。唯一需要注意的是权限。 引用官方文档 有些观点是可更新的。也就是说,您可以在诸如UPDATE,DELETE或INSERT之类的语句中使用它们来更新基础表的内容。为了使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。还有某些其他构造会使视图不可更新。

  • 本文向大家介绍从视图中删除行会从MySQL的基表中删除行吗?,包括了从视图中删除行会从MySQL的基表中删除行吗?的使用技巧和注意事项,需要的朋友参考一下 是的,从视图中删除行从基表中删除行。让我们通过创建一个新表来了解这一点。创建表的查询如下 使用insert命令在表中插入一些记录。查询如下- 使用select语句显示表中的所有记录。查询如下- 以下是输出 让我们创建一个视图。创建视图的查询如下

  • 这是不是意味着我没有刷新我的适配器还是什么? @重写公共void onDeleteClick(int position){FoodInfo selectedItem; 食物适配器 } 这是我删除项目后的删除方法,它仍然存在于我的回收视图中,并在firebase中消失 我必须离开这个页面并再次访问,然后它只显示与firebase完全相同的项目 有人帮忙吗?

  • 我以以下方式使用来显示在SQLite DB中更新的位置项列表。 这些项目被很好地删除,并在我滑动它们时更新。但是当我滑动删除最后一个项目时,它又出现在视图中。当我关闭应用程序并重新打开应用程序时,该项目已被删除。 但当项目被滑动以删除时,在该应用程序运行的实例中,即使滑动多次,该项目也不会从视图中删除。 refreshPlacesData() 我错过了什么吗?谢谢。 编辑:我尝试更新适配器而不是m