I have a recycler view of edit texts, however when scrolling my data of edit texts sometimes disappear and sometimes repeat itself in other edit texts.
I also use two functions in my adapter to get the values passed for edit texts in my case (product & price) but as I said I don't get the right values so I'm looking for your help guys.
This is my recycler view adapter:
class MyAdapter(var myList: List<ProductModelClass>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
inner class ViewHolder(val binding: ItemToAddBinding) : RecyclerView.ViewHolder(binding.root) {}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = ItemToAddBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.binding.etProductName.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
val text: String = s.toString()
myList[position].product = text
}
})
holder.binding.etPrice.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
val text: String = s.toString()
myList[position].price = text
}
})
}
override fun getItemCount(): Int {
return myList.size
}
fun getProduct(): ArrayList<String>{
val productList: ArrayList<String> = ArrayList()
for(element in myList) {
productList.add(element.product)
}
return productList
}
fun getPrice(): ArrayList<String>{
val priceList: ArrayList<String> = ArrayList()
for(element in myList) {
priceList.add(element.price)
}
return priceList
}
}
Related
I am new to kotlin-android and I am developing a stock keeping unit, where in one fragment i need to load the data into recycler view using arraylist but also, i need one blank item, where user can enter his own data and once he fills that blank item, a new blank item should be created.
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)
}
fun getUpdatedDetails(skucode:String,pos:Int){
// val skuDetails:ArrayList<ModelClass>
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>?>
) {
if (!response.body().isNullOrEmpty()) {
val skuDetails: ArrayList<ModelClass> = response.body() as ArrayList<ModelClass>
//
// if (!skuDetails.isNullOrEmpty()) {
val x = dataList[pos].sku_code
if (skuDetails[pos].sku_code != x) {
//for (i in skuDetails.indices){
println(skuDetails[pos].sku_code)
println(".........$pos")
dataList.removeAt(pos)
dataList.add(pos, skuDetails[0])
skuDetails.clear()
notifyItemChanged(pos)
//}
}
}
}
override fun onFailure(call: Call<List<ModelClass>?>, t: Throwable) {
}
})
}
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)
}
}
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) {
if (binding.skuStock.isFocused){
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) {
//binding.skuCode.removeTextChangedListener(this)
var pos:Int=adapterPosition
var x= editable.toString().trim()
//RecyclerViewAdapter.ob.dataSelected[adapterPosition].sku_code=x
println("$x in textwatcher and $pos")
//getting edited text and calling the function to get updated details.
getUpdatedDetails(x,pos)
// binding.skuCode.addTextChangedListener(this)
}
})
}
}
}
I have couple of bugs in the adapter too, for example, i cant change textwatcher more than once.
If somebody could help me with this, would be very glad.
ob.data.selected comes from another recyclerview adapter.
One way you can do this is by using a different view type for the blank item that will always stay at the very top (or bottom if you prefer). Once the blank item is filled up then a new non-blank item with a different view type will be added to your list.
More info about view types here: Android and Kotlin: RecyclerView with multiple view types
My textchanged listener is for updatestockdetail() is working very weirdly, it crashes after 4-5 times changing the text and it also shows the first item when i change the text for the third time. I believe i am ding something majorly wrong. If somebody could help me, would be very glad.
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)
}
fun getUpdatedDetails(skucode:String,pos:Int){
// val skuDetails:ArrayList<ModelClass>
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.isNullOrEmpty()) {
val x=dataList[pos].sku_code
for (i in skuDetails.indices){
println(skuDetails[i].sku_code)
println(".........$pos")
if (skuDetails[i].sku_code!=x){
dataList.removeAt(pos)
dataList.add(pos,skuDetails[i])
notifyItemChanged(pos)
}
}
}
}
override fun onFailure(call: Call<List<ModelClass>?>, t: Throwable) {
}
})
}
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)
}
}
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) {
if (binding.skuStock.isFocused){
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) {
binding.skuCode.removeTextChangedListener(this)
var pos:Int=adapterPosition
var x= editable.toString().trim()
//RecyclerViewAdapter.ob.dataSelected[adapterPosition].sku_code=x
println("$x in textwatcher and $pos")
//getting edited text and calling the function to get updated details.
getUpdatedDetails(x,pos)
binding.skuCode.addTextChangedListener(this)
}
})
}
}
}
Before anyone asks, ob.dataselected comes from another rcv adapter. There is a issue in notifyitemchanged(pos) in getupdateddetails() which is called from updateStockDetail().
binding.skuCode.addTextChangedListener(this)
above code is causing the loop. You have to get rid of this line.
I'm new with these case and I want to try to get the value of dynamic EditText(s) from RecyclerView.
And in the layout :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_marginTop="#dimen/_10sdp"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_question"
style="#style/textH4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="bottom"
android:text="Ini adalah tempat pertanyaan pertama" />
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/et_answer"
android:layout_width="match_parent"
android:layout_height="#dimen/_120sdp"
android:hint="Tulis jawaban anda"
style="#style/EdittextPrimary"
android:gravity="top"
android:background="#drawable/bg_outline_grey"
android:inputType="textMultiLine"
android:layout_marginTop="#dimen/_10sdp"/>
</LinearLayout>
The question is.. how do I get the EditText value and put it in an ArrayList ?
Newest update codes :
I add my Recycler Adapter, and tried these :
class RequestJoinAdapter(
val onTextChanged: (text:String,position:Int)->Unit
) :
RecyclerView.Adapter<RequestJoinAdapter.ViewHolder>() {
var listData: MutableList<KeamananModel> = ArrayList()
private var textValue = ""
fun insertAll(data: List<KeamananModel>) {
data.forEach {
listData.add(it)
notifyItemInserted(listData.size - 1)
}
}
fun clear() {
if (listData.isNotEmpty()) {
listData.clear()
notifyDataSetChanged()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemRequestJoinBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = listData[position]
holder.bindTo(item)
holder.binding.etAnswer.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable?) {
onTextChanged.invoke(s.toString(), position)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textValue = s.toString().toLowerCase()
}
})
}
override fun getItemCount() = listData.size
inner class ViewHolder(val binding: ItemRequestJoinBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindTo(item: KeamananModel) {
val context = binding.root.context
binding.tvQuestion.text = item.qt
}
}
private fun isLog(msg: String) {
Log.e("join grup:", msg)
}
}
and in my current Activity, I used to add some codes like
RequestJoinAdapter { text, position ->
val values = ArrayList<Jawaban>()
val hashSet = HashSet<Jawaban>()
values.add(Jawaban(pertanyaan_no = position+1, jawaban = text))
hashSet.addAll(values)
values.clear()
values.addAll(hashSet)
val valuest = ArrayList<JawabanKeamanan>()
val hashSets = HashSet<JawabanKeamanan>()
valuest.add(JawabanKeamanan(values))
hashSets.addAll(valuest)
valuest.clear()
valuest.addAll(hashSets)
isLog("currentResult: $valuest")
}
How do I set my latest valuest into my var listJawaban: MutableList<JawabanKeamanan> = ArrayList() without any duplicate datas inside it ? What I want is like JawabanKeamanan(jawaban_keamanan=[Jawaban(pertanyaan_no=1, jawaban=t)], [Jawaban(pertanyaan_no=2, jawaban=u)]). Thanks..
The simplest answer is to implement a TextWatcher to your EditText and have it return your data when your text is changed.
Now you may ask how can I get such data in my activty? Well it's pretty simple.
Create an interface in your adapter to communicate with your activity.
Pass your interface as a constructor parameter so when you initialize your adapter in the activity your methods are implemented.
Another solution for you is to add a button in each item of your list and call your interface method in the button's OnClickListener.
EDIT:
In the snippet below I have used a lambda function for when the text changes.
class RequestJoinAdapter(
private val onClickedItem: (ArrayList<String>)->Unit,
val onTextChanged: (text:String,position:Int)->Unit
):
RecyclerView.Adapter<RequestJoinAdapter.ViewHolder>() {
var listData: MutableList<KeamananModel> = ArrayList()
var listJawaban: MutableList<Jawaban> = ArrayList()
private var textValue = ""
fun insertAll(data: List<KeamananModel>) {
data.forEach {
listData.add(it)
notifyItemInserted(listData.size - 1)
}
}
fun clear() {
if (listData.isNotEmpty()) {
listData.clear()
notifyDataSetChanged()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemRequestJoinBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = listData[position]
holder.bindTo(item)
}
override fun getItemCount() = listData.size
inner class ViewHolder(val binding: ItemRequestJoinBinding):
RecyclerView.ViewHolder(binding.root) {
fun bindTo(item: KeamananModel) {
val context = binding.root.context
binding.tvQuestion.text = item.qt
binding.etAnswer.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable?) {
if (textValue.isNotEmpty()) {
isLog(textValue)
onTextChanged.invoke(s,absoluteAdapterPosition)
} else {
}
listJawaban.add(Jawaban(pertanyaan_no = adapterPosition + 1, jawaban = textValue))
isLog(listJawaban.toString())
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textValue = s.toString().toLowerCase()
}
})
}
}
private fun isLog(msg: String) {
Log.e("join grup:", msg)
}
}
Pay attention to the onTextChanged.invoke() method. This lambda function can be used like an interface to communicate between your adapter and your view. It will be triggered every time your text is changed.
Finally instantiate your adapter like below:
val adapter = RequestJoinAdapter({
}, { text, position ->
//onTextChanged
})
The position argument is there to help you know which TextView was changed
I have a recyclerview where each view contains a TextView and an EditText. The part where I am stuck is when I start typing in any of the row's EditText box, it automatically updates another row further down the list. I know this is occurring because of the whole recycling aspect of recyclerview and the same view is re-used for the rows further down. I just don't know how to implement this correctly.
class TodoAdapter(private val tasks : List<TodoItem>) : RecyclerView.Adapter<TodoAdapter.ViewHolder>() {
inner class ViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
var editText = view.findViewById<EditText>(R.id.edit_text)
init {
editText.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(s: Editable?) {
tasks[adapterPosition].task = s.toString()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.todo_item,parent,false)
return ViewHolder(view)
}
override fun getItemCount() = tasks.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = tasks[position]
holder.editText.setText(item.task)
}
}
class TodoItem(var task: String = "")
editText.addTextChangedListener(object TextWatcher() {
override fun afterTextChanged(s : Editable) {
item.textFieldValue = s.toString()
}
});
Put textChangeListener inside init block this should work
#MuhammadAhmed
Data class
class todolist(var hours: String, var task: String)
Adapter
override fun onBindViewHolder(holder: TodoAdapter.TodoViewHolder, position: Int) {
val currentItem = todo[position]
holder.todo.text = currentItem.hours
holder.task.text = currentItem.task
holder.task.addTextChangedListener(object: TextWatcher{
override fun afterTextChanged(s: Editable?) {
currentItem.task = s.toString()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
}
Similarly to Saving EditText content in RecyclerView, I have an implementation of a pair of EditTexts to fill inside every row of a RecyclerView. But my position variable inside my TextWatcher is always returning 0, even after the updatePosition function been called.
the Lod.d at afterTextChanged() on both Watchers always shows that position is 0, even after filling the editText at the 3rd position.
I can see that updatePosition goes from 0 to areasimportadas.size during the onBindViewHolder function, but it doesn't happen for onTextChanged.
class importItemsAdapter(val areasImportadas: MutableList, val inclinaLida: MutableList, val desvLido: MutableList) : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.import_items,parent,false)
return CustomViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return areasImportadas.size
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.itemView.importNum.text = (position+1).toString()
holder.itemView.importArea.text = areasImportadas[position]
Watcher1().updatePosition(holder.adapterPosition)
Watcher2().updatePosition(holder.adapterPosition)
holder.itemView.importInclina.addTextChangedListener(Watcher1())
holder.itemView.importDesv.addTextChangedListener(Watcher2())
}
class CustomViewHolder(view: View): RecyclerView.ViewHolder(view)
private inner class Watcher1 : TextWatcher {
var position = 0
fun updatePosition(positionExt: Int) {
this.position = positionExt
Log.d("Registro", this.position.toString())
}
override fun afterTextChanged(arg0: Editable) {
inclinaLida[position]= arg0.toString()
Log.d("Registro", "Inclinação $position: ${inclinaLida[position]}")
}
override fun beforeTextChanged(arg0: CharSequence, arg1: Int, arg2: Int, arg3: Int) {}
override fun onTextChanged(s: CharSequence, a: Int, b: Int, c: Int) {
}
}
private inner class Watcher2 : TextWatcher {
var position = 0
fun updatePosition(positionExt: Int) {
position = positionExt
Log.d("Registro", position.toString())
}
override fun afterTextChanged(arg0: Editable) {
Log.d("Registro", "Desvio ${position}: ${desvLido[position]}")
}
override fun beforeTextChanged(arg0: CharSequence, arg1: Int, arg2: Int, arg3: Int) {
}
override fun onTextChanged(s: CharSequence, a: Int, b: Int, c: Int) {
desvLido[position]=s.toString()
}
}
}
I needed to be able to store each EditText content under its proper position on both lists (desvLido and inclinaLida)
What was missing was calling an instance:
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.itemView.importNum.text = (position+1).toString()
holder.itemView.importArea.text = areasImportadas[position]
val watcher1 = Watcher1()
watcher1.updatePosition(holder.adapterPosition)
holder.itemView.importInclina.addTextChangedListener(watcher1)
}
Now position returns the correct value.