Android移动架构汇总
一、观察者模式在ListView中的运用
观察者模式(建议先熟悉观察者模式的角色定义和调用流程)
BaseAdapter是被观察者,ListView是观察者,被观察者一般有一个存储观察接口的集合,去源码中查找BaseAdapter中的这个集合:
//集合的封装
private final DataSetObservable mDataSetObservable = new DataSetObservable();
//找到注册观察者的方法:
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
ListView是观察者的话,那么得有observer对象(DataSetObserver)用于注册,查看ListView的setAdapter方法,找到了注册观察者的地方:
public void setAdapter(ListAdapter adapter) {
...
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
...
AdapterDataSetObserver在ListView的父类ABSListView中被定义,类中定义了被通知后调用的onChange方法:
class AdapterDataSetObserver {
@Override
public void onChanged() {
...
}
那么onChange方法是在哪个地方被调用的呢?
我们知道ListView通过Adapter的notifyDataSetChanged方法来刷新数据,进入到这个方法:
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
进入到mDataSetObservable的notifyChanged方法中:
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
mObservers.get(i).onChanged();
}
}
}
这就调用了注册者的onChange方法
AdapterDataSetObserver的onChange方法调用,刷新UI:
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
二、ListView中的View重用机制
ListView 具有View重用、高度扩展(ListView、GridView都设置adapter)的特点,如果我们自己去定义一个ListView该如何去做呢?
首先肯定要继承ViewGroup,重写对应的方法:onLayout、onTouchEvent、onMeasure
显示与创建分离
数据适配器 + ViewGroup
数据适配器封装了类似BaseAdapter的功能,填充自定义ListView的View从数据适配器过来的,适配器肯定提供一个getView的方法。
数据适配器 + ViewGroup
数据适配器封装了类似BaseAdapter的功能,填充自定义ListView的View从数据适配器过来的,适配器肯定提供一个getView的方法。
但是View不能直接从Adapter的getView方法直接返回(需要重用机制),需要设计一个View的池子,专门回收View(生产消费模式)。View先从池子中获取,如果没有再去Adapter的getView方法中去获取,adapter.getView是生产者,getView返回View到池子中,这样设计,ListView获取View与数据适配器就耦合了,中间通过回收池关联。
为了证明这一点,到ListView的源码中去:
1、ListView的初始化中,加载第一屏数据
ListView--AbsListView--AdapterView
1) 先查找onLayout方法,ListView 中没有,在其父类AbsListView中找到,onLayout方法主要功能:
layoutChildren();
2)layoutChildren()是空实现,子类必须要实现这个方法(因为AbsListView的子类如ListView和GridView的摆放方式不同-模板模式:onLayout算法固定,layoutChildren让子类实现)
3)进入到ListView的layoutChildren方法中:
switch(mLayoutMode) 判断模式会走到默认default中去,刚开始childrenCount == 0
sel = fillFromTop(childrenTop);//从上往下进行填充
sel = fillFromTop(childrenTop);//从上往下进行填充
4)fillFromTop:
private View fillFromTop(intnextTop) {
mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);
mFirstPosition = Math.min(mFirstPosition, mItemCount -1);
if(mFirstPosition <0) {
mFirstPosition =0;
}
return fillDown(mFirstPosition, nextTop);
//mFirstPosition为第一个可见元素的位置,当前的可见元素为0;
mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);
mFirstPosition = Math.min(mFirstPosition, mItemCount -1);
if(mFirstPosition <0) {
mFirstPosition =0;
}
return fillDown(mFirstPosition, nextTop);
//mFirstPosition为第一个可见元素的位置,当前的可见元素为0;
5)fillDown:
加载一屏数据
while(nextTop < end && pos < mItemCount) {
...
View child = makeAndAddView(pos, nextTop,true, mListPadding.left, selected);
nextTop = child.getBottom() + mDividerHeight;
//nextTop 进行累加,如果超出屏幕则跳出循环
加载一屏数据
while(nextTop < end && pos < mItemCount) {
...
View child = makeAndAddView(pos, nextTop,true, mListPadding.left, selected);
nextTop = child.getBottom() + mDividerHeight;
//nextTop 进行累加,如果超出屏幕则跳出循环
6)makeAndAddView:
->finalView child = obtainView(position, mIsScrap);
->AbsListView:obtainView
->第一次没有从回收池中拿View,而是通过Adapter的getView方法
finalView updatedView =mAdapter.getView(position, transientView,this);
obtainView返回child到makeAndAddView中去,此时还没有添加到ListView中
finalView child = obtainView(position, mIsScrap)
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
->finalView child = obtainView(position, mIsScrap);
->AbsListView:obtainView
->第一次没有从回收池中拿View,而是通过Adapter的getView方法
finalView updatedView =mAdapter.getView(position, transientView,this);
obtainView返回child到makeAndAddView中去,此时还没有添加到ListView中
finalView child = obtainView(position, mIsScrap)
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
7)setupChild()中:
...
addViewInLayout(child, flowDown ? -1: 0, p,true);//ViewGroup中定义
将child绑定到ListView中
...
addViewInLayout(child, flowDown ? -1: 0, p,true);//ViewGroup中定义
将child绑定到ListView中
2、View的复用
1)看ListView的onLayout(adapter.notify->观察者onChange->ListView重新绘制->onLayout),
跟上面一样,还是调用了layoutChildren方法
跟上面一样,还是调用了layoutChildren方法
2)layoutChildren中:
if (dataChanged) {
for(inti = 0; i < childCount; i++) {
//添加View
recycleBin.addScrapView(getChildAt(i), firstPosition+i);
}
} else{
recycleBin.fillActiveViews(childCount, firstPosition);
}
a) recycleBin->AbsListView:RecycleBin;RecycleBin中有一个集合mScrapViews用来存放被滑出屏幕的item,
b)addScrapView:scrap.dispatchStartTemporaryDetach();//View中的方法,解除View和parent之间的关系,否则View是不能被重复添加的,这是因为在ViewTree中所有的View只有一个parent:
c) layoutChildren继续执行:
sel = fillSpecific(mSelectedPosition,
oldSel ==null? childrenTop : oldSel.getTop());
->fillSpecific
->makeAndView//第三个参数为true:
finalView activeView = mRecycler.getActiveView(position);
activeView 不为null,执行setUpChild方法
sel = fillSpecific(mSelectedPosition,
oldSel ==null? childrenTop : oldSel.getTop());
->fillSpecific
->makeAndView//第三个参数为true:
finalView activeView = mRecycler.getActiveView(position);
activeView 不为null,执行setUpChild方法
//将View和Parent重新关联起来
attachViewToParent(child, flowDown ? -1: 0, p);
综合一和二,第一次使用addViewParent(添加),复用时使用attachViewToParent(绑定,性能高);
attachViewToParent(child, flowDown ? -1: 0, p);
综合一和二,第一次使用addViewParent(添加),复用时使用attachViewToParent(绑定,性能高);
三、滑动过程中的实现
1、onTouchEvent:(AbsListView中实现),找Move事件
->onTouchEvent:switch(mTouchMode) {走TOUCH_MODE_SCROLL
scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev);
2、scrollIfNeeded:
int incrementalDeltaY =
mLastY!= Integer.MIN_VALUE? y - mLastY+ scrollConsumedCorrection : deltaY;
//判断向上还是向下滑
->trackMotionScroll
3、trackMotionScroll:
确定边界
final int firstTop = getChildAt(0).getTop();
final int lastBottom = getChildAt(childCount - 1).getBottom();
...
final boolean down = incrementalDeltaY < 0;//表示向下滑
..
if(down){
//向下
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getBottom() >= top) {
break;
} else {//child.getBottom()< top-->当前child滑出了
count++;
int position = firstPosition + i;
if (position >= headerViewsCount && position < footerViewsStart) {
child.clearAccessibilityFocus();
//添加到回收池
mRecycler.addScrapView(child, position);
}
}
}ele{
//向上滑动
int bottom = getHeight() - incrementalDeltaY;
if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
bottom -= listPadding.bottom;
}
for (int i = childCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child.getTop() <= bottom) {
break;
} else {//child.getTop() > bottom
start = i;
count++;
int position = firstPosition + i;
if (position >= headerViewsCount && position < footerViewsStart) {
// The view will be rebound to new data, clear any
// system-managed transient state.
child.clearAccessibilityFocus();
mRecycler.addScrapView(child, position);
}
}
}
}
if (count > 0) {
//解绑
detachViewsFromParent(start, count);
mRecycler.removeSkippedScrap();
}
...
fillGap(down);
4.fillGap:ListView中实现(模板模式)
->fillDown
->makeAndAddView(调用adapter.getView的),第三个参数为true
->setupChild
从而将View添加或关联到ListView中去
...
attachViewToParent(child, flowDown ? -1: 0, p);
...
else{
...
addViewInLayout(child, flowDown ? -1 : 0, p, true);
...
else{
...
addViewInLayout(child, flowDown ? -1 : 0, p, true);