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
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:RecyclerView
in this case)Adapter
)In a
RecyclerView
, the data gets displayed in mini layouts calledViewHolder
s. Instead of creating one for each item, a pool of them gets created, and when aViewHolder
scrolls out of view it gets recycled (reused) to display a new item – which is why it’s called aRecyclerView
(as opposed to aListView
)So each
ViewHolder
is a thing that can display details for an item, and whenonBindViewHolder
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 theViewHolder
and set those during binding, so you have more info about what it’s currently displaying – setting aposition
variable is common. That way, you can put some code in theViewHolder
that works with the data it currently represents.Point is, you can make the
ViewHolder
this self-contained component that:EditText
and aTextView
TextWatcher
on theEditText
when it’s createdTextView
whenever theEditText
content changesThat’s all internal to the
ViewHolder
, it’s just wiring up the components in its own layoutIf 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:If you have a
position
variable in yourViewHolder
(that you set duringonBindViewHolder
) 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 theRecyclerView
. So you can get this chain of events:EditText
onViewHolder
updatesTextWatcher
code calls the adapter’supdateThing
function with the new dataonBindViewHolder
gets called and displays the (now changed) dataViewHolder
‘sTextView
is set to show the new dataso 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 jobI tried re-working with your code, but some things didn’t make sense, for example
RecyclerViewAdapter.ob.dataSelected
. Where are you getting theRecyclerViewAdapter
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.
Few things to note:
binding
instance to handle all of the items of the recycler view. We have theViewHolder
for this purpose.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.