Home > Net >  Can't update my recyclerView with view model in room database, every item edits the previous on
Can't update my recyclerView with view model in room database, every item edits the previous on

Time:01-26

When i tried to update my recyclerView with view model in room database every id edits the previous one, not its item id in recyclerView. I used adapterposition for passing the data from recyclerView item in (EditCartFragment) to (EditSingleItemFragment) on button Clicked.

EditCartFragment.kt

class EditCartFragment : Fragment(), CommunicatorEdit{
    //Then MyCart data is ignored and we took our data from local database
    private val myCartItems = ArrayList<MyCartItemsDatabase>()
    private val myCartEditAdapter = MyCartEditAdapter(myCartItems, this)
    //Our ViewModel instance
    private lateinit var cartViewModel: CartViewModel
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        myCartDynamic.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
        myCartDynamic.adapter = myCartEditAdapter
        //addData()
        //Setting my view model and my dynamic list
        cartViewModel = ViewModelProvider(this)[CartViewModel::class.java]
        cartViewModel.readAllData.observe(viewLifecycleOwner) { cart ->
            myCartEditAdapter.setData(cart)
        }
    }

    override fun passData(position: Int, title: String, image: Bitmap, price: Double, priceI: Double, num: Int, des: String) {
        val bundle = Bundle()
        bundle.putInt("edit_pos", position)
        bundle.putString("edit_name", title)
        bundle.putString("edit_image", getImageUri(requireContext(), image).toString())
        bundle.putDouble("edit_price", price)
        bundle.putDouble("edit_priceI", priceI)
        bundle.putInt("edit_num", num)
        bundle.putString("edit_des", des)

        val transaction = this.parentFragmentManager.beginTransaction()
        val editSingleItemFragment = EditSingleItemFragment()
        editSingleItemFragment.arguments = bundle

        transaction.replace(R.id.fragment_container, editSingleItemFragment)
        transaction.addToBackStack("editSI_fragment").setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
        transaction.commit()
    }

    //Converting bitmap image to URL
    private fun getImageUri(inContext: Context, inImage: Bitmap): Uri? {
        val bytes = ByteArrayOutputStream()
        inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes)
        val path = MediaStore.Images.Media.insertImage(
            inContext.contentResolver,
            inImage,
            "Title",
            null
        )
        return Uri.parse(path)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_edit_cart, container, false)
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment MyCartFragment.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            EditCartFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

CartEditAdapter.kt

