SeniorUI13_ListView源码分析

SeniorUI_高级UI汇总目录

1 onLayout方法

  • ListView继承了AbsListView,AbsListView是ViewGroup的子类;自定义控件继承了ViewGroup的话,会重写onLayout方法
  • ListView没有重写onLayout方法,但是其父类重写了
    ABSListView.onLayout
 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;

        // TODO: Move somewhere sane. This doesn't belong in onLayout().
        if (mFastScroll != null) {
            mFastScroll.onItemCountChanged(getChildCount(), mItemCount);
        }
    }

  • AbsListView 的onLayout方法中,比较关键的就是调用了layoutChildren方法,此方法在ListView中具体实现了

2 ListView的layoutChildren()方法

  • 在layoutChildren中会有一个mLayoutMode的switch判断,走默认代码块调用fillFromTop方法
  • fillFromTop调用fillDown方法 ,fillDown中会通过makeAndAddView获取每一个child
  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) {
            // is this the selected item?
            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;
    }

3 makeAndAddView方法

  private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
            boolean selected) {
        if (!mDataChanged) {
            // Try to use an existing view for this position.
            final View activeView = mRecycler.getActiveView(position);
            if (activeView != null) {
                // Found it. We're reusing an existing child, so it just needs
                // to be positioned like a scrap view.
                setupChild(activeView, position, y, flow, childrenLeft, selected, true);
                return activeView;
            }
        }

        // Make a new view for this position, or convert an unused view if
        // possible.
        final View child = obtainView(position, mIsScrap);

        // This needs to be positioned and measured.
        setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);

        return child;
    }
  • mRecycler.getActiveView(position); 从缓存中获取每个child,此时返回的View有可能为空
  • obtainView 方法的调用是传入缓存中取出的convertView,最终调用adapter的getView方法确定每个child的值,obtainView在AbsListView中具体实现
  • setupChild方法 设置child的大小和布局位置

4 AbsListView 的obtainView 方法

obtainView:中会执行以下内容

 final View scrapView = mRecycler.getScrapView(position);
        final View child = mAdapter.getView(position, scrapView, this);
        if (scrapView != null) {
            if (child != scrapView) {
                // Failed to re-bind the data, return scrap to the heap.
                mRecycler.addScrapView(scrapView, position);
            } else if (child.isTemporarilyDetached()) {
                outMetadata[0] = true;

                // Finish the temporary detach started in addScrapView().
                child.dispatchFinishTemporaryDetach();
            }
        }

从上面可以看出child是从adapter中获取
(我们在给ListView设置适配器时,如果getView返回为null会报错,就是因为此处的调用)

  • mRecycler 是缓存,是ABSListView的内部类RecycleBin,addScrapView添加缓存,getScrapView获取缓存
  • 在addScrapView中会调用 mScrapViews[viewType].add(scrap);
    ,mScrapView的声明:
    private ArrayList[] mScrapViews;
    即通过ArrayList实现缓存View

5 缓存机制

除了RecycleBin缓存View,ListView高效加载的原因,在于只绘制了当前屏幕的数据,向下滑动的时候,再去获取对应的View(从缓存获取,缓存没有调用adapter的getView方法),加载显示

//复用机制的核心,控制了一页屏的绘制
  while (nextBottom > end && pos >= 0) {
            // is this the selected item?
            boolean selected = pos == mSelectedPosition;
            View child = makeAndAddView(pos, nextBottom, false, mListPadding.left, selected);
            nextBottom = child.getTop() - mDividerHeight;
            if (selected) {
                selectedView = child;
            }
            pos--;
        }

代码中的child是下一个child,end是屏幕底部,当nextBottom>end时,停止while循环,整个屏幕填充完成。

发布了211 篇原创文章 · 获赞 63 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/baopengjian/article/details/103089119
今日推荐