从RecyclerView中获取EditTexts值

时间:2019-05-22 22:43:26

标签: android kotlin android-recyclerview

我正在构建一个应用程序,该应用程序需要用户填充一些数据才能发布内容,因此一个片段由编辑文本,单选按钮和微调框以及recyclerview组成,后者动态呈现大量包含TextView和editText的子布局。 因此,当用户从微调器中选择类别时,与该类别相关的某些属性会显示在RecyclerView中,用户可以选择填充其中的一些属性。 我尝试使用回调和Tex​​tWatcher实现此功能,但是我没有得到想要的值。

回叫

interface PropertiesCallback {
    fun addProp(position: Int, title: String, value: String)
}

适配器

class PropertiesAdapter(private val propertiesCallback: PropertiesCallback)
    : RecyclerView.Adapter<PropertiesAdapter.ViewHolder>() {

    private var list = listOf<CategoriesAndSubcategoriesQuery.Property>()

    fun setData(listOfProps: List<CategoriesAndSubcategoriesQuery.Property>) {
        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())
                }
            })
        }
    }
}

片段

private var propertiesList = mutableListOf<CategoriesAndSubcategoriesQuery.Property>()
private var propertiesInputList = mutableListOf<ProductPropertiesInput>()



  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<*>, 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<*>) {}
        }
    }

覆盖

 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()}")
    }

提交乐趣

    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()} ")
    }
}

以上代码旨在用作。当用户在recyclerView中的editText之一中键入值时,该值将被分段并添加到带有标题和值的对象中,然后添加到 propertiesInputList

问题1: propertiesInputList 将有很多具有相同标题的重复对象,我认为最好的解决方法是使用 distinctBy

问题2::当用户填写许多与 category1 有关的编辑文本时,他改变了主意,并从微调器中选择了另一个类别。 不属于新选择的类别的先前值保留在propertiesInputList列表中。因此,我认为最好的解决方案是清除propertiesInputList,并使用与类别相关的标题的removeAll来过滤不需要的对象。

但是现在我只得到首字母用户类型。如果用户键入鞋子,我会得到 s 。因此,似乎distinctBy返回了第一个对象,但我想得到用户键入的最后一个单词,如果用户键入并擦除了我想要的所有内容,则为空。

是否有更好的解决方案来解决此问题?像仅当用户按下提交而不是textwatcher时循环recyclerView吗?还是应该修复哪一部分才能使其正常工作?

3 个答案:

答案 0 :(得分:3)

我不完全了解您要在此处实现的目标。由于以下原因,RecyclerView中的EditText通常不是一个好主意。

  1. 滚动recyclerView时,您想保留 用户为该特定字段/项目添加的文本并显示 用户向后滚动时正确显示。
  2. TextWatcher添加到EditText时,还需要在回收视图或重新绑定视图持有者时将其删除。否则,您将获得多个侦听器,并且会出错。

对于其他问题,

  

但是现在我只得到首字母用户类型。如果用户键入鞋子,我会得到

那是设计使然。每当输入一个字符时,TextWatcher都会发出事件。这样您就会得到s,sh,sho,shoe,shoes。因此,您无法对此数据采取任何措施,因为用户仍在向该字段中添加内容。


所以

  • 您不知道用户何时停止将文本添加到EditText(或是否完成用户操作)。您可以使用类似debounce的方法,但这很复杂。您应该给用户一个按钮。用户点击按钮时取值。

  • 我假设您在RecyclerView中有多个编辑文本。因此,您将需要存储每个edittext的值,因为recyclerview将重新使用视图,并且您将丢失数据。您可以在适配器的onViewRecycled回调中执行此操作。保留一个id->字符串映射,用于存储此数据并在绑定视图持有者时检索。

  • 您还可以使用TextWatcher,但是在附加新的文本监视器或在onViewRecycled中附加它之前,应先将其分离。

更新:

如果我有类似的内容,我将使用带有垂直LinearLayout的ScrollView(为简单起见),并根据要求添加EditText。如果要添加TextWatcher,则需要某种稳定的ID。

class EditTextContainer : LinearLayout {

    private val views = mutableListOf<EditText>()
    private val textWatchers = hashMapOf<Int, TextWatcher>()

    ... constructor and bunch of stuff

    fun updateViews(items: List<Item>, textCallback: (id, text) -> Unit) {
        // Remove text watchers
        views.forEach { view ->
             view.removeTextWatcher(textWatchers[view.id])
        }

        // More views than required
        while (views.size > items.size) {
            val view = views.removeAt(views.size-1)
            removeView(view)
        }

        // Less views than required
        while (view.size < items.size) {
            val view = createView()
            view.id = View.generateViewId()
            addView(view, createParams()) // Add this view to the container
            views.add(view)
        }

        // Update the views
        items.forEachIndexed { index, item ->
            val editText = views[item]
            // Update your edittext.
             addTextWatcher(editText, item.id, textCallback)
        }
    }

     private fun createView(): EditText {
         // Create new view using inflater or just constructor and return
     }

     private fun createParams(): LayoutParams {
         // Create layout params for the new view
     }

     private fun addTextWatcher(view, itemId, textCallback) {
         val watcher = create text watcher where it invokes textCallback with itemId
         view.addTextWatcher(watcher)
         textWatchers[view.id] = watcher
     }
}

答案 1 :(得分:1)

您的投入较少以识别问题。我想您正在使用编辑文本列表来开发一些数据收集应用程序。 在回收商列表中使用编辑文本时出现问题。 当您向下滚动时,即使您的用户尚未填写,回收者视图中的底部编辑文本也会被已填充的编辑文本值填充。

作为解决方法,您可以创建最适合您的任何数据结构的稀疏数组,这些结构可以映射您的位置和价值 喜欢      mPropertyValue [] =新的字符串[LIST_SIZE]。 ,假设您的列表项的位置与数组的索引匹配。

尝试使用文本观察器的值更新索引      mPropertyValue [POSITION] = YOUR_EDIT_TEXT_VALUE

要初始化编辑文本时,请使用mPropertyValue [POSITION]

的值。

通过此方法,您始终可以确保您的编辑文本具有正确的值。

答案 2 :(得分:0)

我在Java代码中遇到这样的问题,这就是解决方法

for (int i = 0; i < list.size(); i++) {
(put her the getter and setter class) mylist = list.get(i);
//use the getter class to get values and save them or do what ever you want
}