如何使用实时数据从 RecyclerView 中删除项目?

Posted

技术标签:

【中文标题】如何使用实时数据从 RecyclerView 中删除项目?【英文标题】:How to delete item from RecyclerView with Live Data? 【发布时间】:2021-09-25 16:35:52 【问题描述】:

我在为 android 制作应用程序方面也是全新的(大概一周左右。) 我正在尝试根据数据库中的数据更改(使用 LiveData)实时更新购物车片段 UI,但我不知道如何使用该特定行中的按钮从购物车中删除一行。

我一直在谷歌上搜索,但我真的被困住了,因为我不完全确定自己在做什么。

这是我的购物车适配器的代码:

class CartAdapter(private var cartData: LiveData<List<Cart>>) : RecyclerView.Adapter<CartAdapter.ItemViewHolder>()



    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder 
        //create a new view, which defines the UI of the list item
        val view = LayoutInflater.from(parent.context).inflate(R.layout.cart_item,parent,false)
        return ItemViewHolder(view)
    

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) 
        holder.nameTV.text = cartData.value?.get(position)?.itemName
        holder.priceTV.text = cartData.value?.get(position)?.itemPrice.toString()
        holder.imagebtn.setOnClickListener 
            deleteItem(position)
        
    
    private fun deleteItem(position: Int)
    
        
        notifyItemChanged(position)
        cartData.observe(/**help here ;-;)*/)
    
    override fun getItemCount(): Int 
        return cartData.size
        
    

    class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view)
        var nameTV: TextView = view.findViewById(R.id.item_name_text_view)
        var priceTV: TextView = view.findViewById(R.id.item_price_text_view)
        var imagebtn: ImageButton = view.findViewById(R.id.clear_button)
    

这是 CartFragment 的代码:

class CartFragment : Fragment() 
    
    private val viewModel: ItemViewModel by activityViewModels 
        ItemViewModelFactory(
            (activity?.application as NiekBakehouseApplication).database.itemDao()
        )
    

    private var cartItems = viewModel.cartList
    private var _binding: FragmentCartBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? 
        _binding = FragmentCartBinding.inflate(inflater, container, false)
        return binding.root
    

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)

        val adapter = CartAdapter(cartItems) //help here ;-;)
        binding.recyclerView.layoutManager = LinearLayoutManager(this.context)
        binding.recyclerView.adapter = adapter

        viewModel.cartList.observe(viewLifecycleOwner,binding.recyclerView.adapter?.notifyDataSetChanged())

    


    override fun onDestroyView() 
        super.onDestroyView()
        _binding = null
    

这是 DAO:

