都用过 BaseAdapter的notifyDataSetChanged()方法,用法很简单,当BaseAdapter的数据更新了,需要更改显示,这时候就要调用notifyDataSetChanged()方法来更新数据,当然你可以用一种比较恶心的方式,在你所使用的AdapterView(这里是指AdapterView的子类,ListView,GridView,Gallery等等),调setAdapter()方法。好好分析一下如何使用以及为什么要使用notifyDataSetChanged方法。

首先我们先看一下,AdapterView有哪些子类:An AdapterView is a view whose children are determined by an Adapter.See ListViewGridViewSpinner and Gallery for commonly used subclasses of AdapterView.从这句描述中,我们可以看出,AdapterView就是结合Adapter使用的ViewGroup,其好处是不言而喻的,直接来说,这种View能实现View的复用,对节省内存很有帮助。

下面我们分别看一下源码中setAdapter()函数,

AdapterView的setAdapter函数:

/**
* Sets the adapter that provides the data and the views to represent the data
* in this widget.
*
* @param adapter The adapter to use to create this view's content.
*/
public abstract void setAdapter(T adapter);

从这段代码中,我们可以看出,setAdapter在AdapterView中是没有实现的,需要在子类中进行实现。ListView的setAdapter()函数,虽然ListView不是直接继承AdapterView,但是并不影响我们分析setAdapter的机制,为了方便大家阅读,我在代码中做一些注释:

/**
* Sets the data behind this ListView.
*
* The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
* depending on the ListView features currently in use. For instance, adding
* headers and/or footers will cause the adapter to be wrapped.
*
* @param adapter The ListAdapter which is responsible for maintaining the
* data backing this list and for producing a view to represent an
* item in that data set.
*
* @see #getAdapter()
*/
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);//解除数据监听
}
resetList();//清除ListView中的数据
mRecycler.clear();//mRecyler 是一个用来管理View复用的类,清空其中的View数据。
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);//如果你对ListView调用过addHeaderView或者是AooterView,
//adapter将被转换为HeaderViewListAdapter。
} else {
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);//调用父类的setAdapter方法。
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();//生成一个数据监听器,
mAdapter.registerDataSetObserver(mDataSetObserver);//给Adapter注册数据监听
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();//重新请求分配布局
}
从这里我们可以看出,对ListView调用setAdapter会清除所有的数据,然后重新设置数据,这样对软件性能的损耗是不言而喻的。下面我们列举以下,其他的几个Adapter子类的setAdapter方法。

GridView的setAdapter()方法:

/**
* Sets the data behind this GridView.
*
* @param adapter the adapter providing the grid's data
*/
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
mAdapter = adapter;
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null) {
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
mDataChanged = true;
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
checkSelectionChanged();
} else {
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}

我们可以看到GridView的setAdapter()方法中的内容和ListView中的内容几乎是一样的,这里先不做分析。

/**
* The Adapter is used to provide the data which backs this Spinner.
* It also provides methods to transform spinner items based on their position
* relative to the selected item.
* @param adapter The SpinnerAdapter to use for this Spinner
*/
@Override
public void setAdapter(SpinnerAdapter adapter) {
if (null != mAdapter) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
resetList();
}
mAdapter = adapter;
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
if (mAdapter != null) {
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
int position = mItemCount > 0 ? 0 : INVALID_POSITION;
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
checkFocus();
resetList();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}

几乎还是跟之前一样的方式,另外一个Spinner的这个方法的源码,这里就不列出来了。总是分析而言,setAdapter方法会重置所有的数据,虽然能到达数据更新的效果,但是对软件性能的损耗很大,不建议这么做,也就是不建议大家频繁的使用setAdapter函数来更新数据。

public interface Adapter {
void registerDataSetObserver(DataSetObserver observer);
void unregisterDataSetObserver(DataSetObserver observer);
int getCount();
Object getItem(int position);
long getItemId(int position);
boolean hasStableIds();
View getView(int position, View convertView, ViewGroup parent);
static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
int getItemViewType(int position);
int getViewTypeCount();
static final int NO_SELECTION = Integer.MIN_VALUE;
boolean isEmpty();
}

从这里我们看到其实Adapter是一个接口只要实现了这个接口的任何类都可以用于setAdapter。那么BaseAdapter又是怎么一回事呢?,我们来看一下:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public boolean hasStableIds() {
return false;
}
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
}
}
public interface ListAdapter extends Adapter {
public boolean areAllItemsEnabled();
boolean isEnabled(int position);
}
public interface SpinnerAdapter extends Adapter {
public View getDropDownView(int position, View convertView, ViewGroup parent);
}

ListAdapter和SpinnerAdapter是继承了Adapter的另外两个接口,而BaseAdapter同时实现了这两个接口。从Adapter的源码中不难看出,关于数据更新的代码只有两个函数:

