表明转载自http://blog.csdn.net/iispring/article/details/50967445

在自己定义Adapter时,我们经常会重写Adapter的getView方法,该方法的签名例如以下所看到的:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">abstract</span> View <span class="hljs-title" style="box-sizing: border-box;">getView</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position, View convertView, ViewGroup parent) </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

此处会传入一个convertView变量。它的值有可能是null。也有可能不是null,假设不为null,我们就能够复用该convertView。对convertView里面的一些控件赋值后能够将convertView作为getView的返回值返回,这么做的目的是降低LayoutInflater.inflate()的调用次数。从而提升了性能(LayoutInflater.inflate()比較消耗性能)。

本文将介绍ListView中的RecycleBin机制,让大家对ListView中的优化机制有个概括的了解。同一时候也说明convertView的来龙去脉。

首先,我们知道,Adapter是数据源,AdapterView是展示数据源的UI控件。Adapter是给AdapterView使用的。通过调用AdapterView的setAdapter方法就能够让一个AdapterView绑定Adapter对象,从而AdapterView会将Adapter中的数据展示出来。

AdapterView的子类有AbsListView和AbsSpinner等,当中AbsListView的子类又有ListView、GridView等。所以ListView继承自AdapterView。

假设Adapter中有10000条数据。将这个Adapter对象赋给ListView。假设ListView创建10000个子View,那么App肯定崩溃了,由于Android没有能力同一时候绘制这么多的子View。并且,即便能同一时候绘制这10000个子View也没什么意义,由于手机的屏幕大小是有限的,有可能ListView的高度仅仅能最多显示10个子View。

基于此,Android在设计ListView这个类的时候。引入了RecycleBin机制—–对子View进行回收利用,RecycleBin直译过来就是回收站的意思。


RecycleBin基本原理

以下先简要说一下RecycleBin中的工作原理。后面会结合源代码具体说明。

在某一时刻,我们看到ListView中有很多View呈如今UI上,这些View对我们来说是可见的,这些可见的View能够称作OnScreen的View,即在屏幕中能看到的View,也能够叫做ActiveView。由于它们是在UI上可操作的。

当触摸ListView并向上滑动时,ListView上部的一些OnScreen的View位置上移。并移除了ListView的屏幕范围。此时这些OnScreen的View就变得不可见了,不可见的View叫做OffScreen的View。即这些View已经不在屏幕可见范围内了,也能够叫做ScrapView。Scrap表示废弃的意思。ScrapView的意思是这些OffScreen的View不再处于能够交互的Active状态了。ListView会把那些ScrapView(即OffScreen的View)删除,这样就不用绘制这些本来就不可见的View了,同一时候,ListView会把这些删除的ScrapView放入到RecycleBin中存起来,就像把临时没用的资源放到回收站一样。

当ListView的底部须要显示新的View的时候,会从RecycleBin中取出一个ScrapView,将其作为convertView參数传递给Adapter的getView方法,从而达到View复用的目的,这样就不必在Adapter的getView方法中运行LayoutInflater.inflate()方法了。

RecycleBin中有两个重要的View数组,各自是mActiveViews和mScrapViews。这两个数组中所存储的View都是用来复用的,仅仅只是mActiveViews中存储的是OnScreen的View。这些View非常有可能被直接复用;而mScrapViews中存储的是OffScreen的View,这些View主要是用来间接复用的。

上面对mActiveViews和mScrapViews的说明比較笼统,事实上在细节上还牵扯到Adapter的数据源发生变化的情况,详细细节后面会解说。


源代码解析

AdapterView是继承自ViewGroup的,ViewGroup中有addView方法能够向ViewGroup中加入子View。可是AdapterView重写了addView方法,例如以下所看到的:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">addView</span>(View child) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> UnsupportedOperationException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"addView(View) is not supported in AdapterView"</span>);
} <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">addView</span>(View child, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> index) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> UnsupportedOperationException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"addView(View, int) is not supported in AdapterView"</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

在AdapterView的addView方法中会抛出异常,也就是说AdapterView禁用了addView方法。

