Arfmann
Arfmann

Reputation: 353

kotlin - Check if editText content has been modifed by user or programmatically

I have 2 editTexts. When editText1 is modified, editText2 content will equal to editText1 * 5. When editText2 is modified, editText1 content will be equal to editText2 * 5.

Now, if I use addTextChangedListener() it goes in loops beacause editTexts content will change always causing a crash. I need to detect if editTexts are modified by user or programmatically.

Any idea?

If can help, here's my code:

     fun EditText.doubleValue() = text.toString().toDoubleOrNull() ?: 0.0

     editText1.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) {

               editText2.setText(editText1.doubleValue() * 5)
            }
        })



editText2.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) {

                   editText1.setText(editText2.doubleValue() * 5)
                }
            })

EDIT: SOLVED

I used if(editText.isFocused) to check if was changed by user

Upvotes: 1

Views: 4962

Answers (3)

forpas
forpas

Reputation: 164099

First of all, remove the function doubleValue(), it does not work (or at least I could not make it work).
Use 2 boolean variables from1 and from2, indicating that the change that is about to be done is from code and check their values inside the watchers.
Say that you type inside editText1.
The textwatcher of editText1 before changing the value of editText2 sets from1 = true.
When the text of editText2 changes, the textwatcher of editText2 checks the value of from1 and because it is true does nothing, only sets from1 = false.

var from1 = false;
var from2 = false;

editText1.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 (from2) {
            from2 = false
        } else {
            val value = editText1.text.toString().toDoubleOrNull() ?: 0.0
            from1 = true
            editText2.setText((value * 5).toString())
        }
    }
})

editText2.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 (from1) {
            from1 = false
        } else {
            val value = editText2.text.toString().toDoubleOrNull() ?: 0.0
            from2 = true
            editText1.setText((value * 5).toString())
        }
    }
})

Upvotes: 0

pdegand59
pdegand59

Reputation: 13029

You can save the TextWatchers in variables, remove them before programatically changing the content of each TextView then add them back again.

class MyActivity: AppCompatActivity() {

  private val textWatcher1 = 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) {
      updateEditText2()
    }
  }

  fun EditText.doubleValue() = text.toString().toDoubleOrNull() ?: 0.0

  private val textWatcher2 = 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) {
      updateEditText1()
    }
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    editText1.addTextChangedListener(textWatcher1)
    editText2.addTextChangedListener(textWatcher2)
  }


  private fun updateEditText1() {
    editText1.removeTextChangedListener(textWatcher1)
    editText1.setText(editText2.doubleValue() * 5)
    editText1.addTextChangedListener(textWatcher1)
  }

  private fun updateEditText2() {
    editText2.removeTextChangedListener(textWatcher2)
    editText2.setText(editText1.doubleValue() * 5)
    editText2.addTextChangedListener(textWatcher2)
  }
}

Upvotes: 5

Eric Martori
Eric Martori

Reputation: 2975

I think your best option would be to use the actionDone ime option. This way only when the user is done editing the field the other edit text will be updated. To do that you would need to add android:imeOptions="actionDone" to your editText xml el elements. Then in your code instead of listening for changes you would add an action listener:

editText1.setOnEditorActionListener() { v, actionId, event ->
      if(actionId == EditorInfo.IME_ACTION_DONE){
          editText2.setText(editText1.doubleValue() * 5)
          true
      } else {
          false
      }
}

Without knowing you use case I don't know if this solution will work for you. If you need to update the editTexts in real time maybe checking if the field is in focus could prevent the infinit loop:

 editText1.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(editText1.hasFocus()) {
             editText2.setText(editText1.doubleValue() * 5)
           }
        }
    })

Upvotes: 0

Related Questions