public interface Adapter {
/**
* Register an observer that is called when changes happen to the data used by this adapter.
*
* @param observer the object that gets notified when the data set changes.
*/
void registerDataSetObserver(DataSetObserver observer);
/**
* Unregister an observer that has previously been registered with this
* adapter via {@link #registerDataSetObserver}.
*
* @param observer the object to unregister.
*/
void unregisterDataSetObserver(DataSetObserver observer);
//.....
}
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}

这四个函数前两个用于注册和接触内容监听器,后两个是用来通知数据发生变化。

细心的同学可能会发现,在BaseAdapter中以notify开头的函数有四个,其实都是跟通知有关的,但是前两个是Object函数中实现的,用于线程的锁相关的。我们只关心后两个。

//..........
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
//.....
/*这个类是AdapterView的内部类*/
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
requestLayout();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}package android.database;
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
package android.database;
/**
* Receives call backs when a data set has been changed, or made invalid. The typically data sets
* that are observed are {@link Cursor}s or {@link android.widget.Adapter}s.
* DataSetObserver must be implemented by objects which are added to a DataSetObservable.
*/
public abstract class DataSetObserver {
/**
* This method is called when the entire data set has changed,
* most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
*/
public void onChanged() {
// Do nothing
}
/**
* This method is called when the entire data becomes invalid,
* most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
* {@link Cursor}.
*/
public void onInvalidated() {
// Do nothing
}
}

看到这里我想大家就清除明了了,AdapterView在内部实现了一个AdapterDataSetObserver类,对BaseAdapter调用NotifyDataSetChanged就是调用了这个类的onChanged()函数,细看以下AdapterDataSetObserver 的onChanged函数,发现,其实这个函数并没有什么奇特的写法。这个函数中做了这么几件事:
下面我们看一下更新是如何完成的,在listView中有如下函数:

private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected) {
View child;
if (!mDataChanged) {
// Try to use an existing view for this position
child = mRecycler.getActiveView(position);
if (child != null) {
// Found it -- we're using an existing child
// This just needs to be positioned
setupChild(child, position, y, flow, childrenLeft, selected, true);
return child;
}
}
// Make a new view for this position, or convert an unused view if possible
child = obtainView(position, mIsScrap);
// This needs to be positioned and measured
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
return child;
}

这里只是其中的一处,如果没DataChanged为True也就是如果数据变化就重新获取View,如果数据没有变化就从mRecyler中获取activieView。其他mDateChanged的使用地方源代码中还有很多,大家有兴趣可以去参考源代码。总结而言,NotifyDataSetChanged就是告诉ListView数据变化了,进行数据改变,setAdapter函数将清空所有的数据,并且重新设置监听和添加View。这点有很大的不同。

Galler的setAdapter()方法,查看之下,我们发现Gallery本身并没有setAdapter方法,但是Galler的父类AbsSpinner实现了这个方法,

AbsSpinner.setAdapter(SpinnerAdapter adapter):

下面我们来分析BaseAdapter的notifyDataSetChanged()函数。值得一提的是,这个函数在Adapter类中并不存在,是baseAdapter中才出现的。

不多说,上源码,先看一下Adapter的源码和BaseAdapter的源码,为了简明,我删去了原本的注释:

这里的英文我就不做翻译了,友情提示一下大家,想做软件开发英语不好的抓紧学,否则就不要做了。也就是说在Adapter接口中提供了registerDataSetObserver(DataSetObserver observer)和unregisterDataSetObserver(DataSetObserver observer),补充一句,这些都是见名知义的函数,不做解释了。而这两个函数的实现确实在BaseAdapter中,BaseAdapter同时实现了ListAdapter和SpinnerAdapter接口,所以大家通常情况下只要使用baseAdapter就行了,在BaseAdapter中有四个数据更新相关的函数:

其实我们很容易发现,这两个函数仅仅是调用了BaseAdapter函数的内部成员变量的的notifyInvalidated()函数和notifyChanged()函数,而这个成员变量则是通registerDataSetObserver()函数进行设置的。回头看一下ListView的setAdapter的源码有这么一段:

可以看出,内容监听器是在这里设置的,设置了一个AdapterDataSetObserver()的内容监听器。这又是一个怎么的监听器呢?:继续看源码:

a.设置了数据发生变化的表示 mDataChanged=true;

b.将没ItemCount赋值给mOldItemCount,也就是数据变化了,但是保留之前的数据数量。mOldItemCount = mItemCount;

c.更新mItemCount的大小 mItemCount = getAdapter().getCount();

d.最后,重新布局,relayout();

其实这里的关键就是设置数据变化表示,然后更新数量,重新布局。所谓的数据变化监听就是这么回事。就是通知数据变了,然后ListView通知变化。那执行完这段代码之后会执行什么呢?requestLayout();熟悉这个函数的都知道,这个函数的调用会让子控件,也就是当前的ListView重新向父控件请求layout。具体请看 android 图形系统requestLayout的流程。从这里开始,将重新布局和分配空间。

