2

I have a problem with addTextChangedListener. addTextChangedListener is triggered immediately when I launch the app (after touch the screen).

addTextChangedListener is in a ViewHolder Class in a recyclerview with a custom adapter.

I have tried to move both setOnClickListener1() and setOnClickListener2() from onBindViewHolder to onCreateViewHolder but it is worst (triggered immediately after launching the app).

here is my adapter code

import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

open class CustomAdapter(private val data: DataClass, private val activity: MainActivity) :
        RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    init {
        setHasStableIds(true)
    }


    override fun onCreateViewHolder(
            parent: ViewGroup,
            viewType: Int
    ): ViewHolder {

        val rowItem: View =
                LayoutInflater.from(parent.context).inflate(com.example.essai2.R.layout.list_item_view, parent, false)

        var myview= ViewHolder(rowItem)

      //myview.setOnClickListener1()
      //myview.setOnClickListener2()

        return myview


    }

    override fun onBindViewHolder(
            holder: ViewHolder,
            position: Int
    ) {

        holder.editText.setText(data.list1[position])
        holder.editText2.setText(data.list2[position])
        holder.textview.setText(data.list3[position])
        holder.setOnClickListener1()
        holder.setOnClickListener2()
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

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

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

        val textview : TextView = rowItem.findViewById(com.example.essai2.R.id.textview)
        val editText : EditText= rowItem.findViewById(com.example.essai2.R.id.editText)
        val editText2 : EditText= rowItem.findViewById(com.example.essai2.R.id.editText2)


        fun fonction3(position: Int)
        {
              if (position >=1 && position <=98) {
                  data.list3[position] = (0.5 * (data.list2[position - 1].toDouble() + data.list2[position + 1].toDouble())).toString()
                  //activity.Update()
              }
        }

        fun setOnClickListener1() {

            editText.addTextChangedListener(object : TextWatcher {

                override fun afterTextChanged(p0: Editable?) {
                   data.list1[getAdapterPosition()]= editText.text.toString()
                  // activity.fonction()
                }
                override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
                }
                override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                }
            })
        }

        fun setOnClickListener2() {
            editText2.addTextChangedListener(object : TextWatcher {

                override fun afterTextChanged(p0: Editable?) {
                    data.list2[getAdapterPosition()] = editText2.text.toString()
                    activity.fonction2()
                    fonction3(getAdapterPosition())

                }
                override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
                }
                override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                }
            })
        }
    }

}

here is my activity code

import android.os.Bundle
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.layoutManager = LinearLayoutManager(this)

        recyclerView.adapter = CustomAdapter(DataClass(),this)

        recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))

        var textView2: TextView
        textView2 = findViewById(R.id.textView)
        var editText : EditText
    }

    fun Update()
    {
       // findViewById<RecyclerView>(R.id.recycler_view).getAdapter()?.notifyDataSetChanged()
        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.adapter=CustomAdapter(DataClass(),this)
        (recyclerView.adapter as CustomAdapter).notifyDataSetChanged()
    }


    fun fonction ()
    {
        var textView2: TextView
        textView2 = findViewById(R.id.textView)
        textView2.text="!!!!!!"
    }


    fun fonction2 ()
    {
        var textView2: TextView
        textView2 = findViewById(R.id.textView)
        textView2.text="?????"
    }

    private fun generateData(): List<String> {
        val data: MutableList<String> = ArrayList()
        for (i in 0..99) {
            data.add(i.toString() + "th Element")
        }
        return data
    }
}

class DataClass(){
    var list1: MutableList<String> = generateData2() as MutableList<String>
    var list2: MutableList<String> = generateData3() as MutableList<String>
    var list3: MutableList<String> = generateData4() as MutableList<String>

    private fun generateData2(): List<String> {
        val data: MutableList<String> = mutableListOf()
        for (i in 0..99) {
            data.add(i.toString())
        }
        return data
    }

    private fun generateData3(): List<String> {
        val data: MutableList<String> =mutableListOf()
        for (i in 0..99) {
            data.add((10*i).toString())
        }
        return data
    }

    private fun generateData4(): List<String> {
        val data: MutableList<String> = mutableListOf()
        for (i in 0..99) {
            data.add("none")
        }

        return data
    }

}

Thanks in advance for your help

EDIT : here is the list_item_view.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        app:layout_constraintBottom_toBottomOf="parent"

        tools:ignore="MissingConstraints">

        <TextView
            android:id="@+id/textview2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="TextView"
            android:textSize="16dp" />

        <TextView
            android:id="@+id/textview"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:text="TextView"
            android:textSize="16dp" />


        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/editText2"
            android:layout_width="196dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:focusableInTouchMode="true"
            android:focusedByDefault="false"
            android:hint="hint" />

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/editText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:focusedByDefault="false"
            android:hint="hint" />

    </LinearLayout>



</androidx.constraintlayout.widget.ConstraintLayout>
jujuf1
  • 175
  • 2
  • 10

2 Answers2

0

you shouldn't addTextWatcher in onBindViewHolder because you add multiple listeners to a widget each time onBindViewHolder called.

set your listeners in onCreateViewHolder to add just one listener and then when you want setText to EditText you can setTag to view like

editText.setTag("system input");

and then in listener check your view's tag to determine it's user input or system input and if tag=="system input" do your work and finally remove tag

view.setTag(null)
saeedata
  • 901
  • 6
  • 14
  • Thanks a lot for your help. But I have no understand if it was for debugging my problem or to solve it (my skills in english and programming are poor). – jujuf1 Dec 21 '20 at 09:40
