欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > 内容正文

自己实现notifyDatasetChanged,notifydatasetchanged

来源: 开发者 投稿于  被查看 15758 次 评论:265

自己实现notifyDatasetChanged,notifydatasetchanged


今天这篇博客,我们来实现一下adapter那个最常用的notifyDatasetChanged功能,我们利用一个继承一个LinearLayout来实现一个可能在日常工作中很常用的功能。
大家在工作中可能经常遇到这样的功能:

需要定义一个列表来展示菜单,但是这个菜单并不一定适合ListView,然后,我们可能就通过一个LinearLayout来实现。

如何让我们的LinearLayout使用起来更像ListView呢? 那就是设置Adapter。那你的代码可能是这样的。
扩展LinearLayout:

public class MyLinearLayout extends LinearLayout {

    private Adapter mAdapter;

    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(VERTICAL);
    }

    public void setAdapter(Adapter adapter) {
        removeAllViews();
        mAdapter = adapter;
        int itemCount = mAdapter.getItemCount();
        View child;
        for (int i = 0; i < itemCount; i++) {
            child = mAdapter.getView(this, i);
            addView(child);
        }
    }
}

自己动手实现一个简单的Adapter:

public abstract class Adapter {
    public abstract int getItemCount();
    public abstract View getView(View parent, int position);
}

在我们的代码如何使用呢?
首先继承咱们定义的Adapter

public class MyAdapter extends Adapter {

    private ArrayList<String> mData;

    public MyAdapter(ArrayList<String> data) {
        mData = data;
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }

    @Override
    public View getView(View parent, int position) {
        TextView textView = new TextView(parent.getContext());
        textView.setText(mData.get(position));
        return textView;
    }
}

给我们的LinearLayout设置Adapter。

mLinearLayout = (MyLinearLayout) findViewById(R.id.linear);
mAdapter = new MyAdapter(mData);
mLinearLayout.setAdapter(mAdapter);

这些都是最基础的代码,可能很多朋友在工作中也会有这么办的时候,所以,我们只是展示一下代码,至于效果,肯定就是一个普通的竖向的LinearLayout。这里也不再多提。
现在,我们来假设这样的一个情形,假如我们的数据是变化的,上面的代码该如何去做呢?
1. 手动调用LinearLayout.getChildAt(index)去设置数据。
2. 重新设置adapter,可以看到在MyLinearLayout.setAdapter开始,我们调用了removeAllViews()。

对于第一种方法,我们感觉有悖于我们的封装,每次数据变化都要手动去更新item;而第二种方式肯定会影响性能,毕竟每次都重新添加view,感觉这肯定是没必要的。
那么我们该怎么办呢? 看看android的adapter是如何实现的,我们只需要调用notifyDatasetChanged就ok。
在开始实现notifyDatasetChanged之前,我们来引入一个概念,那就是观察者模式,java本身提供了观察者模式的实现,但是使用起来有点小麻烦,所以android SDK给我们提供了另一种方式:DataSetObserver。DataSetObserver其实就是一个简单的抽象类,在原理上也很简单,可以这么说,我们完全可以自己定义一个DataSetObserver。但是我们还是选择使用原生的DataSetObserver,毕竟android已经给我们提供好了。
这里需要说明一下,代码不具备任何价值,只有参考意义,而且,我们代码有很大的局限性,因为我们只考虑了TextView。
先来看看我们的Adapter吧,在上面的基础上加了几行代码:

public abstract class Adapter {

    private DataSetObserver mDataSetObserver;

    public void registerObserver(DataSetObserver observer) {
        mDataSetObserver = observer;
    }

    public void unregisterObserver() {
        mDataSetObserver = null;
    }

    public void notifyDataSetChanged() {
        if(mDataSetObserver != null) mDataSetObserver.onChanged();
    }

    public void notifyDataSetInvalidate() {
        if(mDataSetObserver != null) mDataSetObserver.onInvalidated();
    }

    public abstract String getItem(int position);
    public abstract int getItemCount();
    public abstract View getView(View parent, int position);
}

