浅析listview及Adapter原理二

之前有说过adapter如何跟listview产生联系,以及通知listview去全局刷新requestlayout。请参考 浅析listview及Adapter原理一。接下来就要看下全局刷新的时候跟adapter之间又做了什么交互?

(1) listview测量过程中与adapter交互

全局刷新就会调用到视图的onMeasure,onLayout,onDraw流程。我们参考下listview的部分源码段如下:

OnMeasure():

        if (heightMode == MeasureSpec.AT_MOST) {
            // TODO: after first layout we should maybe start at the first visible position, not 0
            heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
        }


measureHeightOfChildren():

   for (i = startPosition; i <= endPosition; ++i) {
           child = obtainView(i, isScrap);
          measureScrapChild(child, i, widthMeasureSpec);
}


!!!obtainView就是创建子view的场所:

        scrapView = mRecycler.getScrapView(position);

        View child;
        if (scrapView != null) {
            child = mAdapter.getView(position, scrapView, this);
       }else{
            child = mAdapter.getView(position, null, this);
      }

这个地方,adapter.getView去获取视图(同时listview中有缓存模块RecyclerBin记录所缓存的视图),稍后接受recyclebin大致缓存策略。

!!!:measureScrapChild()方法:

     LayoutParams p = (LayoutParams) child.getLayoutParams();
        if (p == null) {
            p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
            child.setLayoutParams(p);
        }
        p.viewType = mAdapter. getItemViewType(position);
        p.forceAdd = true;

        int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
                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);

当前测量首先会对子view本身进行测量,同时用到 adapter.getItemViewType(position) ,这个方法主要是设置当前item的缓存视图数组中的索引,稍后做说明。


(2)listview视图缓存机制跟adapter如何交互?

我们都知道listview内部是有缓存视图的,否则我们数据条目过大的时候,会给UI绘制带来极大的压力。下面我们来看看adapter接口如何来控制缓存视图策略的。

1.  getView(position, convertview, viewgroup)接口

相信大家并不陌生,这个接口我们通常会用来定义每条item的视图,并且绑定数据显示。我们也知道convertview一开始的时候是为空的,但是有的时候又不为空可以直接拿来用。这是不是就是视图缓存?

前面我们在测量过程分析 obtainView是去创建视图的,有个方法obtainView中用到了adapter.getView(),大家有没有注意到之前一句代码

scrapView = mRecycler.getScrapView(position);  这个就是通过RecycleBin这个视图缓存管理器去获取缓存视图的方法。


(2) getViewTypeCount()

该方法字面上理解是视图类型的数量, 有点疑惑,通常我们并不会主动去设置这接口,我们看调用它的位置,

我们知道适配器与listview通过setAdapter方法建立联系。

ListView中setAdapter:
         mAdapter.registerDataSetObserver(mDataSetObserver);
         mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());   


RecyleBin中setViewTypeCount:
        public void setViewTypeCount(int viewTypeCount) {
            if (viewTypeCount < 1) {
                throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
            }
            //noinspection unchecked
            ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
            for (int i = 0; i < viewTypeCount; i++) {
                scrapViews[i] = new ArrayList<View>();
            }
            mViewTypeCount = viewTypeCount;
            mCurrentScrap = scrapViews[0];
            mScrapViews = scrapViews;
        }

我们可以知道recycleBin中根据viewTypeCount创建一个视图缓存数组,listview可能直接使用缓存视图。

(3)getItemViewType(int position)

前面我们知道listview内部维护了缓存视图数组,那么问题来了,我们每一条item怎么就获取到正确的缓存视图呢。前面obtainview创建视图时候 mRecycler.getScrapView(position)获取缓存视图。

具体源码:
        View getScrapView(int position) {
            if (mViewTypeCount == 1) {
                return retrieveFromScrap(mCurrentScrap, position);
            } else {
                int whichScrap = mAdapter.getItemViewType(position);
                if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
                    return retrieveFromScrap(mScrapViews[whichScrap], position);
                }
            }
            return null;
        }

我们发现getItemViewType(position)方法实际是获取视图缓存数组中的索引,找到对应缓存视图。


既然有用到获取视图缓存,自然需要将视图添加到缓存中,我们视图在测量绘制阶段会加入到listview缓存数组中。
       
void addScrapView(View scrap, int position) {
            AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
            if (lp == null) {
                return;
            }

            lp.scrappedFromPosition = position;

            int viewType = lp.viewType;
            final boolean scrapHasTransientState = scrap.hasTransientState();
            if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) {
                    if (mSkippedScrap == null) {
                        mSkippedScrap = new ArrayList<View>();
                    }
                    mSkippedScrap.add(scrap);
                }
                if (scrapHasTransientState) {
                    if (mTransientStateViews == null) {
                        mTransientStateViews = new SparseArray<View>();
                    }
                    scrap.dispatchStartTemporaryDetach();
                    mTransientStateViews.put(position, scrap);
                }
                return;
            }

            scrap.dispatchStartTemporaryDetach();
            if (mViewTypeCount == 1) {
                mCurrentScrap.add(scrap);
            } else {
                mScrapViews[viewType].add(scrap);
            }

            scrap.setAccessibilityDelegate(null);
            if (mRecyclerListener != null) {
                mRecyclerListener.onMovedToScrapHeap(scrap);
            }
        }

以上的viewtype是通过当前AbsListView.Layoutparam获取,而前面 measureScrapChild方法中

LayoutParams p = (LayoutParams) child.getLayoutParams();
        if (p == null) {
            p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
            child.setLayoutParams(p);
        }
        p.viewType = mAdapter.getItemViewType(position);
        p.forceAdd = true;

则将视图缓存索引添加进LayoutParams


总结:以上是adapter跟listview视图缓存建立关系,getViewTypeCount用于产生视图缓存容器大小,getItemViewType(int position)则定位当前视图缓存容器的索引,getView则是用于创建视图,或者拿缓存视图进行更新。

(如果有问题的请各位大神指正!!!)
















猜你喜欢

转载自blog.csdn.net/u011098381/article/details/79216652