adapter的notify.notifyDataSetChange的作用,什么时候用

34225人阅读
初级android(31)
android使用notifyDataSetChanged()方法,发现adapter的数据更新了,但是ListView的内容没有更新?
查阅如下网站解决方案都没有解决:
后来冷静一想,我遇到这个问题的情景跟他们的有所区别:
我的问题是从另个界面跳转回来后,再调用apater的notifyDataSetChanged()方法时失效,adapter的数据更新了,
但listview显示的内容并没有改变。
针对这个问题的解决方案是:
在Activity的onResume()方法中将adaper和listView重新再绑定一次。
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
在此记录下,备用。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:819805次
积分:6935
积分:6935
排名:第2469名
原创:105篇
评论:107条
(1)(1)(1)(1)(5)(4)(4)(2)(4)(4)(4)(2)(2)(7)(6)(6)(11)(9)(10)(8)(8)(9)点滴纪录(5)
技术心得(5)
先不说别的, 上图:
项目中,我们可能会遇到以上那种情况 ,
颜色,品牌,尺寸,等规格,每个规格里面有长短不一,大小不一,数量不一的规格项,如果用GridView,则每一项的个数, 都会固定,实现不了这种错落排版的效果。
于是在github上逛了一圈, 找到了我想要的:
感谢前人们为我们提供的方便
到这里,还没有结束, 下面才是今天要讨论的东西。
这个FlowLayout,一般用法是addView(view),可是,我还要嵌套在一个listview里面,要在getView里面每次
for(xx : xx) {
View view = View.inflate(xxxx,xxx,xx);
Xx xx = view.findViewById(R.id.xx)
Xx xx = ....;
xx.setText(xx);
android已经给我们提供指导方向了, 所以我们在使用ListView,GridView等等多条目控件的时候,会写一个适配器,把数据层给分开,那这个能加一个适配器吗,ok,老思路先分析 :
1.首先我要给FlowLayout加个setAdapter(ListAdapter adapter);
2.数据变动我要改动Flowlayout里面的view,看看ListView源码,要给adapter注册一个Observer,类似这样:
adapter.registerDataSetObserver(new DataSetObserver() {
public void onChanged() {
changeViews();
public void onInvalidated() {
changeViews();
实现这个之后,在adapter的notifyDatasetChange的时候,onChanged方法就会回调
3.利用adapter的getView来得到赋值好的view,并加到FlowLayout,
4.能不能考虑下复用view,不能每次removeAllViews()再addView啊。
好了,分析完了,从第一条开始上代码:
private ListA
public void setAdapter(@NonNull ListAdapter adapter) {
if (adapter == null) throw new NullPointerException("FlowLayout.setAdapter不能传空参数");
this.adapter =
changeViews();
adapter.registerDataSetObserver(new DataSetObserver() {
public void onChanged() {
changeViews();
public void onInvalidated() {
changeViews();
代码很简单,
1.就是在FlowLayout里面保留一个adapter,
2.然后从adapter里面询问view,
3.注册观察对象,得以在adapter.notifyDataSetChanged()的时候,能感知到。
本身很简单,一共就两个方法嘛~
下面贴changeViews()方法:
private void changeViews() {
final int count = adapter.getCount();
if (count & 0) {
int childCount = getChildCount();
if (childCount & count) {
removeViews(count, childCount - count);
for (int i = 0; i & count; i++) {
final View view = adapter.getView(i, getChildAt(i), this);
if (view.getLayoutParams() == null) {
LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.rightMargin
lp.bottomMargin = childV
view.setLayoutParams(lp);
if(getChildAt(i) != view) {
addView(view);
removeAllViews();
这里稍微麻烦点,
为什么呢?因为考虑到我们不能每一次都让adapter重新创建新view,这样实则是一种性能上的浪费,但是真做到ListView那种级别的复用,也是难。所以,我就简单点做,能复用就复用,不能复用再创建。
"能复用就复用,不能复用再创建" 怎么体现出来的呢?
final int count = adapter.getCount();
if (count & 0) {
int childCount = getChildCount();
if (childCount & count) {
removeViews(count, childCount - count);
for (int i = 0; i & count; i++) {
final View view = adapter.getView(i, getChildAt(i), this);
if (view.getLayoutParams() == null) {
LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.rightMargin
lp.bottomMargin = childV
view.setLayoutParams(lp);
if(getChildAt(i) != view) {
addView(view);
ok,FlowLayout的改造已经完工了,
回到第一张图上去,看看怎么用的吧
&?xml version="1.0" encoding="utf-8"?&
xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="/apk/res-auto"
android:padding="@dimen/dp10"
android:background="#fff"
android:orientation="vertical"&
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="42dp"
android:gravity="center_vertical"
android:textColor="@color/normalText"
android:textSize="@dimen/normal"
android:id="@+id/flowLayout"
android:layout_marginTop="@dimen/dp10"
app:childHorizontal="5dp"
app:childVerticle="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/&
public class SelectGuigeAdapter extends LBaseAdapter&GuigeBean, SelectGuigeAdapter.VH& {
public SelectGuigeAdapter(Context context) {
super(context);
public VH createViewHolder(ViewGroup parent, int position) {
return new VH(View.inflate(getContext(),R.layout.select_guige_list_item,null));
public void bindViewHolder(VH holder, GuigeBean data, int position) {
holder.txtTitle.setText(data.name);
holder.mAdapter.setDataSource(data.items, true);
static class VH extends LBaseAdapter.ViewHolder {
private TextView txtT
private FlowLayout flowL
private SelectGuigeItemAdapter mA
public VH(View convertView) {
super(convertView);
txtTitle = get(R.id.title);
flowLayout = get(R.id.flowLayout);
if (mAdapter == null) {
mAdapter = new SelectGuigeItemAdapter(getContext());
flowLayout.setAdapter(mAdapter);
再看FlowLayout的适配器
public class SelectGuigeItemAdapter extends LBaseAdapter&String, LBaseAdapter.ViewHolder& {
public SparseBooleanArray selectMap = new SparseBooleanArray();
public SelectGuigeItemAdapter(Context context) {
super(context);
public ViewHolder createViewHolder(ViewGroup parent, int position) {
return new ViewHolder(View.inflate(getContext(), R.layout.checkedtextview_item,null));
public void bindViewHolder(ViewHolder holder, String data, final int position) {
CheckedTextView txt = holder.get(R.id.text);
txt.setText(data);
txt.setChecked(selectMap.get(position,false));
txt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
selectMap.clear();
selectMap.put(position, true);
notifyDataSetChanged();
ok,连起来了, 这个LBaseAdapter是什么鬼, 大家可以关注下我的上一遍文章
嗯,先到这,写的不好,望指正
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:11510次
排名:千里之外android(5)
之前写程序就一直没搞懂Adapter的notifyDataSetChange()方法为什么会根据数据源来刷新ListView的列表,在网上荡了很久却总不得精髓,今天因为要用耐着性子很仔细的研究了一下。(斜体的看不懂可以略过)
首先说一下观察者模式,这个网上介绍很多,但是说实话很简单的一个东西我不明白他们干嘛要讲那么多,我给大家简单的来说一说(是不是侮辱了艺术)。首先我们定义两个类,一个观察者类A,一个被观察类B,那被观察类B中有一个观察者A的引用,然后呢,当被观察者B调用更新数据的方法的时候就会顺带执行观察者A的某一个既定的方法。简单点说就这个意思。其实我记得我之前在看观察者模式的时候是结合这ListView来的,可是说实话,ListView中的观察者模式究竟是谁观察谁呢,因为当时写的项目的云因,当时我看的可真是云里雾里。先把这个观察者模式说完啊。那一般在各种语言环境中,语言的开发者都会给你写好一些设计模式的固定接口,方便你的使用,那在java中的观察者模式也是一样的。
一般呢会给你两个接口一个是观察者的接口叫Observer吧,另一个是被观察者的接口就叫Observable。然后呢里面会分别会定义不同的方法和变量。那需要什么方法和变量呢?我把代码写上来
public abstract class Observer{
void onChange();
public abstract class Observable{
private final List&Observer& mObservers= new ArrayList&Observer&();
void registerDataSetObserver(Observer observer){
mObservers.add(observer);
void unRegisterDataSetObserver(Observer observer) {
mObservers.remove(observer);
public void notifyChanged() {
for (int i = mObservers.size() - 1; i &= 0; i--) {
mObservers.get(i).onChanged();
观察者模式讲完了,下面开始来分析ListView是怎么将观察者模式应用起来的。
首先我们先来说说这张图的上半部分,把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) {
public int getViewTypeCount() {
public boolean isEmpty() {
return getCount() == 0;
可以看到里面有一个mDataSetObservable参数,那这个参数是来干嘛的呢,可以看到他的类型是DataSetObservable,由上图又可以看到DataSetObservable实现了抽象类Observable,并且呢定义了刷新的方法notifyChanged,注意到没有,谷歌的这个监听者模式把注册监听者和遍历监听者这两个方法放在了两个类中,并且BaseAdapter并不是直接继承DataSetObservable来作为一个被监听对象,而是在自己的空间中定义了一个DataSetObservable对象,来实现给监听者传递消息,何必呢你说。难道就是为了改个名字。。。。。构造的这么复杂,还真是这样,就是为了给遍历监听者的方法改个名字,毕竟在外面包一层就可以改名字了嘛。嘿嘿,说详细点吧,其实吧改个名字确实不假,它更深一层的意思是什么呢,以这种方式你可以绑定不同的监听者来监听不同的对象,并且可以通过该方法名字的方式做到很好的代码可读性。而且呢,谷歌更厉害的是在实现Observable(参看图片)的时候它是这样写的
public abstract class Observable&T& {
protected final ArrayList&T& mObservers = new ArrayList&T&();
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
mObservers.add(observer);
public void unregisterObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered.");
mObservers.remove(index);
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
看到没这是一个泛型类,那这个泛型类起什么作用呢?谷歌是这么用的,你看代码public class DataSetObservable extends Observable&DataSetObserver&{}利用它就可以实现一种类型的观察者和另外一种类型的被观察者一一照应,就比如DataSetObservable这个被观察者就只能被DataSetObserver所观察,只能执行DataSetObserver中的onChange方法,如果有一个XXXObserver是不能被注册到DataSetObservable,只能重新定义另外一个XXXObservable extends Observable。知道了吧。
咱们再说图片中下面的那部分,可以看到在setAdapter( )方法中,执行了adapter中的registerDateSetObserver(mDataSetObserver)方法,那这个mDataSetObserver是个什么?你会发现在ListView里面似乎根本就没有这个对象(注意似乎没有,实际上是有的),其实他是在ListView的父类AbsListView里面,AbsListView里面有这样一句代码AdapterDataSetObserver mDataSetO 那AdapterDataSetObserver又是什么类型呢?他是AbsListView里面的一个内部类,看看它的代码啊
class AdapterDataSetObserver extends AdapterView&ListAdapter&.AdapterDataSetObserver {
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
public void onInvalidated() {
super.onInvalidated();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
可以看到 AdapterDataSetObserver是继承了AdapterView的内部类AdapterDataSetObserver,这两个内部类名字是一样的,那AdapterView又是什么呢,AdapterView是AbsListView的父类,AdapterView呢是一个抽象类,这个抽象类就是可以和Adapter配合的最祖宗级别的类了,我先把AbsListView中 那个观察者贴过来啊,因为在AbsListView中的mDataSetObserver所对应的类是mDataSetObserver.AdapterDataSetObserver,代码如下:
class AdapterDataSetObserver extends AdapterView&ListAdapter&.AdapterDataSetObserver {
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
public void onInvalidated() {
super.onInvalidated();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
可以看到在onChange的第一行代码中,直接就执行了一句supe.onChange( )方法,也就是说同时调用了AdapterView这个祖宗级别的类里面的AdapterDataSetObserver中的onChange( )方法,再看看里面都执行了什么,
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemC
mItemCount = getAdapter().getCount();
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount & 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
rememberSyncState();
checkFocus();
requestLayout();
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
mInstanceState = AdapterView.this.onSaveInstanceState();
mOldItemCount = mItemC
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;
可以看到里面有一句requestLayout(),那么这句代码就是用来刷新ListView的,那这个方法究竟是怎么来刷新ListView的呢????抱歉,我看了一下,没看懂,回头有时间研究一下ListView的绘制流程再来给大家作分析。
关于ListView的观察者模式就算介绍完了,总结一下,Android的观察者模式是这样一种类型,定义一个抽象观察者类A、一个抽象的别被观察者类B并且采用泛型的形式,然后呢,写一个A的实现AA extends A,一个B的实现类BB extends B&AA&{public void notify(){aa.onChange};}//aa是AA类的对象 然后呢,再在真正的被观察者X中定义一个BB变量,真正的观察者中定义一个AA变量,并实现相应的方法,从而达到屌的不行的设计方式。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:847次
排名:千里之外
(1)(1)(2)(1)(1)

我要回帖

更多关于 notifydatasetchange 的文章

 

随机推荐