title: Getting EditTexts Values From RecyclerView
body: <p>I am building an app where user is required to fill some data in order to post something, so a fragment consists of <code>EditText</code>, radio buttons and <code>Spinner</code> along with <code>RecyclerView</code> which dynamically renders a number of child layout containing <code>TextView</code> and <code>EditText</code>. </p>
  
  <p>So when user select category from <code>Spinner</code>, some properties which are related to that category are displayed in <code>RecyclerView</code> and user can optionally fill some of them.</p>
  
  <p>I have tried to implement this functionality using callback and <code>TextWatcher</code> but I don't get the values I want.</p>
  
  <p><strong>CallBack</strong></p>
  
  <pre><code>interface PropertiesCallback {
      fun addProp(position: Int, title: String, value: String)
  }
  </code></pre>
  
  <p><strong>Adapter</strong></p>
  
  <pre><code>class PropertiesAdapter(private val propertiesCallback: PropertiesCallback)
      : RecyclerView.Adapter&lt;PropertiesAdapter.ViewHolder&gt;() {
  
      private var list = listOf&lt;CategoriesAndSubcategoriesQuery.Property&gt;()
  
      fun setData(listOfProps: List&lt;CategoriesAndSubcategoriesQuery.Property&gt;) {
          this.list = listOfProps
          notifyDataSetChanged()
      }
  
      override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
          val view = LayoutInflater.from(parent.context)
                  .inflate(R.layout.z_property_input, parent, false)
          return ViewHolder(view)
      }
  
      override fun getItemCount(): Int = list.size
  
      override fun onBindViewHolder(holder: ViewHolder, position: Int) {
          holder.bind(list[position], position)
      }
  
      inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
          private val label: TextView = view.findViewById(R.id.label)
          private val input: EditText = view.findViewById(R.id.input)
  
          fun bind(prop: CategoriesAndSubcategoriesQuery.Property, position: Int) {
              label.text = prop.title()
  
              prop.hint()?.let { input.hint = prop.hint() }
  
              input.addTextChangedListener(object : TextWatcher {
                  override fun afterTextChanged(s: Editable?) {}
  
                  override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
  
                  override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                      propertiesCallback.addProp(position, prop.title(), input.text.toString())
                  }
              })
          }
      }
  }
  </code></pre>
  
  <p>In Fragment </p>
  
  <pre><code>private var propertiesList = mutableListOf&lt;CategoriesAndSubcategoriesQuery.Property&gt;()
  private var propertiesInputList = mutableListOf&lt;ProductPropertiesInput&gt;()
  
  
  
    private fun setUpSubcategorySpinner() {
          subcategoriesAdapter = ArrayAdapter(
                  this@AddProductFragment.context!!,
                  android.R.layout.simple_spinner_item,
                  subcategoriesList
          )
          //Subcategories
          subcategoriesAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line)
  
          subcategory_spinner.adapter = subcategoriesAdapter
  
          subcategory_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
              override fun onItemSelected(parent: AdapterView&lt;*&gt;, view: View, position: Int, id: Long) {
                  subcategoryId = subcategoriesList[position].id()
  
                  //Adding properties
                  subcategoriesList[position].properties()?.let {
                      //Clear previous properties data of another subcategory.
                      propertiesInputList.clear()
                      propertiesList.clear()
  
                      propertiesList.addAll(it)
                      propertiesAdapter.setData(propertiesList)
                      propertiesAdapter.notifyDataSetChanged()
                  }
              }
  
              override fun onNothingSelected(parent: AdapterView&lt;*&gt;) {}
          }
      }
  </code></pre>
  
  <p>overide</p>
  
  <pre><code> override fun addProp(position: Int, title: String, value: String) {
          val prop = ProductPropertiesInput
                  .builder()
                  .title(title)
                  .value(value)
                  .build()
  
          propertiesInputList.add(prop)
  
          //Log.d(TAG, "prop: ${prop.title()} : ${prop.value()}")
      }
  </code></pre>
  
  <p>submit fun</p>
  
  <pre><code>    private fun submitProduct() {
          //Initializing properties.
          val properties: Any
  
          //The keys needed in final list.
          val propertyKeys = propertiesList.map { it.title() }
  
          //Removing objects which keys are not needed.
          propertiesInputList.removeAll { it.title() !in propertyKeys }
  
          Log.d(TAG, "propertiesInputList: $propertiesInputList")
  
          //Removing duplicate and assign result in properties var.
          properties = propertiesInputList
                  .distinctBy { it.title() }
  
          Log.d(TAG, "properties: $properties")
  
          for (prop in properties) {
          Log.d(TAG, "properties , title: ${prop.title()}, value: ${prop.value()} ")
      }
  }
  </code></pre>
  
  <p>Above codes is intended to work as. When user types a value in one of the <code>EditText</code> in <code>RecyclerView</code> the value will be taken to fragment and added to an object which takes title and value and then added to <code>propertiesInputList</code>.</p>
  
  <p><strong>Problem 1:</strong> <code>propertiesInputList</code> will have so many duplicates objects with the same title and I thought the best solution was using <code>distinctBy</code>.</p>
  
  <p><strong>Problem 2:</strong> When user fills a number of <code>EditText</code> which are related to let's say <code>category1</code> and changes his mind and select another category from <code>Spinner</code>. The previous values which are <strong>not part of new chosen category</strong> remain in <code>propertiesInputList</code> list. So I thought the best solution was to clear <code>propertiesInputList</code> and using <code>removeAll</code> with the titles related to category to filter unwanted objects.</p>
  
  <p>But now I get only the first letter user types. If user types <strong>shoes</strong> I get <strong>s</strong>. So it seems <code>distinctBy</code> returns the first object but I want to get exactly last word user typed and if the user typed and erased everything I want blank.</p>
  
  <p>Is there a better solution to handle this? Like looping <code>recyclerView</code> only when user press submit instead of <code>TextWatcher</code>? Or which part should I fix to make this work?</p>