移动架构18_ListView源码分析

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();
	}
}
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;	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++) {
//添加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方法
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





 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值