BaseAdapter的notifyDataSetChanged方法相关推荐

  1. notifyDataSetChanged方法

    对于Android开发来说处理一些界面需要和Adapter适配器打交道,虽然Android自带了一些比如ArrayAdapter但是大多数情况下无法满足我们需要,所以就要从BaseAdapter派生一 ...

  2. Android源码与设计模式之notifyDataSetChanged()方法与观察者模式

    BaseAdapter在调用notifyDataSetChanged()方法后,GridView就刷新了,下面从源码角度对此原理进行剖析. 首先进到BaseAdapter中查看其notifyDataS ...

  3. 解决ViewPager和PagerAdapter中调用notifyDataSetChanged失效问题(从notifyDataSetChanged方法的源码入手,超详细)

    从PagerAdapter的notifyDataSetChanged方法源码入手解决ViewPager和PagerAdapter中调用notifyDataSetChanged失效的解决办法 1:问题描 ...

  4. notifyDataSetChanged()方法怎么使用安卓

    notifyDataSetChanged() 方法用于通知数据集改变.它可以用于在数据集更新之后,刷新列表视图的显示. 使用方法如下: 在您需要更新列表视图的地方,获取列表视图的适配器. 调用适配器的 ...

  5. Android中关于notifyDataSetChanged()方法的注意

    1.首先介绍下notifyDataSetChanged()方法的原理 notifyDataSetChanged方法原理 2.介绍下我在此处犯的错误 public class HomeFragment ...

  6. android notifydatasetchanged 刷新错误,Android 调用notifyDataSetChanged方法失败解决办法

    Android 调用notifyDataSetChanged方法失败解决办法 如果使用ListView.GridView等进行数据展示,当绑定的数据有了更新的时候,需要实时刷新ListView,即调用 ...

  7. 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么

    导师安排我做一个小项目,其中涉及到利用Adapter作为ListView的适配器,为ListView提供数据.选中某一项后,要让这一项变成选中状态,也就是背景图片要换一下.下面我就用一个小例子来模拟. ...

  8. BaseAdapter调用NotifyDataSetChanged不刷新

    记录一个今天碰到的NotifyDataSetChanged不刷新的问题,以及我的解决方法. 问题描述:GirdView显示相册,需要在进入编辑的时候,直接调用NotifyDataSetChanged刷 ...

  9. Android中ListView数据使用sAdapter.notifyDataSetChanged();方法不刷新的问题

    原文链接:http://blog.csdn.net/caihongdao123/article/details/51513410  点击阅读原文 ------------------------- 1 ...

最新文章

  1. BZOJ4551: [Tjoi2016Heoi2016]树
  2. 在SD/MMC卡上实现hive (Implement WinCE HIVEROM system on NAND or SD system )
  3. 【Linux学习】epoll详解
  4. 驱动人生2008_驱动人生致敬深圳经济特区建立四十周年!
  5. 一组动图看懂3D打印原理
  6. 【渝粤教育】国家开放大学2018年春季 7397-21T家庭教育咨询与辅导 参考试题
  7. matlab里输出恒压的逆变器,基于IGBT逆变器的异步电机变频调速系统的MATLAB仿真...
  8. 深入浅出 Proguard
  9. c语言getchar的作用,c=getchar()!='\n'到底什么用呢
  10. Atititjs javascript异常处理机制与java异常的转换.js exception process
  11. python爬取饿了么评论_爬虫实例:饿了么爬虫
  12. linux脚本回车键是什么,回车是什么意思 回车键功能介绍
  13. vue导出word文档
  14. 派对屋3000效果器怎样调试_卡拉OK前级效果器如何调试?
  15. 国内外快递公司名称一览表
  16. 内网渗透建立代理通道(如何攻击目标内网机器?)-Socks代理(゚益゚メ) 渗透测试
  17. JAVA定时任务的创建方式
  18. 解决Android调试微信页面,chrome的inspect弹出空白
  19. 前端接收list的情况 (批量添加)出现这个错说明不是传参错误,是解析错误
  20. 怎么用快影去除视频中的水印?

热门文章

  1. GAMES101作业1-VS2019
  2. (一)JPA的快速入门
  3. linux 16.04 密码,诡异的 登录 Linux / Ubuntu 16.04 系统 时, 系统提示 登录密码错误 之谜 !...
  4. 五个好用的网络协议分析工具(附下载地址)
  5. 总谐波失真80_总谐波失真(THD)
  6. Featuretools 学习3 - 深度特征合成
  7. mysql 唯一键_MySQL数据库8(十)唯一键
  8. CSS的小特效之:融合效果
  9. C和C++中的struct
  10. Linux文件类型与属性