skip to Main Content

I am developing a demo app where i have to update the texview of an item in recyclerview whenever i change the value of edittext in the same item of recycler view. Following is my rcv adapter.

class RecyclerViewAdapterU (val dataList:ArrayList<ModelClass>): RecyclerView.Adapter<RecyclerViewAdapterU.ViewHolder>() {
    var _binding: UploadItemViewBinding? = null
    val binding get() = _binding!!
    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): RecyclerViewAdapterU.ViewHolder {

        val v =
            LayoutInflater.from(parent.context).inflate(R.layout.upload_item_view, parent, false)
        _binding = UploadItemViewBinding.bind(v)
        return ViewHolder(binding.root)
    }


    override fun onBindViewHolder(holder: ViewHolder, @SuppressLint("RecyclerView") position: Int) {


        bindItems(dataList[position])
        holder.getStock()
        holder.updateStockDetail()

    }
    fun bindItems(data: ModelClass) {

        binding.itemquant.text = data.item_quant
        binding.uploadItemName.text = data.item_name
        binding.uploadMfg.text = data.mfg
        binding.skuStock.setText(data.item_stock.toString())
        binding.skuCode.setText(data.sku_code)
    }


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


    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var stockdetail = ArrayList<ModelClass>()

        fun getStock() {

            binding.skuStock.addTextChangedListener(object : TextWatcher {
                override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
                override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
                override fun afterTextChanged(editable: Editable) {
                    for (i in 0 until RecyclerViewAdapter.ob.dataSelected.size){
                        if (editable.toString().trim()!=""){
                            var x= editable.toString().trim().toInt()
                            RecyclerViewAdapter.ob.dataSelected[adapterPosition].item_stock=x
                        }
                    }


                }

            })
        }

        fun updateStockDetail(){
            binding.skuCode.addTextChangedListener(object : TextWatcher{
                override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
                override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {


                }
                override fun afterTextChanged(editable: Editable) {
                    if (editable.length==3){

                    var x:String
                    for (i in 0 until RecyclerViewAdapter.ob.dataSelected.size){
                        if (editable.toString().trim()!=""){
                            x=editable.toString().trim()
                            RecyclerViewAdapter.ob.dataSelected[adapterPosition].sku_code=x
                            println("$x in if")

                        }
                    }
                    println(RecyclerViewAdapter.ob.dataSelected[adapterPosition].sku_code)
                    getUpdatedDetails(RecyclerViewAdapter.ob.dataSelected[adapterPosition].sku_code)
                        for (i in stockdetail){
                            bindItems(i)

                            //binding.uploadItemName.text=i.item_name
                            println(i.item_name)
                        }
       
                }
                }

            })
        }


        fun getUpdatedDetails(skucode:String){
            val call: Call<List<ModelClass>>? =
                ApiClient.instance?.myApi?.getfromsku(skucode)!!
            call!!.enqueue(object : Callback<List<ModelClass>?> {
                override fun onResponse(
                    call: Call<List<ModelClass>?>,
                    response: Response<List<ModelClass>?>
                ) {
                    val skuDetails=response.body()

                    stockdetail.clear()
                    if (skuDetails != null) {
                        for (i in skuDetails){
                            println(i.item_name)
                            stockdetail.addAll(skuDetails)
                        }
                    }
                }

                override fun onFailure(call: Call<List<ModelClass>?>, t: Throwable) {
                }
            })
        }
    }
}

check out updatestockdetail function. Not sure if i am doing the right way.. if its not right can someone help me in doing it the right way?.

Detailed explanation of problem.
Screenshot
in this image B20 is sku code for Bingo20gm. in the same way, L20 is for Lays20gm, so if i change B20 to L20 the textview that is showing Bingo 20gm should change to Lays 20gm.
data is being delivered by API, which i am calling in getupdateddetails(), and putting them in a list.

2

