首页 > 解决方案 > 在 ListView 中添加到 ArrayList 时出现 ArrayIndexOutOfBoundsException

问题描述

我发现了有关此异常的其他几个问题,但没有一个问题适合我。

这是我的整个代码:

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() 但他们没有提到如何修复它。

标签: androidkotlin

解决方案


发生这种情况是因为您使用 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,所以它只是使用了之前项目中的旧数据。

所以总结一下:

  1. 不要覆盖getViewTypeCount()getItemViewType()

  2. 让您的getView()方法使用正确的数据更新 ViewHolder 的视图。


推荐阅读