ListView 复用机制


大致的讲解了下ListView的复用的原理,了解手势触摸的时候大致做了什么操作

1.首先查看onTouchEvent,实现是在AbsListView里面

  case MotionEvent.ACTION_MOVE:
  {
        onTouchMove(ev, vtev);
        break;
  }

2.onTouchMove方法中,实现的过程,其中的mTouchMode 在滑动的时候取值为TOUCH_MODE_SCROLL

private void onTouchMove(MotionEvent ev, MotionEvent vtev) 
{
    case TOUCH_MODE_SCROLL:
    case TOUCH_MODE_OVERSCROLL:
            scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev);
            break;
}

3.scrollIfNeeded方法的实现为

 private void scrollIfNeeded(int x, int y, MotionEvent vtev) 
 {
     。。。
     final int deltaY = rawDeltaY;
     int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY;
     int overScrollDistance = -incrementalDeltaY;
     trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
     。。。
 }

4.trackMotionScroll方法的实现为这里传递的为滑动的距离

 boolean trackMotionScroll(int deltaY, int incrementalDeltaY)
  {
        //如果incrementalDeltay小于0,代表向下滑动,如果大于0,代表向上滑动
        final boolean down = incrementalDeltaY < 0; 
        final int headerViewsCount = getHeaderViewsCount();
        final int footerViewsStart = mItemCount - getFooterViewsCount();
        int start = 0;
        int count = 0;

        if (down) {
            //向上滑动,界面会往上移动,注意第一个item消失的时机点,top这里为正数,代表要滑动的距离,如果top > child.getBottom代表当前的item就要从屏幕上消失了,就可以添加到缓存机制中
            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);
                //如果第一个childViewd的都不满足消失的点,就没有必要继续往下遍历了,优化。。。。
                if (child.getBottom() >= top) {   
                    break;
                } else {
                    //child.getBottom < top 代表当前的item要从屏幕上消失了
                    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();
                        //将当前要移除的item,添加到缓存机制中
                        mRecycler.addScrapView(child, position);
                    }
                }
            }
        } else {
            //如果是往下滑动,最后的一个item将会从屏幕上消失,这里要缓存起来,这里的incrementalDeltay为正数,这里的bottom代表最后一个item的bottom值
            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) { //最后一个item都不满足,就没有必要再次遍历了
                    break;
                } else {
                    // child.getTop() > bottom 最后一个item要消失了,缓存
                    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) {
            //將当前要移除屏幕的item,从父布局中移除
            detachViewsFromParent(start, count);
            mRecycler.removeSkippedScrap();
        }

        // invalidate before moving the children to avoid unnecessary invalidate
        // calls to bubble up from the children all the way to the top
        if (!awakenScrollBars()) {
           invalidate();
        }

        offsetChildrenTopAndBottom(incrementalDeltaY);

        if (down) {
            mFirstPosition += count;
        }

        //取的是滑动的距离,这里是绝对值
        final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
        if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
            fillGap(down);
            }
        }
    }

5.fillGap(down) 函数 在AbsListView中为 abstract void fillGap(boolean down); 是一个抽象的方法,所以实现类在具体的子类里面,这里为ListView,传递的参数down代表是否是从下往上的填充布局

    /**
     * {@inheritDoc}
     */
    @Override
    void fillGap(boolean down) {
        final int count = getChildCount();
        if (down) {
            int paddingTop = 0;
            if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
                paddingTop = getListPaddingTop();
            }
            final int startOffset = count > 0 ? getChildAt(count - 1).getBottom() + mDividerHeight :
                    paddingTop;
            fillDown(mFirstPosition + count, startOffset);
            correctTooHigh(getChildCount());
        } else {
            int paddingBottom = 0;
            if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
                paddingBottom = getListPaddingBottom();
            }
            final int startOffset = count > 0 ? getChildAt(0).getTop() - mDividerHeight :
                    getHeight() - paddingBottom;
            fillUp(mFirstPosition - 1, startOffset);
            correctTooLow(getChildCount());
        }
    }

6.这里的函数有俩个主要的函数 fillDown(mFirstPosition + count, startOffset);或者 fillUp(mFirstPosition - 1, startOffset);我们查看那一个即可

 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;
        }
        //这个可以保证childView是有限个的,只是一个屏幕的范围
        while (nextTop < end && pos < mItemCount) {
            // is this the selected item?
            boolean selected = pos == mSelectedPosition;
            //得到一个view,会先去从缓存中获取到,如果获取的为空话,适配器自己去创建一个View
            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;
    }

7.makeAndAddView(pos, nextTop, true, mListPadding.left, selected);函数的实现为

 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.
        //得到一个itemView
        final View child = obtainView(position, mIsScrap);

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

        return child;
    }

8.关注函数的 final View child = obtainView(position, mIsScrap);的实现为:

 View obtainView(int position, boolean[] outMetadata) {
        .......
         //这里是从我们的缓存池中获取回收的view,有可能为空,所以我们在适配器中的getView中要对scrapView中要判断是否为空,当为空的时候,就自己创建一个view,这就是listView的复用机制
         final View scrapView = mRecycler.getScrapView(position);
         final View child = mAdapter.getView(position, scrapView, this);
         ......
 }

文章作者: AheadSnail
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 AheadSnail !
评论
  目录