在详细解说之前。我们还是先花一点时间简要说一下View的每一帧的显示流程,当然,ListView也肯定遵循此流程。一个View要想在界面上呈现出来。须要经过三个阶段:measure->layout->draw。

View是一帧一帧绘制的,每一帧绘制都经历了measure->layout->draw这三个阶段,绘制完一帧之后,假设UI须要更新,比方用户滚动了ListView,那么又会绘制下一帧,再次经历measure->layout->draw方法,假设对此不了解。能够參见还有一篇博文
Android中View的量算、布局及画图机制》

我们上面说了,AdapterView把addView方法给禁用了。那么ListView怎么向当中加入child呢?奥秘就在layout中,在布局的时候,ListView会运行layoutChildren方法。该方法是ListView对View进行加入以及回收的关键方法,RecycleBin的非常多方法都在layoutChildren方法中被调用。在layoutChildren方法中实现对子View的增删。经过layoutChildren方法之后,ListView中全部的子View都是在屏幕中可见的,也就是说layoutChildren方法为接下来的帧绘制把子View准备完好了。这就保证了在后面的draw方法的运行过程中可以正确绘制ListView。

ListView的layoutChildren方法代码比較多。我们仅仅研究和View增删相关的关键代码,主要分下面三个阶段:

  1. ListView的children->RecycleBin
  2. ListView清空children
  3. RecycleBin->ListView的children

在layout这种方法刚刚開始运行的时候,ListView中的children事实上还是上一帧中须要绘制的子View的集合,在layout这种方法运行完毕的时候,ListView中的children就变成了当前帧立即要进行绘制的子View的集合。

