zoukankan      html  css  js  c++  java
  • 浅析notifyDataSetChanged内部工作流程

    Reference: http://blog.csdn.net/hp910315/article/details/47174531

    首先我们知道notifyDataSetChanged是Adater的一个方法,主要用来通知ListView,告诉它Adapter的数据发生了变化,需要更新ListView的显示,所以当Adapter的数据内容改变时会调用notifyDataSetChanged()方法。 
    直接看看BaseAdapter中notifyDataSetChanged的源码实现,看notifyDataSetChanged是如何工作的

    public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
        private final DataSetObservable mDataSetObservable = new DataSetObservable();
    
        public void notifyDataSetChanged() {
            mDataSetObservable.notifyChanged();
        }
    }

    可以看到它调用的是DataSetObservable中的notifyChanged,进入DataSetObservable查看实现

    public class DataSetObservable extends Observable<DataSetObserver> {
            public void notifyChanged() {
            synchronized(mObservers) {
    
                for (int i = mObservers.size() - 1; i >= 0; i--) {
                    mObservers.get(i).onChanged();
                }
            }
        }
    }

    其中mObervers是在DataSetObservable的父类Observable中定义:

    public abstract class Observable<T> {
    
        protected final ArrayList<T> mObservers = new ArrayList<T>();
    
    }

    它是一个DataSetObserver类型的ArrayList,最终执行的是DataSetObserver的onChange函数。

    再来看看DataSetOberver类的源代码:

    public abstract class DataSetObserver {
    
        public void onChanged() {
            // Do nothing
        }
    
          public void onInvalidated() {
            // Do nothing
        }
    }

    从上面的过程可以大致看出,如果需要得到ListView更新的通知,首先实现一个DataSetObserver类,重写其中的onChanged回调方法,然后把这个对象添加(注册)到ArrayList中,这样当我们调用notifyDataSetChanged的时候,它会遍历这个ArrayList取出DataSetObserver对象,回调onChanged方法。也就是说我们最终的刷新ListViewd的工作应该在这个onChanged方法中。

    那么疑问就是系统在哪个地方实现了实现一个DataSetObserver类,重写其中的onChanged回调方法,然后把这个对象添加到ArrayList当中的。

    其实这个工作在setAdapter中完成的,当为ListView设置一个Adapter的时候,就在这个Adapter中注册了一个回调监听,也就是上面说的实现一个DataSetObserver类,重写其中的onChanged回调方法,然后把这个对象添加到ArrayList当中,当Adapter调用notifyDataSetChanged的时候,就会回调onChanged函数,在onChanged里面进行ListView的更新,这样ListView就进行更新操作。

    那么我们来看看ListAdapter中setAdapter的实现:

        @Override
        public void setAdapter(ListAdapter adapter) {
            //这里判断是否已经注册了监听
            //如果已经注册,则取消注册
            //如果重复调用setAdapter,下面的代码就会执行mDataSetObserver从ArrayList中移除
            if (mAdapter != null && mDataSetObserver != null) {
                mAdapter.unregisterDataSetObserver(mDataSetObserver);
            }
    
            resetList();
            mRecycler.clear();
    
            if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
            } else {
                mAdapter = adapter;
            }
    
            mOldSelectedPosition = INVALID_POSITION;
            mOldSelectedRowId = INVALID_ROW_ID;
    
            // AbsListView#setAdapter will update choice mode states.
            super.setAdapter(adapter);
    
            if (mAdapter != null) {
                mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
                mOldItemCount = mItemCount;
                //得到数据的数量
                mItemCount = mAdapter.getCount();
                checkFocus();
    
                //看到没有这里定义了一个AdapterDataSetObserver,它就是DataSetObserver的实现类
                mDataSetObserver = new AdapterDataSetObserver();
                //看到这里应该明白了,这里将这个DataSetObserver实现了类对象添加到ArrayList中,这样就可以回调了
                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);
    
                if (mItemCount == 0) {
                    // Nothing selected
                    checkSelectionChanged();
                }
            } else {
                mAreAllItemsSelectable = true;
                checkFocus();
                // Nothing selected
                checkSelectionChanged();
            }
    
            //会导致调用measure()过程 和 layout()过程
            requestLayout();
        }

    具体的解释直接看代码的注释,这样更方便理解,主要就是mDataSetObserver = new AdapterDataSetObserver()和mAdapter.registerDataSetObserver(mDataSetObserver)这两句,我们上面已经注释了AdapterDataSetObserver就是DataSetObserver的实现类,它重写了onChanged方法。 
    看AdapterDataSetObserver源代码,在这里就可以看到,notifyDatasetChanged的最终执行的操作是什么了,因为它最终回调了这个onChanged方法。AdapterDataSetObserver是AbsListView的一个内部类

        class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
            @Override
            public void onChanged() {
                //这里是核心操作
                super.onChanged();
                if (mFastScroller != null) {
                    mFastScroller.onSectionsChanged();
                }
            }
    
            @Override
            public void onInvalidated() {
                super.onInvalidated();
                if (mFastScroller != null) {
                    mFastScroller.onSectionsChanged();
                }
            }
        }
    

    最终回调的就是这里的onChanged函数,直接看代码,核心操作在super.onChanged()里面。 
    super.onChanged意思就是执行AdapterView.AdapterDataSetObserver里面的onChanged函数,它是ApdaterView的一个内部类。

        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();
                //会导致调用measure()过程 和 layout()过程
                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;
            }
        }
    

    看到没有,这个类实现了DataSetObserver,正好说明了上面的说法,直接看它的onChanged函数。直接看最后一句requestLayout(),这里就会进行刷新了, 
    如果细心的话,你应该也会看到在setAdapter中也执行了这个函数,这样就充分说了,执行这个函数,这里是进行了布局和重绘。 
    这里我们可以知道一点,就是我们上面对ListView的刷新,本质就是调用了requestLayout方法。

    关于requestLayout里面具体干了什么,执行流程是什么,可以看看下面这篇文章:

  • 相关阅读:
    Git 最全命令使用
    git 配置(实用)
    用Redis进行实时数据排名
    最长上升子序列
    KMP算法
    计算星期几【基姆拉尔森公式】
    集合划分(贝尔数)
    合数分解(质因数分解)
    乘法逆元
    扩展欧几里得算法
  • 原文地址:https://www.cnblogs.com/skying555/p/8392338.html
Copyright © 2011-2022 走看看