@Dao
interface ItemDao 

    /**on conflict strat is used when there's a conflict when calling function
     * this makes it ignores a new item if it's primary key already exists*/
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(item: Item)

    @Update
    suspend fun update(item: Item)

    @Delete
    suspend fun delete(item: Item)

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(cart: Cart)

    @Update
    suspend fun update(cart: Cart)

    @Delete
    fun delete(cart: Cart)


    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(pesenan: Pesenan)
    @Update
    suspend fun update(pesenan: Pesenan)
    @Delete
    suspend fun delete(pesenan: Pesenan)

    @Query("SELECT * FROM Item where iId = :id")
    fun getItem(id: Long) : Flow<Item>

    @Query("SELECT * FROM Pesenan where oId = :id")
    fun getOrder(id: Long) : Flow<Pesenan>

    @Query("SELECT * From Cart where iId = :id")
    fun getCart(id: Long) : Flow<Cart>

    @Query("SELECT * FROM Pesenan where oId = :id")
    fun getOrder2(id: Long) : Pesenan

    @Query("select itempesenancros-s-ref.itemId  as iId, itemName as itemName, itemPrice as itemPrice, quantity as itemQuantity from itempesenancros-s-ref inner join item where item.iId = itempesenancros-s-ref.itemId AND itempesenancros-s-ref.orderId = :id") // need to get all items with the same in pesenan table via cross ref.
    fun getItemsFromReference(id:Long):Flow<List<Item>>

    @Query("select itempesenancros-s-ref.itemId  as iId, itemName as itemName, itemPrice as itemPrice, quantity as itemQuantity from itempesenancros-s-ref inner join item where item.iId = itempesenancros-s-ref.itemId AND itempesenancros-s-ref.orderId = :id") // need to get all items with the same in pesenan table via cross ref.
    fun getItemsFromReference2(id:Long):List<Item>

    //get item by pesenan ID!
    @Query("SELECT * FROM ItemPesenanCrossRef where orderId=:id")
    fun getListItemId(id:Long): List<ItemPesenanCrossRef>

    /**"SELECT * FROM plants WHERE id IN (SELECT DISTINCT(plant_id) FROM garden_plantings)*/
    /**fun getPlantedGardens(): Flow<List<PlantAndGardenPlantings>>*/

    @Transaction
    @Query("SELECT * FROM Item where iId in (SELECT DISTINCT(itemId) FROM ItemPesenanCrossRef)")
    fun getItemWithPesenans(): Flow<List<ItemsWithPesenan>>

    @Transaction
    @Query("SELECT * FROM Pesenan where oId = :id in (SELECT DISTINCT(orderId) FROM ItemPesenanCrossRef)")
    fun getPesenanItems(id: Long): List<PesenanWithItems>

    @Query("SELECT * FROM item")
    fun getAllItems(): Flow<List<Item>>

    @Query("SELECT * FROM pesenan")
    fun getAllPesenan(): Flow<List<Pesenan>>

    @Query("select * from cart")
    fun getAllCartItem() : Flow<MutableList<Item>>