class CartEditAdapter(private var cartItems: List<CartItemsDatabase>, private val listener: CommunicatorEdit):
    RecyclerView.Adapter<CartEditAdapter.MyViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val cartView = LayoutInflater.from(parent.context).inflate(R.layout.edit_cart_items, parent, false)
        return MyViewHolder(cartView)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val newCart = cartItems[position]
        holder.cartedImg.load(newCart.cartImage){
            crossfade(true)
            transformations(CircleCropTransformation())
            placeholder(R.drawable.ic_launcher_foreground)
        }
        holder.cartedTitle.text = newCart.cartTitle.toString()
        holder.cartedItemNum.text = newCart.cartNum.toString()
        holder.cartedLPriceValue.text = newCart.cartPriceL.toString()
        holder.cartedIPriceValue.text = newCart.cartPriceI.toString()
        holder.cartedDes.text = newCart.cartDes.toString()
    }

    //Setting data from database
    fun setData(myCartComingItem: List<CartItemsDatabase>){
        this.cartItems = myCartComingItem
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int {
        return cartItems.size
    }

    inner class MyViewHolder constructor(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener{

        init {
            //Setting decrease items
            itemView.editMyItem.setOnClickListener(this)
        }

        override fun onClick(v: View?) {
            val position = adapterPosition
            val image = cartItems[adapterPosition].cartImage
            val name =  cartItems[adapterPosition].cartTitle
            val price = cartItems[adapterPosition].cartPriceL
            val priceI = cartItems[adapterPosition].cartPriceI
            val num = cartItems[adapterPosition].cartNum
            val des = cartItems[adapterPosition].cartDes
            if (position != RecyclerView.NO_POSITION) {
                listener.passData(position,name!!, image!!, price!!, priceI!!, num!!, des!!)
            }
        }

        val cartedImg: ShapeableImageView = itemView.myCartedImg
        val cartedTitle: TextView = itemView.myCartedTitle
        val cartedItemNum: TextView = itemView.myCartItemNum
        val cartedLPriceValue: TextView = itemView.localPriceCart
        val cartedIPriceValue: TextView = itemView.localPriceCartA
        val cartedDes: TextView = itemView.myCartedDes
    }
}

EditSingleItemFragment.kt

class EditSingleItemFragment : Fragment() {
    //First, we have to initialize our view model
    private lateinit var cartViewModel: CartViewModel
    private var editPos: Int?= null
    private var editTitle: String = ""
    private var editImage: String = ""
    private var editPriceL: Double = 0.0
    private var editPriceI: Double = 0.0
    private var editNum: Int = 0
    private var editDes: String = ""
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        editPos = arguments?.getInt("edit_pos")
        editTitle = arguments?.getString("edit_name").toString()
        editImage = requireArguments().getString("edit_image").toString()
        editPriceL = requireArguments().getDouble("edit_price")
        editPriceI = requireArguments().getDouble("edit_priceI")
        editNum = requireArguments().getInt("edit_num")
        editDes = arguments?.getString("edit_des").toString()

        editSTitle.text = editTitle
        editSImg.load(editImage){
            crossfade(true)
            transformations(CircleCropTransformation())
            placeholder(R.drawable.ic_launcher_foreground)
        }
        editSLPrice.text = editPriceL.toString()
        editSIPrice.text = editPriceI.toString()
        editSNum.text = editNum.toString()
        editSDes.text = editDes
        //Edit our items number
        incDecNum()
        //View Model Calling
        cartViewModel = ViewModelProvider(this)[CartViewModel::class.java]
        //Update Cart listener
        editSUpdate.setOnClickListener {
            lifecycleScope.launch {
                updateDatabase()
                Toast.makeText(context, "Item is updated", Toast.LENGTH_SHORT).show()
            }
        }
    }
    //Add data to database
    private suspend fun updateDatabase(){
        val cartedTitle = editSTitle.text.toString()
        val cartedImage = getBitmap(editImage)
        val cartedPrice = editSLPrice.text.toString().toDouble()
        val cartedPriceI = editSIPrice.text.toString().toDouble()
        val cartedItemNum = editSNum.text.toString().toInt()
        val cartedDes = editSDes.text.toString()
        val item = MyCartItemsDatabase(editPos!!, cartedTitle, cartedImage,
            cartedPrice, cartedPriceI, cartedItemNum, cartedDes)
        cartViewModel.updateCart(item)
    }
    //Converting image url to bitMap
    private suspend fun getBitmap(img: String): Bitmap {
        val loading = ImageLoader(requireContext())
        val request = ImageRequest.Builder(requireContext())
            .data(img).build()
        val result: Drawable = (loading.execute(request) as SuccessResult).drawable
        return (result as BitmapDrawable).bitmap
    }

    //Increase or decrease items' number
    private fun incDecNum(){
        //Initializing the price value
        val myPrice = editSLPrice.text.toString().toDouble()
        //Initializing the incDec value
        var numberOfItems = editSNum.text.toString().toInt()
        editSDec.setOnClickListener {
            if (numberOfItems > 1){
                numberOfItems -= 1
                val newVal = numberOfItems.toString()
                editSNum.text = newVal
                val price = myPrice*numberOfItems
                editSIPrice.text = price.toString()
            }
        }
        editSInc.setOnClickListener {
            numberOfItems  = 1
            val newVal = numberOfItems.toString()
            editSNum.text = newVal
            val price = myPrice*numberOfItems
            editSIPrice.text = price.toString()
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_edit_single_item, container, false)
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment EditSingleItemFragment.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            EditSingleItemFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

I'm trying to update my room database columns viewed in recyclerView.

CodePudding user response:

I'll start with some suggestions that will help you with your problem and your code will become very clear to understand and performative.

  1. Move the variable myCartItems from the Fragment to ViewModel. The ViewModel is the right place to keep the status and all data that you use on your UI.

  2. Use LiveData to observe the changes on your myCartItems list. Do not forget to protect your property, follow this pattern:

class MyViewModel: ViewModel() {

    private val _myCartItems = MutableLiveData<List<MyCartItemsDatabase>>(emptyList())
    val myCartItems: LiveData<List<MyCartItemsDatabase>>
        get() = _myCartItems

}
  1. Observe changes on myCartItems on your fragment. When myCartItems update on ViewModel, the observer will be notified and then you can send the updated list that you receive on your observer to the adapter
class MyFragment: Fragment()  {

    private val viewModel: MyViewModel by viewModel()

    private lateinit var adapter: MyAdapter

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

        // set the adapter to your recycler view
        // observe changes of your live data
        viewModel.myCartItems.observe(viewLifecycleOwner) { myCartItems ->
            // You will send the myCartItems that you receive to your adapter here
        }
    }
}
  1. Are you using RecyclerView? Prefer always to use RecyclerView instead ListView or GridView, because it's so much more performative.

  2. For the Adapter implementation, always prefer to use ListAdapter too, that have a more efficient way to compare the current list with the new list and update it. You can follow this example:

abstract class MyAdapter: ListAdapter<MyCartItemsDatabase, MyAdapter.MyAdapterViewHolder>(DIFF_CALLBACK) {
   
     // ...

    class MyAdapterViewHolder(
        private val binding: ItemSimplePosterBinding
    ): RecyclerView.ViewHolder(binding.root) {
     // ...
     }

    companion object {
        private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<MyCartItemsDatabase>() {
            override fun areItemsTheSame(oldItem: MyCartItemsDatabase, newItem: MyCartItemsDatabase): Boolean {
                // need a unique identifier to have sure they are the same item. could be a comparison of ids. In this case, that is just a list of strings just compare like this below
                return oldItem.id == newItem.id
            }

            override fun areContentsTheSame(oldItem: MyCartItemsDatabase, newItem: MyCartItemsDatabase): Boolean {
                // compare the objects
                return oldItem.items == newItem.items
            }

        }
    }
}
  • Related