ListView访问列表最后一项时触发NullReferenceException异常
问题根因
你遇到的空引用和数据源是否为空没有任何关系,核心是混淆了ListView的两个完全不同的索引逻辑:
lstViewData.Count实际取的是适配器绑定的全量数据总条数,你这里5条数据取值0-4本身没错- 但
lstViewData.GetChildAt(i)获取的是 ListView当前屏幕上挂载的可见子View实例,这个方法的入参不是数据源的全局位置索引,而是可见区域内子View的相对索引,取值范围只能是0到lstViewData.ChildCount - 1。
ListView本身做了View复用优化,不在当前可见区域的列表项根本不会生成对应的View实例挂载到控件上,哪怕对应位置的数据源有值,调用GetChildAt传入对应全局索引也会返回null,自然触发空引用异常。哪怕你的5条数据刚好一屏能放下,只要列表发生过滚动,可见子View的相对索引和数据源全局索引就会出现错位,直接传遍历的i值本身就是错误用法。
临时修复方案(仅解决空引用,仍有复用隐患)
不要用全量数据长度做遍历边界,只遍历当前实际存在的可见子View,同时通过View反查对应的真实数据源位置再设置背景,你的事件绑定代码lstViewData.ItemClick += OnListViewItemClicked;不需要修改,只需要替换点击事件实现即可:
private void OnListViewItemClicked(object sender, AdapterView.ItemClickEventArgs e) { int selectedPos = e.Position; // 用ChildCount做遍历边界,只处理实际存在的子View for (int i = 0; i < lstViewData.ChildCount; i++) { View itemView = lstViewData.GetChildAt(i); // 反查当前子View对应的真实数据源位置 int realPos = lstViewData.GetPositionForView(itemView); itemView.SetBackgroundColor(realPos == selectedPos ? Android.Graphics.Color.Gray : Android.Graphics.Color.Transparent); } }
规范实现方案(适配复用机制,无状态错乱)
直接操作子View改背景的写法不符合ListView的设计逻辑,列表滑动后View复用时会出现高亮位置错乱的问题,正确实现选中高亮的方式是把选中状态存在适配器层:
- 在你的自定义适配器里增加一个公开的选中位置字段,默认值设为-1(无选中)
- 在适配器的
GetView方法中,根据当前渲染项的position和选中位置字段的匹配关系,设置对应背景色 - 点击事件中只需要更新适配器的选中位置字段,调用
NotifyDataSetChanged()触发列表刷新即可,不需要手动遍历修改子View。
内容的提问来源于stack exchange,提问作者Josip Juros
相关产品推荐
相关产品推荐

