notifyDataSetChanged()源码分析以及如何实现ListView局部刷新

来源:互联网 发布:js的横向时间轴源码 编辑:程序博客网 时间:2024/06/13 07:48

notifyDataSetChanged()

当我们调用BaseAdapter的NotifyDataSetChanged()时,为什么ListView会全局刷新呢?

    /**     * 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();    }

其中调用了DataSetObservable的notifyChanged()

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();            }        }    }    ……}

mObservers是DataSetObservable的成员变量,一个ArrayList,可以在DataSetObservable的父类Observable< T >中看出:

public abstract class Observable<T> {    /**     * The list of observers.  An observer can be in the list at most     * once and will never be null.     */    protected final ArrayList<T> mObservers = new ArrayList<T>();    ……}

从以上代码可以看出notifyChanged()调用的是DataSetObserver的onChanged()

    public void onChanged() {        // Do nothing    }

DataSetObserver是一个抽象类,onChanged()默认是空实现,那我们就试着去找它的具体子类。

猜想应该是在ListView的setAdapter()中设置了DataSetObserver的子类:

    public void setAdapter(ListAdapter adapter) {        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();            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);            if (mItemCount == 0) {                // Nothing selected                checkSelectionChanged();            }        } else {            mAreAllItemsSelectable = true;            checkFocus();            // Nothing selected            checkSelectionChanged();        }        requestLayout();    }

可以看到new了一个AdapterDataSetObserver,并且作为参数传进了BaseAdapter的registerDataSetObserver()

    public void registerDataSetObserver(DataSetObserver observer) {        mDataSetObservable.registerObserver(observer);    }

其中调用了Observable的registerObserver()

    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);        }    }

这里把刚才new的AdapterDataSetObserver添加到了观察者的集合中,当调用notifyDataSetChanged()时就会调用AdapterDataSetObserver的onChange()

    class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {        @Override        public void onChanged() {            super.onChanged();            if (mFastScroll != null) {                mFastScroll.onSectionsChanged();            }        }        @Override        public void onInvalidated() {            super.onInvalidated();            if (mFastScroll != null) {                mFastScroll.onSectionsChanged();            }        }    }

AdapterDataSetObserver 是ListView的一个内部类,它继承自AdapterView的一个也叫AdapterDataSetObserver 内部类:

        @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();        }

在这里可以看到,notifyDataSetChanged()最终调用了requestLayout(),请求了View的重绘。

关于requestLayout(),可以看这篇文章View的绘制流程(Android开发艺术探索学习笔记)。

很多时候我们只要刷新ListView中的一条item即可,不用每次都notifyDataSetChanged(),因为每次对整个ListView进行重绘,很耗费性能,所以下面来介绍一下ListView的局部刷新。

其实很简单就是手动调用一下Adapter的getView(),但是还是要根据实际情况,倘若你需要刷新的这个item还没在界面上展示就没必要刷新了,因为等到它要出现在界面上的时候自然会调用getView()。下面看一下Google推荐的做法:

    private void updateItem(int position) {        /**第一个可见的位置**/        int firstVisiblePosition = listView.getFirstVisiblePosition();        /**最后一个可见的位置**/        int lastVisiblePosition = listView.getLastVisiblePosition();        /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/        if (position >= firstVisiblePosition && position <= lastVisiblePosition) {            /**获取指定位置view对象**/            View view = listView.getChildAt(position - firstVisiblePosition);            adapter.getView(position, view, listView);        }    }

参考:
1.Android源码分析之NotifyDataSetChanged()
2.Android ListView优化之局部刷新(更新)(非notifyDataSetChanged)

阅读全文
1 0
原创粉丝点击