and this is ItemViewModel where the factory goes :

    class ItemViewModel(private val itemDao: ItemDao) : ViewModel() 

    val allItems: LiveData<List<Item>> = itemDao.getAllItems().asLiveData()
    val allOrders: LiveData<List<Pesenan>> = itemDao.getAllPesenan().asLiveData()
    val cartList: LiveData<MutableList<Item>> = itemDao.getAllCartItem().asLiveData()



    /**
     * Updates an existing Item in the database.
     */
    fun updateItem(
        itemId: Long,
        itemName: String,
        itemPrice: String,
        itemCount: Int
    ) 
        val updatedItem = getUpdatedItemEntry(itemId, itemName, itemPrice, itemCount)
        updateItem(updatedItem)
    

    fun updateCart(
        itemId: Long,
        itemName: String,
        itemPrice: String,
        itemCount: Int
    ) 
        val updatedCart = getUpdatedCartEntry(itemId, itemName, itemPrice, itemCount)
        updateCart(updatedCart)
    

    /**
     * Launching a new coroutine to update an item in a non-blocking way
     */
    private fun updateItem(item: Item) 
        viewModelScope.launch 
            itemDao.update(item)
        
    

    private fun updateCart(cart: Cart) 
        viewModelScope.launch 
            itemDao.update(cart)
        
    
    /**
     * Inserts the new Item into database.
     */
    fun addNewItem(itemName: String, itemPrice: String, itemCount: Int) 
        val newItem = getNewItemEntry(itemName, itemPrice, itemCount)
        insertItem(newItem)
    

    fun addToCart(itemId: Long, name: String, price: String, qty: Int)
    
        val newCart = getNewCartEntry(itemId,name,price,qty)
        insertCart(newCart)
    
    /**
     * Launching a new coroutine to insert an item in a non-blocking way
     */
    private fun insertItem(item: Item) 
        viewModelScope.launch 
            itemDao.insert(item)
        
    

    private fun insertCart(cart: Cart) 
        viewModelScope.launch 
            itemDao.insert(cart)
        
    

    private fun insertPesenan(pesenan: Pesenan) 
        viewModelScope.launch 
            itemDao.insert(pesenan)
        
    

    /**
     * Launching a new coroutine to delete an item in a non-blocking way
     */
    fun deleteItem(item: Item) 
        viewModelScope.launch 
            itemDao.delete(item)
        
    

    fun deleteCart(cart: Cart) 
        viewModelScope.launch 
            itemDao.delete(cart)
        
    

    fun deletePesenan(pesenan: Pesenan) 
        viewModelScope.launch 
            itemDao.delete(pesenan)
        
    

    /**
     * Retrieve an item from the repository.
     */
    fun retrieveItem(id: Long): LiveData<Item> 
        return itemDao.getItem(id).asLiveData()
    

    fun retrievePesenan(id: Long): LiveData<Pesenan> 
        return itemDao.getOrder(id).asLiveData()
    

    fun retrieveCart(id: Long): LiveData<Cart>
        return itemDao.getCart(id).asLiveData()
    


    //TODO: extract item list!
    fun retrieveItemsFromOrder(orderIdTarget: Long): LiveData<List<Item>> 
        return itemDao.getItemsFromReference(orderIdTarget).asLiveData()//look for the items associated in this particular order ID
        /**get all items from cros-s-ref*/
    

    fun retrieveItemsFromOrder2(orderIdTarget: Long): List<Item> 
        return itemDao.getItemsFromReference2(orderIdTarget)//look for the items associated in this particular order ID
        /**get all items from cros-s-ref*/
    

    /**
     * Returns true if the EditTexts are not empty
     */
    fun isEntryValid(itemName: String, itemPrice: String): Boolean 
        if (itemName.isBlank() || itemPrice.isBlank()) 
            return false
        
        return true
    

    private fun getNewItemEntry(itemName: String, itemPrice: String, itemCount: Int): Item 
        return Item(
            iId = 0, //hopefully we can make this auto increment
            itemName = itemName,
            itemPrice = itemPrice.toDouble(),
            itemQuantity = itemCount
        )
    
    private fun getNewCartEntry(itemId: Long, itemName: String, itemPrice: String, itemCount: Int): Cart 
        return Cart(
            iId = itemId, //hopefully we can make this NOT auto increment
            itemName = itemName,
            itemPrice = itemPrice.toDouble(),
            itemQuantity = itemCount
        )
    

    /**
     * Called to update an existing entry in the Inventory database.
     * Returns an instance of the [Item] entity class with the item info updated by the user.
     */
    private fun getUpdatedItemEntry(
        itemId: Long,
        itemName: String,
        itemPrice: String,
        itemCount: Int
    ): Item 
        return Item(
            iId = itemId,
            itemName = itemName,
            itemPrice = itemPrice.toDouble(),
            itemQuantity = itemCount
        )
    

    private fun getUpdatedCartEntry(
        itemId: Long,
        itemName: String,
        itemPrice: String,
        itemCount: Int
    ): Cart 
        return Cart(
            iId = itemId,
            itemName = itemName,
            itemPrice = itemPrice.toDouble(),
            itemQuantity = itemCount
        )
    




class ItemViewModelFactory(private val itemDao: ItemDao) : ViewModelProvider.Factory 
    override fun <T : ViewModel?> create(modelClass: Class<T>): T 

        if(modelClass.isAssignableFrom(ItemViewModel::class.java)) 
            @Suppress("UNCHECKED_CAST")
            return ItemViewModel(itemDao) as T
        
        throw IllegalArgumentException("Unknown ViewModel class")
    

