How to detect when user's done entering the value to EditText in RecyclerView?

Viewed 770

enter image description here

I have a recyclerview which contains EditTexts. How can I get the value of each edittext only after the user is done from entering the amount in any of the edit texts so i can update the total amount.

What I'm trying to achieve is adding the values of the edittexts and send it to the Activity. In the Activity I have the Proceed button (where i will perform some validations on the total amount) and a total amount TextView (retrieved from the recyclerview adapter using a listener).

I tried to use setOnEditorActionListener but this will not help if the user clicked on back button instead of hitting the enter.

Also, I tried to use the focus change listener but the problem is that the EditText never loses focus even when clicking outside in the page.

And of course, the TextWatcher is not an ideal solution as it could be very expensive in OnBindViewHolder.

I need to ensure that whenever the user clicks on Proceed Button, the total amount is updated before.

1 Answers

Idea

Best way is to add two communications (via interfaces):

  • FIRST between Activity and Adapter

  • SECOND between Adapter and single ViewHolder

Affter adding such type of communication your can calculate sum "live".

Solution

Step #1

Create FIRST interface, for example:

interface AdapterContentChanged {
    fun valuesChanged()
}

and implement it in your Activity or create new variable (as anonymous class).

Step #2

Pass your activity (or instance of above interface) when you are creating adapter, for example:

private val ownAdapter = OwnAdapter(
    items,    // elements inside list
    this      // interface implementation
)

Step 3

Create SECOND interface, for example:

interface OwnViewHolderTextChanged {
    fun onTextChanged(position: Int, newValue: Int)
}

and implement it in your adapter or create new variable (anonymous class) - same like in step #1.

Step 4

Pass your adapter (or variable) and position (of the item) when you are binding viewHolder, for example:

override fun onBindViewHolder(holder: OwnViewHolder, position: Int) {
    val number = list[position]
    holder.bind(number, position, this)
}

Step 5

In bind() method (from above example), add new TextWatcher to your EditText.

In afterTextChanged() method (from TextWatcher) call method from interface and pass new value. For example:

fun bind(
    // TODO - add here more information which you need,
    position: Int,
    listener: OwnViewHolderTextChanged
) {
    itemView.edit_text.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
            // Get EditText content
            val newValue = getNumber()

            // Call method from interface
            listener.onTextChanged(position = position, newValue = newValue)
        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            // Not used
        }

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

    })
}

To calculate value from EditText you can use something like this:

private fun getNumber(): Int =
    try {
        itemView.edit_text.text.toString().toInt()
    } catch (e: Exception) {
        0
    }

Step 6

When text was changed inside method you have to:

  • update content of the list

  • notify adapter that "something was changed"

For example:

override fun onTextChanged(position: Int, newValue: Int) {
    list[position] = newValue
    listener.valuesChanged()
}

Step 7

When something changed (and Activty will know about that), you can calculate new sum:

override fun valuesChanged() {
    val sum: Int = ownAdapter.getCurrentSum()
    text_view.text = "Sum:  $sum"
}

Demo

demo

Related