以下对以上这三个阶段分别说明。

  1. ListView的children->RecycleBin 

    该阶段的关键代码例如以下所看到的:

    <code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//mFirstPosition是ListView的成员变量,存储着第一个显示的child所相应的adapter的position</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> firstPosition = mFirstPosition;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> RecycleBin recycleBin = mRecycler;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (dataChanged) {
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//假设数据发生了变化,那么就把ListView的全部子View都放入到RecycleBin的mScrapViews数组中</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < childCount; i++) {
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//addScrapView方法会传入一个View。以及这个View所相应的position</span>
    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
    }
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//假设数据没发生变化,那么把ListView的全部子View都放入到RecycleBin的mActiveViews数组中</span>
    recycleBin.fillActiveViews(childCount, firstPosition);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>

    再次强调一下,在上面的代码刚開始的时候,ListView的中的children还是上一帧须要绘制的子View。

    • 假设Adapter调用了notifyDataSetChanged方法。那么AdapterView就会知道Adapter的数据源发生了变化,此时dataChanged变量就为true,这样的情况下,ListView会觉得children中的View都是不合格的了。这时候会用getChildAt方法遍历children中全部的child。并把这些child通过RecycleBin的addScrapView方法将其放入RecycleBin的mScrapViews数组中。

    • 假设adapter的数据没有发生变化,那么会调用RecycleBin的fillActiveViews方法将全部的children都放入到RecycleBin的mActiveViews数组中。

    经过上面的操作之后,ListView全部的子View都放入到了RecycleBin中。这就实现了ListView的children->RecycleBin的迁移过程。放到RecycleBin的目的是为了分类缓存ListView中的children,以便在兴许过程中对这些View进行复用。

  2. ListView清空children 

    然后调用ViewGroup的detachAllViewsFromParent方法,该方法将全部的子View从ListView中分离。也就是清空了children。该方法源代码例如以下所看到的:

    <code class="hljs axapta has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> detachAllViewsFromParent() {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">count</span> = mChildrenCount;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">count</span> <= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> View[] children = mChildren;
    mChildrenCount = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">count</span> - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>; i >= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i--) {
    children[i].mParent = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
    children[i] = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
    }
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li></ul>
  3. RecycleBin->ListView的children

    然后ListView会依据mLayoutMode进行推断,源代码例如以下所看到的:

    <code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">switch</span> (mLayoutMode) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> LAYOUT_SET_SELECTION:
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (newSel != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
    sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom);
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
    sel = fillFromMiddle(childrenTop, childrenBottom);
    }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> LAYOUT_SYNC:
    sel = fillSpecific(mSyncPosition, mSpecificTop);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> LAYOUT_FORCE_BOTTOM:
    sel = fillUp(mItemCount - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>, childrenBottom);
    adjustViewsUpOrDown();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> LAYOUT_FORCE_TOP:
    mFirstPosition = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
    sel = fillFromTop(childrenTop);
    adjustViewsUpOrDown();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> LAYOUT_SPECIFIC:
    sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> LAYOUT_MOVE_SELECTION:
    sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">default</span>:
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (childCount == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!mStackFromBottom) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position = lookForSelectablePosition(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>);
    setSelectedPositionInt(position);
    sel = fillFromTop(childrenTop);
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position = lookForSelectablePosition(mItemCount - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>);
    setSelectedPositionInt(position);
    sel = fillUp(mItemCount - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>, childrenBottom);
    }
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mSelectedPosition >= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> && mSelectedPosition < mItemCount) {
    sel = fillSpecific(mSelectedPosition,
    oldSel == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> ? childrenTop : oldSel.getTop());
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mFirstPosition < mItemCount) {
    sel = fillSpecific(mFirstPosition,
    oldFirst == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> ? childrenTop : oldFirst.getTop());
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
    sel = fillSpecific(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, childrenTop);
    }
    }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li></ul>

    在该switch代码段中,会依据不同情况增删子View,这些方法的代码逻辑大部分终于调用了fillDown、fillUp等方法。 

    fillDown用子View从指定的position自上而下填充ListView,fillUp则是自下而上填充,我们以fillDown方法为例具体说明。

    fillDown方法的源代码例如以下所看到的:

    <code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> View <span class="hljs-title" style="box-sizing: border-box;">fillDown</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> pos, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> nextTop) {
    View selectedView = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//end表示ListView的高度</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> end = (mBottom - mTop);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
    end -= mListPadding.bottom;
    } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//nextTop < end确保了我们仅仅要将新增的子View可以覆盖ListView的界面就行了</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//pos < mItemCount确保了我们新增的子View在Adapter中都有相应的数据源item</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (nextTop < end && pos < mItemCount) {
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// is this the selected item?</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> selected = pos == mSelectedPosition;
    View child = makeAndAddView(pos, nextTop, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>, mListPadding.left, selected);
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将最新child的bottom值作为下一个child的top值,存储在nextTop中</span>
    nextTop = child.getBottom() + mDividerHeight;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (selected) {
    selectedView = child;
    }
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//position自增</span>
    pos++;
    } setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> selectedView;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li></ul>
    • fillDown接收两个參数,pos表示列表中第一个要绘制的item的position,其相应着Adapter中的索引,nextTop表示第一个要绘制的item在ListView中实际的位置, 即该item所相应的子View的顶部到ListView的顶部的像素数。

    • 首先将mBottom - mTop的值作为end,end表示ListView的高度。

    • 然后在while循环中加入子View,我们先不看while循环的详细条件。先看一下循环体。在循环体中,将pos和nextTop传递给makeAndAddView方法,该方法返回一个View作为child,该方法会创建View,并把该View作为child加入到ListView的children数组中。

    • 然后运行nextTop = child.getBottom() + mDividerHeight,child的bottom值表示的是该child的底部到ListView顶部的距离,将该child的bottom作为下一个child的top。也就是说nextTop一直保存着下一个child的top值。

    • 最后调用pos++实现position指针下移。

      如今我们回过头来看一下while循环的条件while (nextTop < end && pos < mItemCount)。

    • nextTop < end确保了我们仅仅要将新增的子View可以覆盖ListView的界面就行了,比方ListView的高度最多显示10个子View。我们不是必需向ListView中增加11个子View。

    • pos < mItemCount确保了我们新增的子View在Adapter中都有相应的数据源item,比方ListView的高度最多显示10个子View,可是我们Adapter中一共才有5条数据,这样的情况下仅仅能向ListView中增加5个子View,从而不能填充满ListView的所有高度。

经过了上面的while循环之后,ListView对子View的增删就完毕了。即children中存放的就是要在后面画图过程中即将渲染的子View的集合。

