How to stop textchangedlistener being executed infinitely after changing text once?

412 Views Asked by At

I have been working on a stock keeping application as a demo to learn kotlin and android studio, I have added textchangedlistener in a recycler view item, which everytime it text is changed, gets information from Api and displays new item on textview. This works good for only one time, After that it keeps infinitely changing. please check video for better understanding : ScreenRecording .

Here 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.apply {
        itemquant.text=data.item_quant
        uploadItemName.text = data.item_name
        uploadMfg.text = data.mfg
        skuStock.setText(data.item_stock.toString())
        skuCode.setText(data.sku_code)
    }

}

fun getUpdatedDetails(skucode:String,pos:Int){
    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()

            if (skuDetails != null) {
                dataList.removeAt(pos)
                for (i in skuDetails){
                    println(i.item_name)
                    dataList.add(pos,i)
                }
                notifyItemChanged(pos)
            }

        }

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


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


inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {


    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
                        //getting current itemstock before pushing update.
                        //assigning latest itemstock to the data for the update
                    }
                }
            }

        })
    }

    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) {
                var x:String=""
                var pos:Int=adapterPosition
                    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)
                    //getting edited text and calling the function to get updated details.
                    getUpdatedDetails(x,pos)
    binding.skuStock.removeTextChangedListener(this)

                }
        })
    }

}

}

If possible please review my code and let me know what are things i need to work on. Note: ob.dataselected is a global variable from another recyclerview adapter. Textchangedlistener i am talking about is in the fun updateStockDetail()

1

There are 1 best solutions below

7
Cheticamp On

In the documentation for afterTextChanged we see the following:

This method is called to notify you that, somewhere within s, the text has been changed. It is legitimate to make further changes to s from this callback, but be careful not to get yourself into an infinite loop, because any changes you make will cause this method to be called again recursively.

(The emphasis is mine.) I don't see where you remove the text watcher before changing the text, so you may be in an infinite loop.

To remove the text watcher use removeTextChangedListener. You can replace the text watcher after making changes.