这是购物车片段的 XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/item_name_header"
        android:layout_
        android:layout_
        android:text="@string/item_name_header"
        android:textSize="16sp"
        android:gravity="center_horizontal"
        android:padding="8dp"
        app:layout_constraintWidth_percent="0.5"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="@id/arrival_time_header"
        app:layout_constraintEnd_toStartOf="parent"/>

    <TextView
        android:id="@+id/arrival_time_header"
        android:layout_
        android:layout_
        android:text="@string/item_price_header"
        android:textSize="16sp"
        android:gravity="center_horizontal"
        android:padding="8dp"

        app:layout_constraintWidth_percent="0.5"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toEndOf="@id/item_name_header"
        app:layout_constraintEnd_toEndOf="parent"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_
        android:layout_
        android:clipToPadding="false"
        android:padding="16dp"
        app:layout_constraintTop_toBottomOf="@id/arrival_time_header"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toTopOf="@id/constrabottom"
        />
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constrabottom"
        android:layout_
        android:layout_
        app:layout_constraintBottom_toBottomOf="parent">


        <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_
        android:layout_
            app:layout_constraintWidth_percent="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">

            <ImageButton
                android:id="@+id/decrease"
                android:layout_
                android:layout_
                android:src="@drawable/ic_decrease"
                android:scaleType="centerCrop"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toStartOf="@id/qty"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"/>

            <TextView
                android:id="@+id/qty"
                android:layout_
                android:layout_
                android:textSize="12sp"
                android:paddingStart="18dp"
                android:paddingEnd="18dp"
                app:layout_constraintStart_toEndOf="@id/decrease"
                app:layout_constraintEnd_toStartOf="@id/increase"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"/>

            <ImageButton
                android:id="@+id/increase"
                android:layout_
                android:layout_
                android:scaleType="centerCrop"
                android:src="@drawable/ic_increase"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@id/qty"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>
        <TextView
            android:id="@+id/subtotal_enter"
            android:layout_
            android:layout_
            tools:text="Subtotal"
            style="@style/TextAppearance.MaterialComponents.Headline6"
            app:layout_constraintStart_toStartOf="@id/subtotal_value"
            app:layout_constraintBottom_toTopOf="@id/subtotal_value"
            android:textSize="20sp"/>

        <TextView
            android:id="@+id/subtotal_value"
            android:layout_
            android:layout_
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:textSize="50sp"
            tools:text="8888" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

我刚刚在 developer.android.com 上完成了关于使用 Kotlin 构建应用程序的整个课程,但我不确定我在做什么。

【问题讨论】:

您不应该在适配器中发送实时数据,您应该发送 List 而不是 LiveData> 然后在删除按钮上从列表中删除项目并 notifyItemRemoved(position) ; 【参考方案1】:

首先,不要在适配器中使用实时数据删除实时数据,而只需使用 private var cartData: 还可以在适配器中传递点击侦听器,然后现在在片段内部覆盖这里是我的道删除方法 拿两个这样的列表

private var list: MutableList<DataTable>? = null
private var allListOfKeys: MutableList<DataTable>? = null

在你的观察者中初始化这样的列表

allListOfKeys = dataDao.getAllData().toMutableList()

                allListOfKeys?.forEach 
                    list?.add(it)
                
                allKeysAdapter.submitList(allListOfKeys)

然后在你的数据库中像这样删除它这是我在 DAO 中的删除方法

@Query("DELETE from data_table WHERE id = :id")
suspend fun deleteCard(id: Int?)

这是我的删除点击监听器

 override fun onDeleteClickListener(position: Int, item: DataTable) 

        runOnUiThread 
            MainScope().launch 
                dataDao.deleteCard(item.id)
                removeView(position, item)
            
        
    

    private fun removeView(position: Int, item: DataTable) 

        allListOfKeys?.remove(item)
        binding.rvKeys.removeViewAt(position)
        allKeysAdapter.notifyItemRemoved(position)
        allKeysAdapter.notifyItemRangeChanged(position, allListOfKeys?.size!!)
        allKeysAdapter.notifyDataSetChanged()

    

这是如何从该位置以及数据库中删除数据的示例

【讨论】:

以上是关于如何使用实时数据从 RecyclerView 中删除项目?的主要内容,如果未能解决你的问题,请参考以下文章

单击 RecyclerView 后使用 Firebase 实时数据库填充详细信息活动

无法从firebase实时数据库检索数据到recyclerview [重复]

Android-从Firebase实时数据库读取数据并显示在RecyclerView上

如何通过firebase实时数据库永久隐藏RecyclerView中的imageview

从Firebase实时数据库读取Android数据并在RecyclerView上显示

使用 Firebase 实时数据库参考填充 RecyclerView