0

I just changed my answer based on our conversation below.

First of all, here is you XML file (I just changed the id of the views and nothing else):

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        tools:ignore="MissingConstraints">

        <TextView
            android:id="@+id/tv2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="TextView2"
            android:textSize="16dp" />

        <TextView
            android:id="@+id/tv1"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:text="TextView1"
            android:textSize="16dp" />
        
        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/et2"
            android:layout_width="196dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:focusableInTouchMode="true"
            android:focusedByDefault="false"
            android:hint="hint" />

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/et1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:focusedByDefault="false"
            android:hint="hint" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

And here is your adapter (I checked everything and there was no errors):

import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.list_item_view.view.*

class CustomAdapter(private val data : DataClass, private val context : Context) :
    RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    init {
        setHasStableIds(true)
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

        val rowItem: View = LayoutInflater.from(context).inflate(R.layout.list_item_view, parent, false)

        return ViewHolder(rowItem)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

        holder.itemView.tv1.setText(data.list3[position])

        holder.itemView.et1.setText(data.list1[position])
        holder.itemView.et2.setText(data.list2[position])

        holder.itemView.et1.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(p0: Editable?) {
                data.list1[position]= holder.itemView.et1.text.toString()
                // activity.fonction()
            }
            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }
        })
        holder.itemView.et2.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(p0: Editable?) {
                data.list2[position] = holder.itemView.et2.text.toString()

                //this is instead of `fonction2()`
                holder.itemView.tv2.text = "?????"
                //fonction2()

                fonction3(position)
            }

            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }
        })
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

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

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

    }

    fun fonction3(position: Int) {
        if (position in 1..98) {
            data.list3[position] = (0.5 * (data.list2[position - 1].toDouble() + data.list2[position + 1].toDouble())).toString()
            //activity.Update()
        }
    }
}
MehranB
  • 1,460
  • 2
  • 7
  • 17
  • Thanks a lot for your help. It works ! with some minor changes. (holder.editText without itemview.) Very nice ! – jujuf1 Dec 21 '20 at 09:41
  • no worries. I'm glad I could help. But I still believe you need to add `rowItem` instead of `itemView` if you are writing the code inside the `onBindViewHolder` block. – MehranB Dec 21 '20 at 09:46
  • In fact, i have a new problem. The order of the numbers is not respected anymore when i scroll down and next i scroll up. Else i have tried to add rowItem but it is not known by android studio. – jujuf1 Dec 21 '20 at 09:55
  • What do you mean by "the order of the numbers"? Can you share a screenshot of the dissatisfactory result? – MehranB Dec 21 '20 at 09:59
  • here is the screenshot : https://ibb.co/2yp85cQ another problem is that the calculus in the first column had been triggered without change the number is the second column. Thx a lot. – jujuf1 Dec 21 '20 at 10:05
  • @jujuf1 Just edited my answer. This is the right way to do it. your changeListener is probably triggered because your re assigning one listener to all the EditTexts which means when one EditText is changed, all the EditTexts do the calculations. – MehranB Dec 21 '20 at 10:23
  • thx. I have tried your solution but rowItem is not available in onBindViewHolder. The same for getAdapterPosition or other similars functions. I have for instance tried to find out other solutions by trying something (by putting all the code in an "init" in the viewholder but the listener is launched immediately and for all textview (all the rows). I wonder finally if that I want to achieve with android is possible ? With the low code Microsoft PowerApps solution, it is possible and i can't believe that simple things like that is not achievable with android ... :( – jujuf1 Dec 21 '20 at 11:04
  • Just copied your exact adapter to a new project in my IDE and made the changes I have suggested in my answer. it works fine. `itemView` is available. You are making a mistake in implementing my answer. your variable naming might be confusing you and the IDE. you have named everything editText and textView. try using different names for your xml views and your variables in your code. – MehranB Dec 21 '20 at 11:23
  • I am making probably some mistakes indeed. When I copy your code, itemView exists but after i cannot access to views. getAdapterPosition() is not available too. Could you share your ViewHolder class please, or the custom adapter ? Because by applying your suggestions, my Viewholder Class is empty. It must be wrong. – jujuf1 Dec 21 '20 at 11:58
  • You don't have to have code in your ViewHolder block. As for the `getAdapterposition()`, it was my bad. just edited it in the answer. you have to use `position` instead. – MehranB Dec 21 '20 at 12:06
  • Thx for your availability. Here is my code and the errors that I have in Android Studio : https://ibb.co/G3JMDV1 – jujuf1 Dec 21 '20 at 12:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/226237/discussion-between-mehran-behbahani-and-jujuf1). – MehranB Dec 21 '20 at 12:19
  • @jujuf1 Just added the answer with absolutely no errors. just copy and paste and it should work. – MehranB Dec 21 '20 at 16:40
  • hi :) So i have put the code both xml and class file but there is two errors : 1) the same than before : view aren't recognized 2) import kotlinx is not recognized – jujuf1 Dec 21 '20 at 16:53
  • Then [this](https://stackoverflow.com/a/34173727/10695663) will solve your problem. – MehranB Dec 21 '20 at 16:56
  • Thx. This problem has been fixed and i can run the app. But there is no calculation but the worst thing is that there is a problem of order of item : there is a mix. Thx a lot for your help i have learned some things. Finally, I think recyclerview is a bad idea in order to do what I want to do. I give up. I am thinking about listview. It seems more adapted. thx a lot again. – jujuf1 Dec 21 '20 at 17:12