Answers


  1. This is more of a code review question, but you’re overcomplicating this I think.

    When you’re using a RecyclerView (or any list widget really) you have three parts:

    • your data
    • the thing that displays the data (a RecyclerView in this case)
    • the thing that takes data and works out how to display it (the Adapter)

    In a RecyclerView, the data gets displayed in mini layouts called ViewHolders. Instead of creating one for each item, a pool of them gets created, and when a ViewHolder scrolls out of view it gets recycled (reused) to display a new item – which is why it’s called a RecyclerView (as opposed to a ListView)

    So each ViewHolder is a thing that can display details for an item, and when onBindViewHolder is called, that’s when the adapter works out how to represent your data using the view holder – things like setting text, images, colours, checked status etc. You can also stick some variables in the ViewHolder and set those during binding, so you have more info about what it’s currently displaying – setting a position variable is common. That way, you can put some code in the ViewHolder that works with the data it currently represents.


    Point is, you can make the ViewHolder this self-contained component that:

    • has an EditText and a TextView
    • sets a TextWatcher on the EditText when it’s created
    • updates the TextView whenever the EditText content changes

    That’s all internal to the ViewHolder, it’s just wiring up the components in its own layout


    If you need the ViewHolder to push some information (like your entered text, or a "button clicked" event) you probably want to put a function in your adapter that it can call:

    fun updateThing(data: String, position: Int) {
        // update your data sources, do any validation, etc
        // call notifyDataSetChanged() or similar, if required
    }
    

    If you have a position variable in your ViewHolder (that you set during onBindViewHolder) then the VH just needs to pass that when calling the function. Hey, here’s an update, I’m showing position X and here’s the new data. That way, the VH is just concerned with UI stuff, and all the data logic is kept separate in the adapter – and that can do things like deciding whether the list needs to update.


    The other reason for doing that, is that the adapter handles updates from other sources too, like your API call. So it’s much easier if all your data update logic is in one place, handled by one thing. That way, whatever the source of the update, whatever the reason is for needing an update, it’s all done the same way. And if the adapter decides that the data has changed, and the display needs to update, that happens through the normal methods – i.e. onBindViewHolder gets called, where you set the current data on the RecyclerView. So you can get this chain of events:

    • EditText on ViewHolder updates
    • TextWatcher code calls the adapter’s updateThing function with the new data
    • the adapter stores the new data and forces a display update
    • onBindViewHolder gets called and displays the (now changed) data
    • the ViewHolder‘s TextView is set to show the new data

    so in this case, you don’t need to directly modify the TextView at all, you just update the data, and as part of that process the new data will just get displayed. Because that’s what should happen when the data is changed, whatever caused it to change. And you don’t need to worry about what caused it, you just display it!


    That was a long answer and I obviously haven’t rewritten your code, but hopefully it gives you a better idea of how this works and how you can separate things and simplify them. You have a lot of code in your ViewHolder that manipulates data, pokes around at the adapter, iterates over all the items in it etc. – it should really just display stuff and react to UI events. Messing with the data is the adapter’s job

    Login or Signup to reply.
  2. I tried re-working with your code, but some things didn’t make sense, for example RecyclerViewAdapter.ob.dataSelected. Where are you getting the RecyclerViewAdapter instance from?

    Hence, I’ve pasted below a snippet of an adapter code which I had written sometime ago which solves exactly the same purpose as you require.

    class RecyclerViewAdapter(
        val interactionListener: InteractionListener,
        val onTextChangedListener: OnTextChangedListener
    ) : RecyclerView.Adapter<RecyclerViewAdapter.OptionViewHolder>() {
    
        private val variantsList = ArrayList<VariantsItem>()
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OptionViewHolder {
            val binding = SellVariantRowItemBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
            return OptionViewHolder(binding)
        }
    
        override fun onBindViewHolder(holder: OptionViewHolder, position: Int) {
            holder.bind(variantsList[position])
        }
    
        override fun getItemCount() = variantsList.size
    
        fun updateVariantsList(newList: ArrayList<VariantsItem>) {
            variantsList.clear()
            variantsList.addAll(newList)
            notifyDataSetChanged()
        }
    
        inner class OptionViewHolder(
            private val binding: SellVariantRowItemBinding
        ) : RecyclerView.ViewHolder(binding.root) {
    
            private var subVariant: String = ""
    
            @SuppressLint("SetTextI18n")
            fun bind(variantsItem: VariantsItem) {
                subVariant = variantsItem.subVariant
    
                binding.apply {
                    binding.root.tag = "pos_$bindingAdapterPosition"
                    sizeTv.text = "Size (${bindingAdapterPosition+1})"
                    variantValue.text = variantsItem.variant
                    subVariantValue.text = variantsItem.subVariant
                    quantityET.setText(variantsItem.quantity.toString().takeIf { it != "0" }?:"1")
                    sellingPriceET.setText(variantsItem.listingPrice.toString())
                    marketPriceET.setText(variantsItem.labelPrice.toString())
    
                    setOnClickListeners(variantsItem)
                    setTextChangeListeners(variantsItem)
    
                }
            }
    
            private fun setTextChangeListeners(item: VariantsItem) {
                binding.apply {
                    quantityET.addTextChangedListener {
                        if (it.toString().toIntOrNull() != null) {
                            onTextChangedListener.onQuantityChanged(
                                bindingAdapterPosition,
                                item.subVariant, item.variant,
                                it.toString()
                            )
                        }
                    }
    
                    marketPriceET.addTextChangedListener {
                        if (it.toString().toIntOrNull() != null) {
                            onTextChangedListener.onMarketPriceChanged(
                                bindingAdapterPosition,
                                item.subVariant,item.variant,
                                it.toString()
                            )
                        }
                    }
    
                    sellingPriceET.addTextChangedListener {
                        if (it.toString().toIntOrNull() != null){
                            onTextChangedListener.onSellingPriceChanged(
                                bindingAdapterPosition,
                                item.subVariant,item.variant,
                                it.toString()
                            )
                        }
                    }
                }
            }
    
            private fun setOnClickListeners(variantsItem: VariantsItem) {
                binding.apply {
                    deleteVariantBtn.setSafeOnClickListener {
                        interactionListener.onDeleteCard(variantsItem)
                    }
    
                    subVariantLayout.setSafeOnClickListener {
                        interactionListener.onChangeSubVariant(variantsItem)
                    }
                    variantLayout.setSafeOnClickListener {
                        interactionListener.onChangeVariant(variantsItem)
                    }
                    addNewVariant.setSafeOnClickListener {
                        it.pan()
                        interactionListener.onAddVariant(variantsItem.subVariant)
                    }
                }
            }
        }
    
        interface InteractionListener{
            fun onChangeSubVariant(subToBeDeleted: VariantsItem)
            fun onChangeVariant(variant: VariantsItem)
            fun onDeleteCard(variant: VariantsItem)
            fun onAddVariant(subVariantValue: String)
        }
    
        interface OnTextChangedListener{
            fun onQuantityChanged(position: Int, subVariant: String, variant: String, quantity: String)
            fun onMarketPriceChanged(position: Int, subVariant: String, variant: String, price: String)
            fun onSellingPriceChanged(position: Int, subVariant: String, variant: String, price: String)
        }
    }
    

    Few things to note:

    1. You cannot have a single binding instance to handle all of the items of the recycler view. We have the ViewHolder for this purpose.
    2. Keep your binding logic and everything related to each individual item inside the ViewHolder. That’s what it is meant for.
    3. Try keeping your adapter simple and only write presentation logic. Keep all of the business logic outside the adapter.
    4. Interfaces are your best friends. This supports point 3. Make your activity / fragment implement these interfaces and pass the instance to the Adapter constructor as such val variantsAdapter = RecyclerViewAdapter(this, this). This will allow you to write your business logic outside the Recycler View adapter (Including context required statements like calling a different activity or starting a service or as in your case, making an API call)

    Feel free to reach out in case you have any questions.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search