Android 从父recyclerview适配器添加数据并从子recyclerview适配器删除数据
我有一个嵌套的回收视图。父recyclerview适配器可以通过按钮将数据添加到子级,然后子recyclerview适配器可以通过按钮删除子级中的数据。预览如下图所示 添加数据似乎很好,但当删除数据时,我得到了超出范围的索引。我在添加或删除函数中调用了Android 从父recyclerview适配器添加数据并从子recyclerview适配器删除数据,android,kotlin,android-recyclerview,Android,Kotlin,Android Recyclerview,我有一个嵌套的回收视图。父recyclerview适配器可以通过按钮将数据添加到子级,然后子recyclerview适配器可以通过按钮删除子级中的数据。预览如下图所示 添加数据似乎很好,但当删除数据时,我得到了超出范围的索引。我在添加或删除函数中调用了notifyDataSetChanged() 我的父适配器代码: class FormPlanParentAdapter : RecyclerView.Adapter<FormPlanParentAdapter.ViewHolder
notifyDataSetChanged()
我的父适配器代码:
class FormPlanParentAdapter :
RecyclerView.Adapter<FormPlanParentAdapter.ViewHolder>() {
private val data: MutableList<MutableList<ItemPlan>> = mutableListOf()
private val viewPool = RecyclerView.RecycledViewPool()
fun setData(newData: List<MutableList<ItemPlan>>) {
data.clear()
data.addAll(newData)
notifyDataSetChanged()
}
fun getData(): String = Gson().toJson(data)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_plan_parent, parent, false)
)
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val listPlans = data[position]
val childLayoutManager = LinearLayoutManager(
holder.recyclerView.context,
LinearLayoutManager.VERTICAL,
false
)
val childAdapter = FormPlanChildAdapter(listPlans, object : PlanParentCallback {
override fun deletePlan(position: Int) {
listPlans.removeAt(position)
notifyDataSetChanged()
}
})
holder.recyclerView.apply {
layoutManager = childLayoutManager
adapter = childAdapter
setRecycledViewPool(viewPool)
}
holder.textView.text = "Hari ke ${position + 1}"
holder.imageButton.setOnClickListener {
listPlans.add(ItemPlan())
notifyDataSetChanged()
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recyclerView: RecyclerView = itemView.rv_plan_child
val textView: TextView = itemView.tv_days_of
val imageButton: ImageButton = itemView.ib_add
}
interface PlanParentCallback {
fun deletePlan(position: Int)
}
}
class FormPlanChildAdapter(
private val listPlans: List<ItemPlan>,
private val callback: FormPlanParentAdapter.PlanParentCallback
) :
RecyclerView.Adapter<FormPlanChildAdapter.ViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_plan_child, parent, false)
)
}
override fun getItemCount(): Int = listPlans.size
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val itemPlan = listPlans[position]
holder.etPlan.setText(itemPlan.plan)
holder.etPlan.doAfterTextChanged {
listPlans[position].plan = it.toString()
}
if (position == 0) {
holder.ibDelete.invisible()
} else {
holder.ibDelete.visible()
}
holder.ibDelete.setOnClickListener {
if (listPlans.size > 1) {
callback.deletePlan(position)
}
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val etPlan: EditText = itemView.et_plan
val ibDelete: ImageButton = itemView.ib_delete
}
}
类FormPlanParentAdapter:
RecyclerView.Adapter(){
私有val数据:MutableList=mutableListOf()
private val viewPool=RecyclerView.RecycledViewPool()
fun setData(新数据:列表){
data.clear()
data.addAll(newData)
notifyDataSetChanged()
}
fun getData():String=Gson().toJson(数据)
覆盖视图保持器(
父对象:视图组,
视图类型:Int
):视窗座{
返回视窗座(
LayoutFlater.from(parent.context)。充气(R.layout.item\u plan\u parent,parent,false)
)
}
重写getItemCount():Int=data.size
覆盖BindViewHolder(
持有者:视图持有者,
职位:Int
) {
val listPlans=数据[位置]
val childLayoutManager=LinearLayoutManager(
holder.recyclerView.context,
LinearLayoutManager.VERTICAL,
假的
)
val childAdapter=FormPlanChildAdapter(listPlans,对象:PlanParentCallback{
覆盖平面图(位置:Int){
listPlans.removeAt(位置)
notifyDataSetChanged()
}
})
holder.recyclerView.apply{
layoutManager=childLayoutManager
适配器=子适配器
setRecycledViewPool(viewPool)
}
holder.textView.text=“Hari ke${position+1}”
holder.imageButton.setOnClickListener{
添加(ItemPlan())
notifyDataSetChanged()
}
}
内部类ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
val recyclerView:recyclerView=itemView.rv\u计划\u子项
val textView:textView=itemView.tv\u天
val imageButton:imageButton=itemView.ib_添加
}
接口PlanParentCallback{
平面图(位置:Int)
}
}
我的子适配器代码:
class FormPlanParentAdapter :
RecyclerView.Adapter<FormPlanParentAdapter.ViewHolder>() {
private val data: MutableList<MutableList<ItemPlan>> = mutableListOf()
private val viewPool = RecyclerView.RecycledViewPool()
fun setData(newData: List<MutableList<ItemPlan>>) {
data.clear()
data.addAll(newData)
notifyDataSetChanged()
}
fun getData(): String = Gson().toJson(data)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_plan_parent, parent, false)
)
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val listPlans = data[position]
val childLayoutManager = LinearLayoutManager(
holder.recyclerView.context,
LinearLayoutManager.VERTICAL,
false
)
val childAdapter = FormPlanChildAdapter(listPlans, object : PlanParentCallback {
override fun deletePlan(position: Int) {
listPlans.removeAt(position)
notifyDataSetChanged()
}
})
holder.recyclerView.apply {
layoutManager = childLayoutManager
adapter = childAdapter
setRecycledViewPool(viewPool)
}
holder.textView.text = "Hari ke ${position + 1}"
holder.imageButton.setOnClickListener {
listPlans.add(ItemPlan())
notifyDataSetChanged()
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recyclerView: RecyclerView = itemView.rv_plan_child
val textView: TextView = itemView.tv_days_of
val imageButton: ImageButton = itemView.ib_add
}
interface PlanParentCallback {
fun deletePlan(position: Int)
}
}
class FormPlanChildAdapter(
private val listPlans: List<ItemPlan>,
private val callback: FormPlanParentAdapter.PlanParentCallback
) :
RecyclerView.Adapter<FormPlanChildAdapter.ViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_plan_child, parent, false)
)
}
override fun getItemCount(): Int = listPlans.size
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val itemPlan = listPlans[position]
holder.etPlan.setText(itemPlan.plan)
holder.etPlan.doAfterTextChanged {
listPlans[position].plan = it.toString()
}
if (position == 0) {
holder.ibDelete.invisible()
} else {
holder.ibDelete.visible()
}
holder.ibDelete.setOnClickListener {
if (listPlans.size > 1) {
callback.deletePlan(position)
}
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val etPlan: EditText = itemView.et_plan
val ibDelete: ImageButton = itemView.ib_delete
}
}
类FormPlanChildAdapter(
私有val列表计划:列表,
私有val回调:FormPlanParentAdapter.PlanParentCallback
) :
RecyclerView.Adapter(){
覆盖视图保持器(
父对象:视图组,
视图类型:Int
):视窗座{
返回视窗座(
LayoutFlater.from(parent.context).充气(R.layout.item\u plan\u child,parent,false)
)
}
重写getItemCount():Int=listPlans.size
覆盖BindViewHolder(
持有者:视图持有者,
职位:Int
) {
val itemPlan=listPlans[位置]
holder.etPlan.setText(itemPlan.plan)
holder.etPlan.doAfterTextChanged{
listPlans[position].plan=it.toString()
}
如果(位置==0){
holder.ibDelete.invisible()文件
}否则{
holder.ibDelete.visible()
}
holder.ibDelete.setOnClickListener{
如果(listPlans.size>1){
callback.deletePlan(位置)
}
}
}
内部类ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
val etPlan:EditText=itemView.et_计划
val ibDelete:ImageButton=itemView.ib_delete
}
}
感谢您的帮助。请尝试
listPlans.removeAt(position)
notifyItemRemoved(position)
而不是
listPlans.removeAt(position)
notifyDataSetChanged()
原因:
当数据集更改时,旧的TextWatcher
s和适配器位置会出现混乱
notifyDatasetChanged
触发onBindViewHolder
,然后EditText
的TextWatcher
由于此行而收到通知
holder.etPlan.setText(itemPlan.plan)
由于etPlan
已经有一个来自上一个绑定的活动TextWatcher
,因此它尝试用适配器的旧位置通知它
快速回答:
将TextWatcher
保留在ViewHolder
中,将其从onBindViewHolder
上的EditText
中删除,并在设置EditText
的值后再次将其设置为EditText
因此,将子适配器的onBindViewHolder更新为
覆盖BindViewHolder(
持有者:视图持有者,
职位:Int
) {
val itemPlan=listPlans[位置]
holder.etPlan.removeTextChangedListener(holder.textWatcher)
holder.etPlan.setText(itemPlan.plan)
holder.textWatcher=holder.etPlan.doaftertexchanged{
listPlans[position].plan=it.toString()
}
...
}
并更新其ViewHolder,如:
内部类ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
val etPlan:EditText=itemView.et_计划
val ibDelete:ImageButton=itemView.ib_delete
var textWatcher:textWatcher?=null
}
但是,由于性能问题,不建议在
onBindViewHolder
上设置侦听器。请查收
以下是另一个解决方法:
通过侦听器将所有TextWatcher
内容移动到Adapter
,并防止在不必要时通知TextWatcher
接口TextChangeListener{
有趣的onTextChange(文本:字符串,位置:Int)
}
类FormPlanChildAdapter(…):
RecyclerView.Adapter(),TextChangeListener{
覆盖BindViewHolder(
持有者:视图持有者,
职位:Int
) {
val itemPlan=listPlans[位置]
固定平面图(一)