我正在构建一个应用程序,该应用程序需要用户填充一些数据才能发布内容,因此一个片段由编辑文本,单选按钮和微调框以及recyclerview组成,后者动态呈现大量包含TextView和editText的子布局。 因此,当用户从微调器中选择类别时,与该类别相关的某些属性会显示在RecyclerView中,用户可以选择填充其中的一些属性。 我尝试使用回调和TextWatcher实现此功能,但是我没有得到想要的值。
回叫
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吗?还是应该修复哪一部分才能使其正常工作?
答案 0 :(得分:3)
我不完全了解您要在此处实现的目标。由于以下原因,RecyclerView中的EditText通常不是一个好主意。
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
}