Android问题集:notifyDataSetChanged()方法无用

Posted 王梵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android问题集:notifyDataSetChanged()方法无用相关的知识,希望对你有一定的参考价值。

问题描述

话说,我接了坑同事的代码,据说这是同事接的前同事的代码…..不管怎么说,遇到了一个bug,很容易解决,但原理一直搞不明白,于是网络搜索,没搜到结果,但发现了一个类似却又不同的问题,写代码重现了之后,准备记录与此。
在使用ListView时,不可避免地要为它设置adapter,并且要为adapter设置数据,那么就很容易出现该问题,先写个错误代码,大家high一下。

问题代码

布局不贴了,下面会贴一张代码的运行图,一看便知。先来看看activity的onCreate方法,如下

ListView mListView;
ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_adapter_bug);

    findViewById(R.id.tv_type_1).setOnClickListener(this);
    findViewById(R.id.tv_type_2).setOnClickListener(this);
    findViewById(R.id.tv_type_refresh).setOnClickListener(this);
    mListView = (ListView) findViewById(R.id.lv_adapter_bug);
    getData(1);
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mDatas);
    mListView.setAdapter(adapter);

    

很简单,有没有,很单纯地为ListView设置了一个adapter,其中涉及到了一个方法,getData(),如下

List<String> mDatas = new ArrayList<>();

private void getData(int type) 
    mDatas = new ArrayList<>();
    for (int i = 0; i < 10; i++) 
        if (type == 1) 
            mDatas.add("--------aaaaaaa" + i);
         else 
            mDatas.add("--------bbbbbbb" + i);
        
    

还是很简单,对不,我们再来看点击事件

@Override
public void onClick(View v) 
    switch (v.getId()) 
        case R.id.tv_type_1:
            getData(1);
            break;

        case R.id.tv_type_2:
            getData(2);
            break;

        case R.id.tv_type_refresh:
            adapter.notifyDataSetChanged();
            break;
    

就是这个样子,貌似没啥问题,是不,好吧,我们来看下运行效果,

我去,你到底干了啥,为啥没啥变化,你操作了吗?脑袋中有没有一系列问号,来来来,我们看个正确的运行结果。

嗯,貌似看出点不对了,那么,为什么我调用了notifyDataSetChanged()方法,却没有刷新数据呢?
别着急,听我慢慢道来。

问题解析

假设,程序的onCreate()方法已经运行过,之后,我们按步骤来分析
第一步:点击type=2的按钮
这个方法做了啥,就调用了getData(2)方法,我们来看看getData()方法的关键代码,也就是第一句,

List<String> mDatas = new ArrayList<>();

private void getData(int type) 
    mDatas = new ArrayList<>();
    ...

我们重新创建了一个对象,将它指向名为mDatas的引用,然后就是为集合添加元素。
第一步完成。第二步走起!

第二步:点击中间的按钮,刷新,代码很简单就是一句

adapter.notifyDataSetChanged();

结束了,没毛病啊,我点击刷新了,你为毛不给我刷啊?!!
年轻人,别冲动,电脑是不会骗人的,我们来分析一下adapter的构造方法,在onCreate方法中

adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mDatas);

最后一个参数,将mDatas传进去了,之后ListView的源码会调用adapter的方法,获取数据,设置数据,都是mDatas这个对象
mDatas这个对象!!!
看到没,问题就在这里,调用的是名为mDatas的对象。当我们点击type=2的按钮时,重新创建了一个对象,指向了mDatas,也就是说,现在另一个对象叫mDatas了,而传入了adapter中的那个集合对象,现在没有名字了,但是,它还在内存中,并没有消失,仅仅是没有名字罢了,匿名啊,兄弟!
所以,任凭你怎么调用adapter的notifyDataSetChanged()方法也没什么用。

问题解决

原因搞清楚了,解决就很简单了。而且方式有很多,比如我们改下getData()方法

List<String> mDatas = new ArrayList<>();

private void getData(int type) 
//    mDatas = new ArrayList<>();
    mDatas.clear();
    for (int i = 0; i < 10; i++) 
        if (type == 1) 
            mDatas.add("--------aaaaaaa" + i);
         else 
            mDatas.add("--------bbbbbbb" + i);
        
    

不再重建mDatas,每次清空集合即可。
下面是另一种,在点击事件中

adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mDatas);
mListView.setAdapter(adapter);
//    adapter.notifyDataSetChanged();

还有第三种方法,不过在这个demo中无法使用,比如你使用的是BaseAdapter的子类,那么就可以添加一个方法,setData(),具体如下

class Adapter extends BaseAdapter

    Context context;
    List<String> list;
    public Adapter(Context context,List<String> list)
        this.context = context;
        this.list = list;
    

    public void setData(List<String> list)
        this.list = list;
        notifyDataSetChanged();
    

    @Override
    public int getCount() 
        return list.size();
    
     @Override
    public Object getItem(int position) 
        return null;
    

    @Override
    public long getItemId(int position) 
        return 0;
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        View view = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, null, false);
        TextView textView = (TextView) view.findViewById(android.R.id.text1);

        String text = list.get(position);
        textView.setText(text);
        return view;
    

在点击事件中,调用方法如下

adapter.setData(mDatas);

之所以写出这种写法,是之前的同事很多都用这种写法,自己虽然不习惯这种写法,但还是写出来,供大家参考一下。

总结

简单一句话,这个问题不是移动端问题(我实在受不了红色的那个啥,所以用移动端代替),是个对象问题…

以上是关于Android问题集:notifyDataSetChanged()方法无用的主要内容,如果未能解决你的问题,请参考以下文章

我将如何下载 *** android 数据集?

android问题笔记集

结果集到 Android 光标

加载程序集 Xamarin.Android.Support.v4 时出现异常

android中的图例和字段集

Android MediaCodec 的程序集容器