Search code examples
android-recyclerviewandroidx

How to get a list of items from user using RecyclerView


So, the problem statement is I want to implement a feature in which user can add and remove as many items in the list as they want. I thought of using RecyclerView with edittext in each view and handling the state of all Views and saving the data as the data state changes.

So far I have tried this in the CheckListAdapter:

import android.text.Editable
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.RecyclerView
import com.app.dynamicrv.databinding.CheckListItemBinding

class CheckListAdapter
     : RecyclerView.Adapter<CheckListAdapter.ViewHolder>() {

     private val checkListItems = arrayListOf("")

    inner class ViewHolder(val binding: CheckListItemBinding) :
        RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            CheckListItemBinding.inflate(
                LayoutInflater.from(parent.context),
                parent, false
            )
        )
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val index = checkListItems[position]

        holder.binding.checkListEt.setText(index)

        holder.binding.checkListEt.addTextChangedListener {text: Editable? ->
            if ( position < checkListItems.size)
                checkListItems[position] = text.toString()
        }

        holder.binding.checkListEt.setOnEditorActionListener { v, actionId, event ->
            if (actionId == EditorInfo.IME_ACTION_DONE){
                checkListItems.add("")
                holder.binding.checkListEt.setSelection(0)
                
                notifyItemInserted(checkListItems.size)
            }
            true
        }

    }

    override fun getItemCount() = checkListItems.size


    fun getListItems() : List<String>{
        return checkListItems
    }
}

The result I was expecting that user can enter another field in the recyclerView by actionDone. But It doesn't work as expected.


Solution

  • I tried a lot of approaches, but this one is best among them along with error handling. But still you can enhance if you think there is something that can be improved.

    My CheckListAdapter.kt looks like this:

    package com.app.dynamicrv.adapters
    
    import android.content.Context
    import android.text.Editable
    import android.text.TextWatcher
    import android.view.LayoutInflater
    import android.view.ViewGroup
    import android.view.inputmethod.EditorInfo
    import android.widget.EditText
    import android.widget.Toast
    import androidx.core.widget.addTextChangedListener
    import androidx.recyclerview.widget.RecyclerView
    import com.app.dynamicrv.R
    import com.app.dynamicrv.databinding.CheckListItemBinding
    
    class CheckListAdapter
        (private val context : Context,
         private val recyclerView: RecyclerView )
         : RecyclerView.Adapter<CheckListAdapter.ViewHolder>() {
    
         private val checkListItems = arrayListOf("")
    
        inner class ViewHolder(val binding: CheckListItemBinding) :
            RecyclerView.ViewHolder(binding.root)
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            return ViewHolder(
                CheckListItemBinding.inflate(
                    LayoutInflater.from(parent.context),
                    parent, false
                )
            )
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val index = checkListItems[position]
    
            holder.binding.checkListEt.setText(index)
    
            val pos = holder.adapterPosition
            // Add TextWatcher for EditText changes
            holder.binding.checkListEt.addTextChangedListener { text: Editable? ->
                if (position < checkListItems.size)
                    checkListItems[position] = text.toString()
            }
    
            // Ensure focus and cursor position for the last item
            if (pos == checkListItems.size - 1) {
                holder.binding.checkListEt.requestFocus()
                holder.binding.checkListEt.setSelection(holder.binding.checkListEt.text.length) // Move cursor to end
            }
    
            // Handle IME_ACTION_DONE to add a new item
            holder.binding.checkListEt.setOnEditorActionListener { _, actionId, _ ->
                if (actionId == EditorInfo.IME_ACTION_DONE) {
                    if (checkListItems.size > 0) {
                        checkListItems.add("") // Add new empty item
                        notifyItemInserted(checkListItems.size - 1)
    
                        // Scroll to the newly added item
                        holder.itemView.post {
                            recyclerView.scrollToPosition(checkListItems.size - 1)
                        }
                    }
                    true
                } else {
                    false
                }
            }
    
            // Handle item deletion
            holder.binding.deleteItem.setOnClickListener {
                if (checkListItems.size > 1 && pos in checkListItems.indices) {
                    checkListItems.removeAt(pos)
                    notifyItemRemoved(pos)
                    notifyItemRangeChanged(pos, checkListItems.size)
    
                    // Optionally, focus and scroll to the previous item or the new last item
                    if (pos < checkListItems.size) {
                        holder.itemView.post {
                            recyclerView.scrollToPosition(pos)
                            if (checkListItems.size > 0) {
                                recyclerView.findViewHolderForAdapterPosition(pos)?.itemView?.findViewById<EditText>(
                                    R.id.checkListEt)?.requestFocus()
                            }
                        }
                    }
                } else {
                    Toast.makeText(context, "Must have at least 1 item", Toast.LENGTH_SHORT).show()
                }
            }
        }
    
    
        override fun getItemCount() = checkListItems.size
    
    
        fun getListItems() : List<String>{
            return checkListItems
        }
    }