Android ListView未调用notifyDataSetChanged仍显示新增项问题
问题描述
我是Android开发初学者,对setAdapter与ListView的运行逻辑存在疑问。
我的MainActivity.java代码如下:
public class MainActivity extends AppCompatActivity { private Button generate_btn; private ArrayList<Integer> arr; private Random rand_generator; private ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); arr = new ArrayList<Integer>(); setContentView(R.layout.activity_main); for(int i = 0; i < 5; i++){ arr.add(i); } generate_btn = findViewById(R.id.button); ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(getApplicationContext(), android.R.layout.simple_list_item_1, arr); listView = findViewById(R.id.listView); listView.setAdapter(adapter); for(int i = 0; i < 5; i++){ arr.add(i + 100); } } }
如上代码包含两个for循环:第一个循环向ArrayList<Integer> arr中添加0-4共5个整数后,我为listView调用setAdapter绑定适配器,此时arr中仅包含0-4这5个元素。
第二个循环中我向arr添加100-104共5个整数,且未调用notifyDataSetChanged()方法。
我原本预期列表仅会渲染绑定适配器时arr已存在元素对应的TextView,但实际运行时第二个循环新增的整数对应的TextView也正常显示,请问我对相关机制的理解存在什么误区?
核心解答
你的认知存在两个关键误区:
setAdapter不会触发立刻绘制
调用setAdapter()仅完成适配器和ListView的绑定关系注册,不会同步执行数据读取、条目渲染流程。ListView真正开始读取适配器数据、测量布局、绘制条目,要等当前onCreate生命周期方法全部执行完毕,主线程进入下一次消息循环、处理绘制任务时才会启动。ArrayAdapter不会拷贝数据集
构造ArrayAdapter时传入的ArrayList,适配器仅保存它的对象引用,不会复制列表内的元素做独立存储。你在setAdapter之后写的第二个for循环,和适配器操作的是同一个内存里的列表对象。
等主线程真正开始绘制ListView时,两次for循环早就同步执行完成,适配器调用getCount()、getView()读取到的已经是包含10个元素的完整列表,自然会把所有条目全部渲染出来,不需要额外调用notifyDataSetChanged()。
补充验证逻辑
你可以通过两个简单测试确认这个机制:
- 把第二个添加元素的for循环放到按钮点击事件里,等页面加载完成、列表已经显示出0-4的条目后再点击按钮触发添加,这时候不调用
notifyDataSetChanged()的话,列表绝不会自动刷新出新条目——因为首次渲染已经完成,没有通知的前提下ListView不会主动重新读取数据。 - 在
onCreate里第二个for循环执行完后立刻打日志打印adapter.getCount(),你会看到返回值已经是10,此时ListView还没开始任何绘制流程,已经能读到修改后的列表长度。
notifyDataSetChanged()的作用是在列表完成首次渲染后,通知注册的观察者数据集发生变更,触发列表重新走测量、布局、绘制流程。如果数据修改发生在列表首次渲染之前,调用这个方法没有任何意义。
内容的提问来源于stack exchange,提问作者Mrityu
相关产品推荐
相关产品推荐