上面while循环的方法体中调用了makeAndAddView方法。通过该方法会获得一个子View。并把该子View加入到ListView的children中。该方法的方法签名例如以下所看到的:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> View <span class="hljs-title" style="box-sizing: border-box;">makeAndAddView</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> y, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> flow, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> childrenLeft,
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> selected)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

其源代码例如以下所看到的:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> View <span class="hljs-title" style="box-sizing: border-box;">makeAndAddView</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> y, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> flow, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> childrenLeft,
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> selected) {
View child; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!mDataChanged) {
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 假设数据源没发生变化,那么尝试用该position从RecycleBin的mActiveViews中获取可复用的View</span>
child = mRecycler.getActiveView(position);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (child != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 假设child 不为空,说明我们找到了一个已经存在的child,这样mActiveViews中存储的View就被直接复用了</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 调用setupChild,对child进行定位</span>
setupChild(child, position, y, flow, childrenLeft, selected, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> child;
}
} <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 假设没可以从mActivieViews中直接复用View。那么就要调用obtainView方法获取View,该方法尝试间接复用RecycleBin中的mScrapViews中的View。假设不能间接复用,则创建新的View</span>
child = obtainView(position, mIsScrap); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 调用setupChild方法。进行定位和量算</span>
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>]); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> child;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>

我们重点说一下前两个參数position和y,position表示的是数据源item在Adapter中的索引。y表示要生成的View的top值或bottom值。假设第三个參数flow是true,那么y表示top值,否则表示bottom值。

  • 假设数据源没发生变化,那么尝试用该position从RecycleBin的mActiveViews中获取可复用的View。RecycleBin的getActiveView方法接收一个position參数,能够在RecycleBin的mActiveViews数组中查找有没有相应position的View。假设能找到就能够直接复用该View作为child了。

    举一个样例,假设在某一时刻ListView中显示了10个子View,position依次为从0到9。

    然后我们手指向上滑动,且向上滑动了一个子View的高度,ListView须要绘制下一帧。这时候ListView在layoutChildren方法中把这10个子View都放入到了RecycleBin的mActiveViews数组中了,然后清空了children数组。然后调用fillDown方法,向ListView中依次加入position1到10的子View。在加入position为1的子View的时候,因为在上一帧中position为1的子View已经被放到mActiveViews数组中了。这次直接能够将其从mActiveViews数组中取出来,这样就是直接复用子View,所以说RecycleBin的mActiveViews数组主要是用于直接复用的。

    在直接复用了子View后,我们须要调用setupChild方法。该方法会将child加入到ListView的children数组中,并对child进行定位。

  • 假设没可以从mActivieViews中直接复用View。那么就要调用obtainView方法获取View。该方法尝试间接复用RecycleBin中的mScrapViews中的View,假设不能间接复用,则创建新的View。

    在通过obtainView获取了View之后,调用setupChild方法。该方法会将child加入到ListView的children数组中,并对child进行定位和量算。

以下我们再来看一下obtainView方法,该方法的方法签名例如以下所看到的:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">View obtainView(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span>[] isScrap)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

