文章开头先贴下另外几篇设计模式的分析,感兴趣的可以看下:
设计模式-简单说明
ListView适配器模式的应用
ListView观察者模式的应用
AsyncTask模板模式的应用
这篇文章我们主要是分析一下Adapter和AdapterView中对桥接模式的应用,照理我们先简单的说明一下桥接模式的含义和使用场景。
含义:将抽象部分和实现部分分离(注意抽象部分并不一定就是抽象类,实现部分也不一定就是具体类),使他们都可以在自己的维度独立变化
场景:
- 如果觉得继承太繁琐,子类中含有太多平常基本用不到的API,可以使用桥接来代替继承,更加灵活
- 如果一个类存在多维度的独立变化,且各维度可以独立变化
在这里我们先贴一下桥接模式UML类图,简单梳理一下:
从图中我们可以发现AdapterView是一个ViewGroup,Adaper则会提供一个生成View的getView方法,通过桥接模式(AbsListView持有ListAdapter的引用)调用ListAdapter的getView方法将子View添加进父容器中。简单的梳理过后我们看下源码。先从Adapter接口的源码:
/**
* An Adapter object acts as a bridge between an {@link AdapterView} and the
* underlying data for that view. The Adapter provides access to the data items.
* The Adapter is also responsible for making a {@link android.view.View} for
* each item in the data set.
*
* Adapter的类声明,简单明了--> Adapter对象,充当AdapterView和数据视图之间的桥梁。提供了访问数据项的功能
*/
public interface Adapter {
static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
static final int NO_SELECTION = Integer.MIN_VALUE;
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);
int getItemViewType(int position);
int getViewTypeCount();
boolean isEmpty();
default @Nullable CharSequence[] getAutofillOptions() {
return null;
}
}
这些都是很常见的方法了,涉及到观察者模式以及适配器模式的使用,有兴趣的可以点击查看另外两篇文章。根据我的理解,这里使用到桥接模式的原因是:
防止API的膨胀,增加使用成本
那么AdapterView和Adapter是怎么关联的呢,我们接下来看下AdapterView。在AdapterView类的声明中,第一句就是AdapterView是一个由Adapter决定子View长啥样的ViewGroup,在AdapterView的类声明中持有一个继承自Adapter的泛型,然后提供了get/set方法,如下:
/**
* Returns the adapter currently associated with this widget.
*/
public abstract T getAdapter();
/**
* Sets the adapter that provides the data and the views to represent the data
* in this widget.
*/
public abstract void setAdapter(T adapter);
然后在它的子类,ListView的父类AbsListView中,实现了setAdapter,注意AbsListView已经指定了泛型为ListAdapter,看下AbsListView的实现:
@Override
public void setAdapter(ListAdapter adapter) {
if (adapter != null) {
mAdapterHasStableIds = mAdapter.hasStableIds();
if (mChoiceMode != CHOICE_MODE_NONE && mAdapterHasStableIds &&
mCheckedIdStates == null) {
mCheckedIdStates = new LongSparseArray();
}
}
if (mCheckStates != null) {
mCheckStates.clear();
}
if (mCheckedIdStates != null) {
mCheckedIdStates.clear();
}
}
我们惊奇的发现这不科学,因为没有为成员变量mAdapter赋值,不要急,我们看下ListView的setAdapter:
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = wrapHeaderListAdapterInternal(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();
}
在这里主要是做了一些初始化操作,比如先注销原来的观察者,然后注册新的观察者;为成员变量mAdapter赋值;调用requestLayout重新布局子View等,感兴趣的可以看下文章前面另两篇文章的分析。好了,此时我们已经通过setAdapter成功的将Adapter实例保存起来,在需要的地方直接调用即可,比如在setAdapter里面就用到了mAdapter.getCount()方法。
ListView中,其实准确的说应该是Adapter和AdapterView中桥接模式的应用已经分析完毕,如有不足还请指出。