最近一直在研究关于安卓中常用控件的源码实现,也参考了不少文章,希望通过自己的总结加深一下记忆,我会从一个view的绘制流程去分析这个控件
作为安卓中最常用的控件ListView,我觉很很有必要学习一下Google的大牛是如何实现这种比较复杂的控件,包括ListVIew的绘制流程,ListView的缓存机制,以及封装思想,对今后自己能早出更好的轮子有所帮助.
注 : 所有的源码都是来自安卓5.1版本.
本文将从以下角度对安卓中最常用的控件ListView进行分析
- ListView的构造方法
- onMeasure
- onLayout
- Item的填充
- Item的布局
- setAdapter
- notifyDataSetChanged
- 以及ListView的回收机制
ListView的构造
我们先从一个类的最开始构造方法开始研究,第二行,ListView在初始化的时候,先执行了super(context, attrs, defStyleAttr, defStyleRes)
方法,ListView的父类是AbsListView
,所以我们先看下父类的初始化究竟做了什么
public ListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); ... }
- ListView 父类 AbsListView的构造
父类方法中调用了initAbsListView
进行ListView的初始化配置,之后就是拿到一些自定义属性
public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initAbsListView(); ... 拿到自定义属性省略 }
initAbsListView()
这个方法中给ListView设置了一些初始化状态
private void initAbsListView() { setClickable(true); setFocusableInTouchMode(true); setWillNotDraw(false); setAlwaysDrawnWithCacheEnabled(false); setScrollingCacheEnabled(true); final ViewConfiguration configuration = ViewConfiguration.get(mContext); mTouchSlop = configuration.getScaledTouchSlop(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mOverscrollDistance = configuration.getScaledOverscrollDistance(); mOverflingDistance = configuration.getScaledOverflingDistance(); mDensityScale = getContext().getResources().getDisplayMetrics().density; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
可以看到在初始化状态之后,通过a.getDrawable(com.Android.internal.R.styleable.ListView_divider);
拿到了分割线的样式,这就是是我们通过在style文件中复ListView_divider
可以自定义Item分割线的原因.而且还可以通过复写ListView_overScrollHeader
,ListView_overScrollFooter
设置头部和底部的drawble文件
public ListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.ListView, defStyleAttr, defStyleRes); CharSequence[] entries = a.getTextArray( com.android.internal.R.styleable.ListView_entries); if (entries != null) { setAdapter(new ArrayAdapter<CharSequence>(context, com.android.internal.R.layout.simple_list_item_1, entries)); } final Drawable d = a.getDrawable(com.android.internal.R.styleable.ListView_divider); if (d != null) { setDivider(d); } final Drawable osHeader = a.getDrawable( com.android.internal.R.styleable.ListView_overScrollHeader); if (osHeader != null) { setOverscrollHeader(osHeader); } final Drawable osFooter = a.getDrawable( com.android.internal.R.styleable.ListView_overScrollFooter); if (osFooter != null) { setOverscrollFooter(osFooter); } final int dividerHeight = a.getDimensionPixelSize( com.android.internal.R.styleable.ListView_dividerHeight, 0); if (dividerHeight != 0) { setDividerHeight(dividerHeight); } mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true); mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true); a.recycle(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
最后总结一下,ListView在构造方法中,就是初始化了一些状态,并且将分割线等样式添加了进来,这就是我们可以通过在sylte.xml复写对应的样式达到修改分割线的原因.
onMeasure方法
在onMeasure方法中会根据我们自定义继承BaseAdapter的adpter.getCount
方法拿到所有item的数量,并且通过View child = obtainView(0, mIsScrap);
方法创建view,那么这个view是怎么创建的呢,进去看一下
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ... mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { final View child = obtainView(0, mIsScrap); measureScrapChild(child, 0, widthMeasureSpec); childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (recycleOnMeasure() && mRecycler.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { mRecycler.addScrapView(child, 0); } } ... 省略 以下是对ListView的测量并赋值给成员变量 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
可以看到最终也是调用了mAdapter.getView(position, scrapView, this);
创建child,getView中的参数scrapView 就是被回收的view对象,后面会讲到
View obtainView(int position, boolean[] isScrap) { ... final View scrapView = mRecycler.getScrapView(position); final View child = mAdapter.getView(position, scrapView, this); ... return child; }
总结一下.在onMeasure方法中,会通过我们设置进来的mAdpter的getCount方法拿到item的数量,通过getView的方法拿到我们创建的每一个view,当然ListVIew第一次创建的时候并没有mAdapter的存在,只有在setAdapter被我们调用过后才会执行这些方法,也就是说在setAdapter中一定会调用requestLayout
方法重新走一遍流程,这个下面会进行讲解.
onLayout方法
通过搜索发现ListView中并没有onLayout方法,那也就是说一定是在他的父类AbsListView
中,我们可以看到它调用了layoutChildren()
,从方法名看应该是对子view进行布局,这个layoutChildren是一个空实现方法,也就是说应该是通过AbsListView的子类ListVIew
和GridView
进行实现
protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mInLayout = true; final int childCount = getChildCount(); if (changed) { for (int i = 0; i < childCount; i++) { getChildAt(i).forceLayout(); } mRecycler.markChildrenDirty(); } layoutChildren(); mInLayout = false; mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR; if (mFastScroll != null) { mFastScroll.onItemCountChanged(getChildCount(), mItemCount); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- listView.layoutChildren()
这个方法比较长,我们具体看重点,这个方法中会判断是否通过adapter进行添加数据的操作,并通过fillXXX()
方法进行对ItemView的填充,并且有两个很重要的对象:
1.View[] mActiveViews
:存放的是当前ListView可以使用的待激活的子item view
2.ArrayList<View>[] mScrapViews
:存放的是在ListView滑动过程中滑出屏幕来回收以便下次利用的子item view
@Override protected void layoutChildren() { ... final int firstPosition = mFirstPosition; final RecycleBin recycleBin = mRecycler; if (dataChanged) { for (int i = 0; i < childCount; i++) { recycleBin.addScrapView(getChildAt(i), firstPosition+i); } } else { recycleBin.fillActiveViews(childCount, firstPosition); } ... switch (mLayoutMode) { ... default: if (childCount == 0) { if (!mStackFromBottom) { final int position = lookForSelectablePosition(0, true); setSelectedPositionInt(position); sel = fillFromTop(childrenTop); } else { final int position = lookForSelectablePosition(mItemCount - 1, false); setSelectedPositionInt(position); sel = fillUp(mItemCount - 1, childrenBottom); } } else { if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) { sel = fillSpecific(mSelectedPosition, oldSel == null ? childrenTop : oldSel.getTop()); } else if (mFirstPosition < mItemCount) { sel = fillSpecific(mFirstPosition, oldFirst == null ? childrenTop : oldFirst.getTop()); } else { sel = fillSpecific(0, childrenTop); } } break; } ... mLayoutMode = LAYOUT_NORMAL; mDataChanged = false; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
总结一下,通过onLayout方法,就将item填充到了ListView中
Item的填充与Item的布局
我们刚才讲到,在layoutChidren中有几个以fill开头的方法就是具体的Item的填充方法,
这个方法中会根据mStackFromBottom参数判断填充方向,通过fillUp
,fillDown
进行填充
private View fillSpecific(int position, int top) { boolean tempIsSelected = position == mSelectedPosition; View temp = makeAndAddView(position, top, true, mListPadding.left, tempIsSelected); mFirstPosition = position; View above; View below; final int dividerHeight = mDividerHeight; if (!mStackFromBottom) { above = fillUp(position - 1, temp.getTop() - dividerHeight); adjustViewsUpOrDown(); below = fillDown(position + 1, temp.getBottom() + dividerHeight); int childCount = getChildCount(); if (childCount > 0) { correctTooHigh(childCount); } } else { below = fillDown(position + 1, temp.getBottom() + dividerHeight); adjustViewsUpOrDown(); above = fillUp(position - 1, temp.getTop() - dividerHeight); int childCount = getChildCount(); if (childCount > 0) { correctTooLow(childCount); } } if (tempIsSelected) { return temp; } else if (above != null) { return above; } else { return below; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
以fillDown()
举例
第一次进入nextTop就是padding,也就是最顶部的位置,通过一个while循环,只要nextTop没有超出end(ListView内容高度)就一直makeAndAddView()
创建view,nextTop在循环里会根据Item数量进行循环赋值,只要判断当前这个item的nextTop超出listView,就停止这个循环,通过这种方法就将可见view都填充出来了
private View fillDown(int pos, int nextTop) { View selectedView = null; int end = (mBottom - mTop); if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) { end -= mListPadding.bottom; } while (nextTop < end && pos < mItemCount) { boolean selected = pos == mSelectedPosition; View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected); nextTop = child.getBottom() + mDividerHeight; if (selected) { selectedView = child; } pos++; } setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1); return selectedView; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected)
listView就是通过这个方法调用obtainView(position, mIsScrap)
mAdapter.getView创建view,然后通过setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
这个方法进行对子view的布局,记住这些方法都在while循环中,
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) { View child; if (!mDataChanged) { child = mRecycler.getActiveView(position); if (child != null) { setupChild(child, position, y, flow, childrenLeft, selected, true); return child; } } child = obtainView(position, mIsScrap); setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,
boolean selected, boolean recycled)
在这个方法中,通过拿到上面while循环传经来的参数,调用了子child的measure和layout方法进行测量和绘制,到此listView中可见区域的view就被填充出来了.
private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft, boolean selected, boolean recycled) { ... if (needToMeasure) { int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, mListPadding.left + mListPadding.right, p.width); int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } else { cleanupLayoutState(child); } ... child.layout(childrenLeft, childTop, childRight, childBottom); ... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
上面就是ListView中item的填充,下面我们来看看setAdapter中究竟做了什么操作
setAdapter
setAdapter中通过mAdapter.registerDataSetObserver(mDataSetObserver);
注册一个AdapterDataSetObserver
订阅者,每当调用notifyDataSetChange
的时候,就会触发AdapterDataSetObserver
的onChanged
的方法,这个是观察者模式,不懂得可以参考下其他文章,这里就不多做赘述,这个方法最终调用requestLayout方法,也就是说我们每次setAdapter之后就会重新布局,这时候mAdapter不为空,就会走刚才所说的绘制流程.
@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 = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } mOldSelectedPosition = INVALID_POSITION; mOldSelectedRowId = INVALID_ROW_ID; 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) { checkSelectionChanged(); } } else { mAreAllItemsSelectable = true; checkFocus(); checkSelectionChanged(); } requestLayout(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
notifyDataSetChanged
这个方法在BaseAdapter
中
public void notifyDataSetChanged() { mDataSetObservable.notifyChanged();}
这时候根据观察者模式,会调用订阅者AdapterDataSetObserver
的onChanged方法,上面提到过,最终还是会调用requestLayout进行重新布局
@Override public void onChanged() { mDataChanged = true; mOldItemCount = mItemCount; mItemCount = getAdapter().getCount(); if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null && mOldItemCount == 0 && mItemCount > 0) { AdapterView.this.onRestoreInstanceState(mInstanceState); mInstanceState = null; } else { rememberSyncState(); } checkFocus(); requestLayout(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
ListView的回收机制
最后我们来看看ListView的复用机制
要想了解这方面,先要从ListView滑动开始看,滚动核心方法AbsListView
的trackMotionScroll
,在这个方法中实现了对ListView,Item的缓存
第一步 : 这个方法会先判断我们先在滑动的位置是否已经到最顶部,或者最底部,如果到了边界值,就不能再滑动了
第二步 : 拿手指向上移动,也就是下滑状态来说名:先遍历所有的item,如果发现这个item的底部还在可视范围之内,说明这个item还没有销毁,如果超出,则表示需要被缓存起来,也就是会加入到mRecycler的mScrapViews(超出屏幕的集合)中保存,这个集合之前有说过,专门用来保存超出屏幕的Item.并将划出的view通过detachViewsFromParent
从ListView中detach掉
第三步 : 判断是否有Item滚入了ListView中,如果滚入,调用fillGap
方法进行填充,这个方法中会调用之前说过的fillDown
或者fillUp
方法填充item,并添加到mActivated
(当前屏幕中Item的集合)中,这样就实现了Item的缓存.
boolean trackMotionScroll(int deltaY, int incrementalDeltaY) { final int childCount = getChildCount(); if (childCount == 0) { return true; } .... final boolean cannotScrollDown = (firstPosition == 0 && firstTop >= listPadding.top && incrementalDeltaY >= 0); final boolean cannotScrollUp = (firstPosition + childCount == mItemCount && lastBottom <= getHeight() - listPadding.bottom && incrementalDeltaY <= 0); if (cannotScrollDown || cannotScrollUp) { return incrementalDeltaY != 0; } final boolean down = incrementalDeltaY < 0; ... int start = 0; int count = 0; if (down) { int top = -incrementalDeltaY; if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) { top += listPadding.top; } for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); if (child.getBottom() >= top) { break; } else { count++; int position = firstPosition + i; if (position >= headerViewsCount && position < footerViewsStart) { child.clearAccessibilityFocus(); mRecycler.addScrapView(child, position); } } } } else { ...向下移动 省略 和上面逻辑相同 } mMotionViewNewTop = mMotionViewOriginalTop + deltaY; mBlockLayoutRequests = true; if (count > 0) { detachViewsFromParent(start, count); mRecycler.removeSkippedScrap(); } if (!awakenScrollBars()) { invalidate(); } offsetChildrenTopAndBottom(incrementalDeltaY); if (down) { mFirstPosition += count; } final int absIncrementalDeltaY = Math.abs(incrementalDeltaY); if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) { fillGap(down); } if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { final int childIndex = mSelectedPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { positionSelector(mSelectedPosition, getChildAt(childIndex)); } } else if (mSelectorPosition != INVALID_POSITION) { final int childIndex = mSelectorPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { positionSelector(INVALID_POSITION, getChildAt(childIndex)); } } else { mSelectorRect.setEmpty(); } mBlockLayoutRequests = false; invokeOnItemScrollListener(); return false; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
以上就是对ListView绘制流程以及其中的观察者模式等等,后面我也会对ReyclerView 以及ViewPager进行源码分析,希望通过这种方式,更加深刻的了解各种View的实现,对以后自定义控件的编写提供更好的思想.
转自:http://blog.csdn.net/hfyd_/article/details/53768690?_t_t_t=0.8988156646955758