该方法接收position參数,其关键的源代码有下面两行:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> View scrapView = mRecycler.getScrapView(position);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> View child = mAdapter.getView(position, scrapView, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

通过调用RecycleBin的getScrapView方法。从mScrapViews数组中获取一个View,该View是用来间接复用的,该View可能为null,也可能不为null。将其作为我们熟悉的convertView传递给Adapter的getView方法,这样我们就能够在AdapterView的getView方法中通过推断convertView是否为空进行间接复用了。

希望本文对大家理解ListView的RecycleBin机制有所帮助!

ListView原理的更多相关文章

  1. (转)Android之ListView原理学习与优化总结

    转自: http://jishu.zol.com.cn/12893.html 在整理前几篇文章的时候有朋友提出写一下ListView的性能优化方面的东西,这个问题也是小马在面试过程中被别人问到的….. ...

  2. Android学习笔记之ListView复用机制

    PS:满打满算,差不多三个月没写博客了...前一阵忙的不可开交...总算是可以抽出时间研究研究其他事情了... 学习内容: 1.ListView的复用机制 2.ViewHolder的概念 1.List ...

  3. Android 如何在 ListView 中更新 ProgressBar 进度

    =======================ListView原理============================== Android 的 ListView 的原理打个简单的比喻就是: 演员演 ...

  4. 深入理解自定义ListView

    深入理解自定义ListView ListView原理 他是一个系统的原生控件,用列表的形式来显示内容.如果内容过过有1000条左右,我们可以通过手势的上下滑动来查看数据.ListView也不是爆出OO ...

  5. 灵活性比Listview更好的RecycleView

    RecycleView:是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好. RecyclerView与ListView原理是类似的:都 ...

  6. Android之RecyclerView(一)

    概述 RecyclerView 是一个 ViewGroup,它用于渲染任何基于适配器的 View.它被官方定义为 ListView 和 GridView 的取代者,是在 Support V7 包中引入 ...

  7. Android 高级编程 RecyclerView 控件的使用

    RecyclerView 是Android 新添加的一个用来取代ListView的控件,它的灵活性与可替代性比listview更好. 看一下继承关系: ava.lang.Object    ↳ and ...

  8. Android列表视图(List View)

    Android列表视图(ListView) ListView是一个显示滚动项列表的示视图组(viewgroup),通过使用适配器(Adapter)把这些列表项自动插入到列表中.适配器比如从一个数组或是 ...

  9. Introducing RecyclerView(一)

    RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.接下来通过一系列的文章讲解如何使用RecyclerView ...

随机推荐

  1. 串口接收模块(verilog) 波特率115200

    我来分享一下uart协议之接收verilog代码 顶层实例化 `timecale 1ns / 1ps////////////////////////////////////////////////// ...

  2. 使用Docker运行Microsoft SQL Server 2017

    最近每天都在空闲时间努力编写Apworks框架的案例代码WeText.在文本发布和处理微服务中,我打算使用微软的SQL Server for Linux来做演示,于是也就在自己的docker-comp ...

  3. robotframework自动化系列:新增流程

    刚接手项目的时候,要求所有流程在上线之前必须确保正向操作是正确的:这个时候又有新的模块需要测试,所以引入自动化测试是非常有必要的!通过对比,尝试使用RF进行自动化的回归测试. 测试中最常见的操作就是增 ...

  4. 开源API测试工具 Hitchhiker v0.4更新 - 没有做不到,只有想不到

    Hitchhiker 是一款开源的 Restful Api 测试工具,支持Schedule, 数据对比,压力测试,支持上传脚本定制请求,可以轻松部署到本地,和你的team成员一起管理Api. 详细介绍 ...

  5. Maven快速使用阿里云的代理maven仓库

    自从开源中国的maven仓库挂了之后就一直在用国外的仓库,慢得想要砸电脑的心都有了.如果你和我一样受够了国外maven仓库的龟速下载?快试试阿里云提供的maven仓库,从此不在浪费生命…… 仓库地址: ...

  6. MSSQL 常用操作

    0.GUID去除横线和变换为小写 SELECT LOWER(REPLACE(LTRIM(NEWID()),'-','')) 1.IDENTITY 函数说明 IDENTITY ( data_type [ ...

  7. [转载] ConcurrentHashMap原理分析

    转载自http://blog.csdn.net/liuzhengkang/article/details/2916620 集合是编程中最常用的数据结构.而谈到并发,几乎总是离不开集合这类高级数据结构的 ...

  8. Spring JDBC 示例

    在使用普通的 JDBC 数据库时,就会很麻烦的写不必要的代码来处理异常,打开和关闭数据库连接等.但 Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常, ...

  9. Kaggle实战之一回归问题

    0. 前言 1.任务描述 2.数据概览 3. 数据准备 4. 模型训练 5. kaggle实战 0. 前言 "尽管新技术新算法层出不穷,但是掌握好基础算法就能解决手头 90% 的机器学习问题 ...

  10. Oracle函数sys_connect_by_path 详解

    Oracle函数sys_connect_by_path 详解 语法:Oracle函数:sys_connect_by_path 主要用于树查询(层次查询) 以及 多列转行.其语法一般为:       s ...