我发现了有关此异常的其他几个问题,但没有一个问题对我有用。
这是我的完整代码:
class MainActivity : AppCompatActivity() {
private lateinit var listView: ListView
private lateinit var addItem: ImageButton
private lateinit var adapter: MyAdapter
private val names = arrayListOf("Person 1", "Person 2", "Person 3", "Person 4", "Person 5",
"Person 6", "Person 7", "Person 8", "Person 9", "Person 10")
private val descriptions = arrayListOf("Actor", "Activist", "Athlete", "Scientist", "Astronaut",
"Engineer", "Software Developer", "Politician", "Soldier", "Shopkeeper")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
listView = list_view as ListView
addItem = addButton as ImageButton
adapter = MyAdapter(this, names, descriptions)
listView.adapter = adapter
addItem.setOnClickListener {
names.add("Person 11")
descriptions.add("Mortician")
adapter.notifyDataSetChanged()
Toast.makeText(this, "Button Clicked", Toast.LENGTH_LONG).show()
}
}
}
private class MyAdapter(val context: Context, val names: ArrayList<String>,
val descriptions: ArrayList<String>) : BaseAdapter() {
override fun getCount(): Int {
return names.size
}
override fun getViewTypeCount(): Int {
return count
}
override fun getItemViewType(position: Int): Int {
return position
}
override fun getItem(position: Int): Any {
return position
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val viewHolder: ViewHolder?
return if (convertView == null) {
val convertViewMutable = LayoutInflater.from(context).inflate(R.layout.activity_custom, parent, false)
val checkBox: CheckBox = convertViewMutable.findViewById(R.id.checkBox_name) as CheckBox
val desc: TextView = convertViewMutable.findViewById(R.id.textView_desc) as TextView
viewHolder = ViewHolder(checkBox, desc)
convertViewMutable.tag = viewHolder
checkBox.text = names[position]
desc.text = descriptions[position]
convertViewMutable
} else
convertView
}
}
private data class ViewHolder(private val checkBox: CheckBox, private val textView: TextView)
当我运行程序时,列表看起来很好。当我单击按钮添加新名称和描述时,由于我看到Toast出现,所以它将运行侦听器中的所有代码。然后,我可以向下滚动并在列表底部看到新项目。但是,当我向上滚动时会引发错误。
我看到有人回答,说您不应覆盖getViewTypeCount()和getItemViewType(),但我只是覆盖它们,因为如果我将它们取出,则当我滚动它们时,列表中的项目将重复。也有回答说发生错误是因为getItemViewType()返回的数字大于getViewTypeCount(),但他们没有提到如何解决。
答案 0 :(得分:3)
之所以发生这种情况,是因为您使用ViewHolder的方式不正确。
但是在我解释这个问题之前,我想告诉您您覆盖 getViewTypeCount()和 getItemViewType()的方式是一个严重的错误。
使用ViewHolder的目的在于可以将其保留在视图上以供ListView重用。这样,ListView不必继续为ArrayList中的每个项目重新创建一个新视图。
但是,您正在对代码进行完全相反的操作。通过覆盖 getViewTypeCount()和 getItemViewType()的方式,您实际上在破坏ViewHolders的用途。您要告诉ListView为ArrayList中的每个项目创建一个新的ViewHolder,这使其无法重用存储在ViewHolder中的视图。
不覆盖这两种方法将导致项目重复的原因与您的问题的解决方案有关,该问题已正确使用ViewHolder。
当您进入显示以下代码的代码时,要正确使用ViewHolder:
} else
convertView
不只是返回convertView。从convertView的TAG获取ViewHolder,并使用当前项目的数据更新CheckBox和TextView。
这样,您的ViewHolder将始终保留其应保留的名称和描述。
您还将正确地重用ViewHolder,而不是为每个项目创建一个新的ViewHolder。
上一个代码在滚动时复制项目的原因是因为您没有使用适当的数据更新ViewHolder,所以它只是使用了上一个项目的旧数据。
所以总结一下:
请勿覆盖 getViewTypeCount()和 getItemViewType()
让您的 getView()方法使用适当的数据更新ViewHolder的视图。
答案 1 :(得分:0)
正如@Jackey所说,在实现ListAdapter时您会犯一些错误。
要使ListView正确重用ViewHolders,您需要弄清楚两件事:
因此,在您的情况下,您不必重写这两种方法,因为stdlib.h
的默认实现是:
BaseAdapter
我将为您提供一个工作示例,以帮助您理解这一点:
public int getItemViewType(int position) {
return 0; // type is 0 for every item
}
public int getViewTypeCount() {
return 1; // we only have 1 type
}