接着上篇文章,现在在通过Android实际开发和源码再探观察者模式,listview是我们日常开发中必用的控件,虽然之前就有listview的替代品(recyclerview),现在不去深究两个控件到底谁好谁差,但有一点需要强调下,它们之间有一大相同点,都是通过观察者模式去实现数据更新。
首先,我们先去看一个简单的例子
/**
*
* created by zero on2016-6-1
*
* 通过listview再探观察者模式
*
*/
public class MainActivity extends Activity
{
private ListView lv_simple;
private ArrayAdapter<String> adapter;
private Button btn_add;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_listview);
lv_simple = (ListView) findViewById(R.id.lv_simple);
btn_add = (Button) findViewById(R.id.btn_add);
btn_add.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
adapter.add("will");
adapter.add("go");
adapter.add("on");
adapter.notifyDataSetChanged();
}
});
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_expandable_list_item_1);
adapter.add("my");
adapter.add("heart");
lv_simple.setAdapter(adapter);
}
}
结果
布局文件就一个listview加上一个button,没必要贴代码了,现在直奔主题,核心分析listview和ArrayAdapter之间的关系,我们都知道listview添加adapter通过setAdapter(adapter)方法,当刷新的时候,需要调用adapter.notifyDataSetChanged()方法,首先,我们先看下ArrayAdapter父类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;
}
}
之前都是继承BaseAdapter,却很少了解它的源码,里面部分方法,在最初工作的两年里面,压根没看到过,先对一些方法作出解释,如下:
int getItemViewType(int position)
int getViewTypeCount()
如果ListView需要显示多种类型的内容,就需要有不同的缓存拿来使用。比如一个listview里面有好几种类型的item,就像基数是一种item,偶数是一种item,或者更复杂的布局,这时候这两个方法就可以实现我们需要的页面。
long getItemId(int position)
boolean hasStableIds()
getItemId是干嘛用的?在调用 invalidateView()时,ListView会刷新显示内容。如果内容的id是有效的,系统会跟据id来确定当前显示哪条内容,也就是firstVisibleChild的位置。id是否有效通过hasStableIds()确定。
boolean areAllItemsEnabled()
boolean isEnabled(int position)
在我们点击item的时候,系统会有一个默认的颜色,如果不希望Item点击时出现背景颜色,就使用这两个方法。它们分别针对所有和单个View实现。这个确实有点恶心,之前一直在listview里面找方法,最后没辙了,就在listview里面设置背景色透明,我相信一定会有好多小伙伴和我一样这么做的,哈哈。
registerDataSetObserver(DataSetObserver observer)
unregisterDataSetObserver(DataSetObserver observer)
对于这两个方法,这里作为重点讲述,通过上一篇博客,看到observer是不是瞬间很亲切,根据字义我们应该就明白这是一个注册和取消注册的操作,之前也讲述过,observer和observable通过注册相关联,当observable发生改变的时候,observer会和observable保持同步操作,这里DataSetObservable的是干嘛用的?看到名字就知道是和Observable相关,我们在回忆一下,上文提到observable中一个很重要的条件就是必须要有一个容器,DataSetObservable会不会和容器相关呢?我们继续追查下去。
/**
* A specialization of {@link Observable} for {@link DataSetObserver}
* that provides methods for sending notifications to a list of
* {@link DataSetObserver} objects.
*/
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* Invokes {@link DataSetObserver#onChanged} on each observer.
* Called when the contents of the data set have changed. The recipient
* will obtain the new contents the next time it queries the data set.
*/
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();
}
}
}
/**
* Invokes {@link DataSetObserver#onInvalidated} on each observer.
* Called when the data set is no longer valid and cannot be queried again,
* such as when the data set has been closed.
*/
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
先看到这里,通过上述的注释可知道,当observable发生改变的时候,便会通知DataSetObserver对象的列表发生对应的改变。现在,好像明白了点什么,但是我们没有找到容器,我们继续追查下去,接下来,我们再看下DataSetObserver的父类,如下:
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>();
/**
* Adds an observer to the list. The observer cannot be null and it must not already
* be registered.
* @param observer the observer to register
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is already registered
*/
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);
}
}
/**
* Removes a previously registered observer. The observer must not be null and it
* must already have been registered.
* @param observer the observer to unregister
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is not yet registered
*/
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);
}
}
/**
* Remove all registered observers.
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
我们在理一下,重新提取核心代码,如下:
public class DataSetObservable extends Observable<DataSetObserver>
public abstract class Observable<T> {
protected final ArrayList<T> mObservers = new ArrayList<T>();
}
现在一目了然了,我们找到了容器了,Observable类里面就是注册observer、取消特定某个observer的注册,还有取消容器中所有observer的注册,DataSetObservable里面提供了notifyChanged()方法,遍历mObservers,通知所有observer发生对应的改变,另外,还提供了notifyInvalidated()方法,在数据源失效的时候会调用这个方法。
现在,我们在分析DataSetObserver,毫无疑问,这个肯定就是和Observer相关喽。先看源码,如下:
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
}
对应DataSetObservable中的两个遍历,一切都是那么的清晰,O(∩_∩)O哈哈~
listview和adapter关联是通过setAdapter(adapter)源码如下:
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();
}
listview有4000行左右的代码,此处就拷贝一些必要代码,通过以上代码可知,当mAdapter和mDataSetObserver同时不为null的时候,便会把mDataSetObserver取消注册,当mAdapter不为null的时候,便会在mAdapter里面进行注册了一个观察者。s当数据集发生改变的时候,我们通过adapter.notifyDataSetChanged()方法去改变数据,点进去后,追查到的代码如下:
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
mNotifyOnChange = true;
}
这是ArrayAdapter里面的代码,我们接着追查它的父类BaseAdapter
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
最后还是指向了DataSetObservable的notifyChanged()方法,一个轮回后,又重新回到了原点,遍历通知发生改变。
现在还有一个最后问题,是怎样更新界面的,onchanged()方法里面什么都没操作,到底是在哪里更新的呢?好吧,我们现在再把目光朝前放一放,回到listview源码中,刚刚提到如果adapter不为null的时候,便会注册一个observer到adapter中,关键的两行代码提取,如下:
mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver);
当我们一路追踪AdapterDataSetObserver的时候,发现AdapterDataSetObserver是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;
}
}
在adapter.notifyDataSetChanged()时,便会调用里面的onchangd()方法,通过mItemCount = getAdapter().getCount()获取adapter中数据的数量,通过requestLayout()重新布局刷新界面。
这里写图片描述刚准备到此为止,忽然想起有件事还没做完,现在,我们再简单看下recyclerview与观察者模式,不多说,直接撸代码:
public static abstract class Adapter<VH extends RecyclerView.ViewHolder>
{
private final RecyclerView.AdapterDataObservable mObservable;
//部分代码省略
//接下来的代码会瞬间让你变得熟悉
public final boolean hasObservers()
{
return this.mObservable.hasObservers();
}
public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer)
{
this.mObservable.registerObserver(observer);
}
public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer)
{
this.mObservable.unregisterObserver(observer);
}
public final void notifyDataSetChanged()
{
this.mObservable.notifyChanged();
}
//recyclerview可以单独刷新item,原因就在此
public final void notifyItemChanged(int position)
{
this.mObservable.notifyItemRangeChanged(position, 1);
}
public final void notifyItemRangeChanged(int positionStart, int itemCount)
{
this.mObservable.notifyItemRangeChanged(positionStart, itemCount);
}
public final void notifyItemInserted(int position)
{
this.mObservable.notifyItemRangeInserted(position, 1);
}
public final void notifyItemMoved(int fromPosition, int toPosition)
{
this.mObservable.notifyItemMoved(fromPosition, toPosition);
}
public final void notifyItemRangeInserted(int positionStart, int itemCount)
{
this.mObservable.notifyItemRangeInserted(positionStart, itemCount);
}
public final void notifyItemRemoved(int position)
{
this.mObservable.notifyItemRangeRemoved(position, 1);
}
public final void notifyItemRangeRemoved(int positionStart, int itemCount)
{
this.mObservable.notifyItemRangeRemoved(positionStart, itemCount);
}
}
recyclerview可以指定范围刷新、插入刷新、移动刷新...一个控件,多套布局,多种玩法,这TM是不是有点违背了单一职责原则。。。
AdapterDataObservable
static class AdapterDataObservable extends Observable<RecyclerView.AdapterDataObserver>
{
public boolean hasObservers()
{
return !this.mObservers.isEmpty();
}
public void notifyChanged()
{
for (int i = this.mObservers.size() - 1; i >= 0; --i)
((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onChanged();
}
public void notifyItemRangeChanged(int positionStart, int itemCount)
{
for (int i = this.mObservers.size() - 1; i >= 0; --i)
((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeChanged(positionStart, itemCount);
}
public void notifyItemRangeInserted(int positionStart, int itemCount)
{
for (int i = this.mObservers.size() - 1; i >= 0; --i)
((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeInserted(positionStart, itemCount);
}
public void notifyItemRangeRemoved(int positionStart, int itemCount)
{
for (int i = this.mObservers.size() - 1; i >= 0; --i)
((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeRemoved(positionStart, itemCount);
}
public void notifyItemMoved(int fromPosition, int toPosition)
{
for (int i = this.mObservers.size() - 1; i >= 0; --i)
((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeMoved(fromPosition, toPosition, 1);
}
}
AdapterDataObserver
public static abstract class AdapterDataObserver
{
public void onChanged()
{
}
public void onItemRangeChanged(int positionStart, int itemCount)
{
}
public void onItemRangeInserted(int positionStart, int itemCount)
{
}
public void onItemRangeRemoved(int positionStart, int itemCount)
{
}
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount)
{
}
}
不做多余解释,今天到此为止。
微信扫我,_
网友评论