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.
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.
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
}
- 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
}
}
}
Are you using RecyclerView? Prefer always to use RecyclerView instead ListView or GridView, because it's so much more performative.
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
}
}
}
}