从RecyclerView获取EditTexts值

Nux*_*Nux 5 android kotlin android-recyclerview

我正在构建一个应用程序,在该应用程序中,用户需要填写一些数据才能发布内容,因此一个片段由个EditText单选按钮组成,并Spinner与之一起RecyclerView动态呈现多个包含TextView和的子布局EditText

因此,当用户从中选择类别时Spinner,将显示一些与该类别相关的属性,RecyclerView用户可以选择填充其中的一些属性。

我尝试使用回调实现此功能,TextWatcher但是我没有得到想要的值。

打回来

interface PropertiesCallback {
    fun addProp(position: Int, title: String, value: String)
}
Run Code Online (Sandbox Code Playgroud)

适配器

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())
                }
            })
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

片段中

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<*>) {}
        }
    }
Run Code Online (Sandbox Code Playgroud)

覆写

 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()}")
    }
Run Code Online (Sandbox Code Playgroud)

提交乐趣

    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()} ")
    }
}
Run Code Online (Sandbox Code Playgroud)

以上代码旨在作为。当用户在中的一个输入值时EditTextRecyclerView该值将被分段,并添加到具有标题和值的对象中,然后添加到中propertiesInputList

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

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

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

有更好的解决方案来解决这个问题吗?就像recyclerView只在用户按Submit而不是TextWatcher?时循环播放?或者我应该修复哪一部分才能完成这项工作?

Fro*_*oyo 4

我不完全理解你想在这里实现什么目标。由于以下原因,RecyclerView 中的 EditText 通常不是一个好主意。

  1. 当 recyclerView 滚动时,您可能希望保留用户为该特定字段/项目添加的文本,并在用户向后滚动时正确显示它。
  2. 当您向 EditText 添加 a 时TextWatcher,您还需要在视图回收或视图持有者再次绑定时将其删除。否则,你最终会遇到多个听众,事情就会出错。

对于您提出的另一个问题,

但现在我只得到用户类型的第一个字母。如果用户输入鞋子,我会得到 s

这是设计使然。每次输入字符时,TextWatcher 都会发出事件。所以你会得到 s、sh、sho、shoe、shoes。因此,您无法对此数据执行操作,因为用户仍在向该字段添加内容。


所以,

  • 您不知道用户何时停止向 EditText 添加文本(或者用户是否完成)。你可以使用类似的东西debounce,但这很复杂。您应该给用户一个按钮。当用户点击按钮时获取该值。

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

  • 您还可以使用 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
     }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想说,使用 `ScrollView` 并根据您的需要添加 EditText。例如。如果需要5个编辑文本,则创建5个新视图并手动添加到ScrollView的容器中。如果数量发生变化,添加/删除 EditText, (2认同)