移动架构18_ListView源码分析

来源:互联网 发布:软件模块接口参数 编辑:程序博客网 时间:2024/06/16 00:35
一、观察者模式在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();      }  } }
这就调用了注册者的onChange方法
AdapterDataSetObserver的onChange方法调用,刷新UI:
public void onChanged() {super.onChanged();if (mFastScroll != null) {mFastScroll.onSectionsChanged();}}
ss
二、ListView中的View重用机制
ListView 具有View重用、高度扩展(ListView、GridView都设置adapter)的特点,如果我们自己去定义一个ListView该如何去做呢?
首先肯定要继承ViewGroup,重写对应的方法:onLayout、onTouchEvent、onMeasure
显示与创建分离
数据适配器 + ViewGroup
数据适配器封装了类似BaseAdapter的功能,填充自定义ListView的View从数据适配器过来的,适配器肯定提供一个getView的方法。

但是View不能直接从Adapter的getView方法直接返回(需要重用机制),需要设计一个View的池子,专门回收View(生产消费模式)。View先从池子中获取,如果没有再去Adapter的getView方法中去获取,adapter.getView是生产者,getView返回View到池子中,这样设计,ListView获取View与数据适配器就耦合了,中间通过回收池关联。

ss

为了证明这一点,到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);//从上往下进行填充
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;
5)fillDown:
加载一屏数据
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]);
7)setupChild()中:
...
addViewInLayout(child, flowDown ? -1: 0, p,true);//ViewGroup中定义
将child绑定到ListView中
2、View的复用
1)看ListView的onLayout(adapter.notify->观察者onChange->ListView重新绘制->onLayout),
跟上面一样,还是调用了layoutChildren方法
2)layoutChildren中:
if (dataChanged) {for(inti = 0; i < childCount; i++) {//添加ViewrecycleBin.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方法
ss
3) setUpChild: 
//将View和Parent重新关联起来 
attachViewToParent(child, flowDown ? -1: 0, p);
综合一和二,第一次使用addViewParent(添加),复用时使用attachViewToParent(绑定,性能高);
ss
三、滑动过程中的实现
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);
ss







原创粉丝点击