自己实现notifyDatasetChanged,notifydatasetchanged
自己实现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);
}
}
效果如下:
最后是代码下载:代码下载
用户评论