使用流程则是 初始化view,setAdapter();
那就走一下主线。

初始化view

先屡清除继承关系: listview –>AbsListView –> AdapterView< T extends Adapter> —> ViewGroup

|
|

从代码从可以看出 首先执行的是AbsListView 中的 initAbsListView();主要对View的Clickable、Focusable、Cache等属性进行的初始设置。同时 获取了 TouchSlop(最小相应距离),Velocity(滑动速度)等变量。
上代码:

private void initAbsListView() {     // Setting focusable in touch mode will set the focusable property to true     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; }

接着往下看:

mOwnerThread = Thread.currentThread();  //获取主线程 后期adapter刷新UIsetVerticalScrollBarEnabled(true);      //设置滚动条显示状态TypedArray a = context.obtainStyledAttributes(R.styleable.View);initializeScrollbarsInternal(a);        //拿到View的Style 初始化 Scrollbars. . . . . .                             //abslistview 风格的设置a.recycle();

之后ListView在自己的方法中实现了 setDivider(分割线)、setOverscrollHeader/setOverscrollFooter等样式上的操作。
接下来的 measure layout draw 暂时先不看 里面肯定是加入了 adapter的各种判断 那就先来看 adapter

Adapter 浅析

adapter
Android 设计本身就遵循 MVC的模式 View控件就是为了 展示数据 /交互用的 ;同样ListView也只承担交互和展示工作而已,至于数据从哪里来,数据类型是啥(数组 or list 或者 Couser 都可以)都不应该关心;
于是就有了 adapter;它在ListView和数据源之间起到了一个桥梁的作用,ListView并不会直接跟数据源打交道,而是通过Adapter 这个接口去访问数据源,至此ListView不用再去担心任何适配方面的问题。而Adapter又是一个接口(interface),它可以去实现各种各样的子类,每个子类都能通过自己的逻辑来去完成特定的功能,以及与特定数据源的适配操作

RecycleBin类


主要的回收类RecycleBin位于AbsListView中。

AbsListView 中有一个 RecycleBin 对象 mRecycler;
RecycleBin 中使用 两级 View来进行回收 : ActiveView[ ](当前屏幕上的激活View)ScrapView[ ](被移除掉的View)

  • mRecyclerListener:RecyclerListener接口只有一个函数onMovedToScrapHeap ,指明某个view被回收到了scrap heap. 该view不再被显示。该函数是处理回收时view中的资源释放。
  • mFirstActivePosition:储存在mActiveViews中的第一个View的位置。
  • mActiveViews:布局开始时屏幕显示的view,这个数组会在布局开始时填充,布局结束后所有view被移至mScrapViews。
    -ArrayList[ ] mScrapViews: 是adapter中getView方法中的参数convertView的来源(系统注释:Unsorted views that can be used by the adapter as a convert view. 未分类的views 能被复用的view)。
  • mViewTypeCount:类型
  • mCurrentScrap:就是指向当前mScrapViews中的一组,默认ViewTypeCount = 1的情况下,mCurrentScrap = mScrapViews[0];

对于mActiveViews通常的操作为,在每次layout开始,AbsListView会将位于屏幕上的view全部填充到RecycleBin的mActiveViews中去。layout过程中,将下一轮即将显示在屏幕上得view从RecycleBin中取出来,最后如果mActiveViews中还有元素,就在layout结束时将它们统统转移到mScrapView中去。这个流程可以从ListView中的layoutChildren看出,每次layout时,onlayout最终会调用这个函数

@Override protected void layoutChildren() {     //....忽略一大坨代码    // layout开始前将所有已经存在的子view放入recycleBin中   // 如果dataChanged为true,就放入mActiveViews中,否则放入mScrapViews中去   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);   }   // Clear out old views   detachAllViewsFromParent();   recycleBin.removeSkippedScrap();    //重新填充子view...    // 将所有没有被重用到的view从mActiveViews转移到mScrapViews中去    recycleBin.scrapActiveViews();}

从layout的过程就可以看出来,scrapview实际上是一个存放备用view的回收池,每次layout完,有多余的view会存储到池子里,以后可能会用到。那这是layout时候做的事情,如果是scroll的情况呢,情况其实类似。我们来看看scroll的流程图

scroll事件从onTouchEvent函数发起,大家肯定知道的,中间经过一些判断,最终带着deltaY和incrementalDetalY到达trackMotionScroll函数,我们的分析从这个函数开始.

boolean trackMotionScroll(int deltaY, int incrementalDeltaY) {        //重新计算deltay和 incrementalDeltaY,判断时候还能继续向下滚动或者向上滚动        //.....一波代码        final boolean down = incrementalDeltaY < 0;//判断是否向下滚动        final boolean inTouchMode = isInTouchMode();        if (inTouchMode) {            hideSelector();        }        final int headerViewsCount = getHeaderViewsCount();        final int footerViewsStart = mItemCount - getFooterViewsCount();        int start = 0;        int count = 0;        if (down) {            int top = -incrementalDeltaY;            if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {                top += listPadding.top;            }            //如果向下滚动,则有些view会从上方滚出            for (int i = 0; i < childCount; i++) {                final View child = getChildAt(i);                //判断子view是否已经滚出                if (child.getBottom() >= top) {//没有滚出                    break;                } else {//已经滚出                    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);//加入scrap集合备用                    }                }            }        } else {   //向上滚动原理类似            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 {                    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);                    }                }            }        }        mMotionViewNewTop = mMotionViewOriginalTop + deltaY;        mBlockLayoutRequests = true;        if (count > 0) {            detachViewsFromParent(start, count);   //将滚出的view进行detach            mRecycler.removeSkippedScrap();        }        // ……        final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);        if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {            fillGap(down);  //填充滚动引起的view间隙        }        // ……    }

从上面的注释里,不难知晓在滚动过程中,RecycleBin所起的作用,当然函数在走到fillGap前,只是完成了一部分滚出view的回收,接下来,是利用这些view进行重用还是生成新的view就要看fillGap函数所做的操作了。

void fillGap(boolean down) {    final int count = getChildCount();    if (down) {        //如果是向下滚动,则调用fillDown        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 {          //如果是向上滚动,则调用fillUp        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());    }}

fillGap的实现在ListView中,它只是做了个分派操作,内部分向上滚动和向下滚动分别调用fillUp和fillDown,只需要分析其中一个就好

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;    }    //开启循环,持续填充view直到已经填满    while (nextTop < end && pos < mItemCount) {        // is this the selected item?        boolean selected = pos == mSelectedPosition;        View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);        //更新下次填充view的top位置        nextTop = child.getBottom() + mDividerHeight;        if (selected) {            selectedView = child;        }        pos++;//更新被填充位置的position    }    setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);    return selectedView;}

fillDown的两个参数分别为即将开始填充的view的item 位置 和 top坐标位置

makeAndAddView所做的操作就是获得一个view并将其添加到viewHierarchy上。

private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,         boolean selected) {     View child;//判断数据时候已经发生变化,如果没有,就尝试从ActiveView中去获取view,     if (!mDataChanged) {         // Try to use an existing view for this position         child = mRecycler.getActiveView(position);         if (child != null) {             // Found it -- we're using an existing child             // This just needs to be positioned             setupChild(child, position, y, flow, childrenLeft, selected, true);             return child;         }     }      // 尝试从scrap中获取可重用的view,如果没有,就创建新的view     child = obtainView(position, mIsScrap);     // 设置子view的位置,如果有必要,会去重新measure子view,并添加到父view上     setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);     return child; }

makeAndAddView在数据没有改变的情况下,会首先尝试从mActiveViews中去获取。需要注意的是,在 scroll 操作中,我们一开始并没有把屏幕上的view 充填到mActiveViews中,因此scroll逻辑走到这里的时候,从mActiveViews 是拿不到view的; 为啥还有这段代码 是因为这段代码在 layout中也会调用,从 layoutChildren 函数的重新 填充子view那一步中,会调用一系列 以fill开头的函数,最后会走到这里。 接下来看 View obtainView。

obtainView

当这个方法被调用时,说明Recycle bin中的view已经不可用了,那么,现在唯一的方法就是,convert一个老的view,或者构造一个新的view。其中
position: 要显示的位置

isScrap: 是个boolean数组, 如果view从scrap heap获取,isScrap [0]为true,否则为false。

View obtainView(int position, boolean[] isScrap) {   Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView");   isScrap[0] = false;   // 检查是否有一个对应的处于 transient state 的view. 如果有尝试重新绑定Data   final View transientView = mRecycler.getTransientStateView(position);   if (transientView != null) {          //此时说明可以从回收站中重新使用scrapView。       final LayoutParams params = (LayoutParams) transientView.getLayoutParams();       // 如果view type没有变化,尝试重新绑定数据       if (params.viewType == mAdapter.getItemViewType(position)) {           final View updatedView = mAdapter.getView(position, transientView, this);           // 如果两者不相等,表示重绑定数据失败,生成了新的view,           //但是我们依然使用transientView,将updatedView入回收池           if (updatedView != transientView) {               setItemViewLayoutParams(updatedView, position);               mRecycler.addScrapView(updatedView, position);           }       }       // Scrap view implies temporary detachment.       isScrap[0] = true;        // Finish the temporary detach started in addScrapView().       transientView.dispatchFinishTemporaryDetach();       return transientView;   }    //找不到transientview的情况下,就从回收池中去取,   final View scrapView = mRecycler.getScrapView(position);   //重新绑定数据,   final View child = mAdapter.getView(position, scrapView, this);   if (scrapView != null) {       //如果重绑定失败,将scrapView重新入回收池,采用新生成的view       if (child != scrapView) {           // Failed to re-bind the data, return scrap to the heap.           mRecycler.addScrapView(scrapView, position);       } else {           isScrap[0] = true;           child.dispatchFinishTemporaryDetach();       }   }    //设置子view的一些属性   if (mCacheColorHint != 0) {       child.setDrawingCacheBackgroundColor(mCacheColorHint);   }   if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {       child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);   }   setItemViewLayoutParams(child, position);   if (AccessibilityManager.getInstance(mContext).isEnabled()) {       if (mAccessibilityDelegate == null) {           mAccessibilityDelegate = new ListItemAccessibilityDelegate();       }       if (child.getAccessibilityDelegate() == null) {           child.setAccessibilityDelegate(mAccessibilityDelegate);       }   }   Trace.traceEnd(Trace.TRACE_TAG_VIEW);   return child;}

到这 listview 复用机制机制 大体上也就说的差不多了; 今后 写类似view回收池时可以参考RecycleBin的写法

更多相关文章

  1. 【Android】获取应用程序(包)的大小-----PackageManager的使用(
  2. 你不知道的Android着色器/渲染器
  3. 自定义View详解
  4. Android(安卓)sensor架构(二)SystemSensorManager以及JNI、sensorS
  5. Android(安卓)WiFi Toggle ON/OFF流程分析
  6. Android(安卓)GestureDetector ScaleGestureDetector
  7. Linux下的两种timer方法 (Android(安卓)下NDK开发)
  8. Android(安卓)怎么把异常的StackTrace信息格式化为String
  9. Android(安卓)一般动画Animation和属性动画Animator

随机推荐

  1. [转]android的Goldfish内核概述
  2. [原]采用MQTT协议实现Android消息推送
  3. 【android】五种控制Android应用的权限的
  4. Android类库常用类型解析
  5. Android(安卓)之 Bluetooth
  6. Android记事本NotePad应用功能拓展(六)
  7. Android(安卓)下使用 JSON 实现 HTTP 请
  8. Android中图像的几何变化中Matrix的使用
  9. Android(安卓)架构组件之 ViewBinding(视
  10. 2011.06.07(2)——— android 调试android