增加了一个DataSetObserver变量,对应的,定义了两个方法来向这个DataSetObserver赋值,而且,我们新增了一个abstract方法
public abstract String getItem(int position);
最主要的,我们来看看两个最重要的方法:

public void notifyDataSetChanged() {
        if(mDataSetObserver != null) mDataSetObserver.onChanged();
    }

    public void notifyDataSetInvalidate() {
        if(mDataSetObserver != null) mDataSetObserver.onInvalidated();
    }

notifyDataSetChanged方法,我们直接调用了DataSetObserver.onChanged(),invalidate也是一样,这里我们就需要关心一下这个DataSetObserver是从哪注册进来的。
下面公布经过修改过的MyLinearLayout:

public class MyLinearLayout extends LinearLayout {

    private Adapter mAdapter;

    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(VERTICAL);
    }

    private DataSetObserver mObserver = new DataSetObserver() {
        public void onChanged() {
            onChange();
        }

        public void onInvalidated() {
            removeAllViews();
        }
    };

    private void onChange() {
        int childCount = getChildCount();
        int itemCount = mAdapter.getItemCount();
        // 如果数据变少了,则移出多余的view
        if(childCount > itemCount) removeViews(itemCount, childCount - itemCount);

        // 现在view的个数 <= 数据的个数
        TextView child;
        for (int i = 0; i < itemCount; i++) {
            if(i < childCount) {
                child = (TextView) getChildAt(i);
                child.setText(mAdapter.getItem(i));
            }else {
                child = (TextView) mAdapter.getView(this, i);
                addView(child);
            }
        }
    }

    public void setAdapter(Adapter adapter) {
        removeAllViews();
        mAdapter = adapter;
        mAdapter.registerObserver(mObserver);

        int itemCount = mAdapter.getItemCount();
        View child;
        for (int i = 0; i < itemCount; i++) {
            child = mAdapter.getView(this, i);
            addView(child);
        }
    }
}

在MyLinearLayout中定义了一个DataSetObserver,并且在setAdapter中向Adapter注册这个DataSetObserver。最主要的代码是在DataSetObserver中那个onChanged方法中:

private void onChange() {
        int childCount = getChildCount();
        int itemCount = mAdapter.getItemCount();
        // 如果数据变少了,则移出多余的view
        if(childCount > itemCount) removeViews(itemCount, childCount - itemCount);

        // 现在view的个数 <= 数据的个数
        TextView child;
        for (int i = 0; i < itemCount; i++) {
            if(i < childCount) {
                child = (TextView) getChildAt(i);
                child.setText(mAdapter.getItem(i));
            }else {
                child = (TextView) mAdapter.getView(this, i);
                addView(child);
            }
        }
    }

第5行代码,如果发现新的数据集比以前少了,那么我们去移除多余的view,
接下来9行,还是去遍历所有的数据集,接着一个if…else…是去判断如果存在view,则只改变数据,如果数据量>子view个数,则添加新的view。
这样,就做到了在最大程度上减少view的添加和移除操作。
当然,这段代码也很有局限性,只能添加TextView,因为我们在这里面强转了一下。这段代码只是去演示如何去实现一个数据观察者。ok,最后再来看看我们的测试代码和最后效果:

public class MainActivity extends Activity {

    private ArrayList<String> mData = new ArrayList<String>() {
        {
            for (int i = 0; i < 10; i++) {
                add("android " + i);
            }
        }
    };

    private MyLinearLayout mLinearLayout;
    private MyAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mLinearLayout = (MyLinearLayout) findViewById(R.id.linear);
        mAdapter = new MyAdapter(mData);
        mLinearLayout.setAdapter(mAdapter);

        mLinearLayout.postDelayed(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    mData.add("loader " + i);
                }
                mAdapter.notifyDataSetChanged();
            }
        }, 2000);

        mLinearLayout.postDelayed(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    mData.remove(0);
                }

                mAdapter.notifyDataSetChanged();
            }
        }, 4000);

        mLinearLayout.postDelayed(new Runnable() {
            @Override
            public void run() {
                mAdapter.notifyDataSetInvalidate();
            }
        }, 6000);
    }
}

效果如下:

最后是代码下载:代码下载

相关频道:

用户评论