android源码解析--ListView

来源:互联网 发布:db2客户端连接数据库 编辑:程序博客网 时间:2024/05/16 11:45
android源码解析--ListView

在listview的源码中,在46行有一个小的主意事项:

/* * Implementation Notes: * * Some terminology: * *     index    - index of the items that are currently visible *     position - index of the items in the cursor */

就是简单的说明下index和position这两个变量名,index表示现在可视的部分的索引值,position是指在cursor中的索引值。

        看下类说明:

A view that shows items in a vertically scrolling list. The items come from the ListAdapter associated with this view.

一个通过竖直滚动条显示其条目的列表视图,与视图关联的条目来自于ListAdapter。

入门指南:http://developer.android.com/guide/topics/ui/layout/listview.html。

@RemoteViewpublic class ListView extends AbsListView {
看一下类,头上有@RemoteView,加上这个标志,就说明该View能作为RemoteView使用,即可以在android widget上面使用listview,在android早起版本中,好像listview是不可以的。

看下其静态变量:

/**     * Used to indicate a no preference for a position type.     */    static final int NO_POSITION = -1;
用于表示一个没有任何偏好的位置类型

私有静态变量:

/**     * When arrow scrolling, ListView will never scroll more than this factor     * times the height of the list.     */    private static final float MAX_SCROLL_FACTOR = 0.33f;    /**     * When arrow scrolling, need a certain amount of pixels to preview next     * items.  This is usually the fading edge, but if that is small enough,     * we want to make sure we preview at least this many pixels.     */    private static final int MIN_SCROLL_PREVIEW_PIXELS = 2;

MAX_SCROLL_FACTOR:当箭头滚动,ListView中永远不会比这个因子乘以列表的高度滚动更多。

MIN_SCROLL_PREVIEW_PIXELS:当箭头滚动,需要有一定的像素预览下一个项目。这通常是衰减的边缘,但如果足够小,我们要确保我们至少预览的像素数。


在看下其内部类:

/**     * A class that represents a fixed view in a list, for example a header at the top     * or a footer at the bottom.     */    public class FixedViewInfo {        /** The view to add to the list */        public View view;        /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */        public Object data;        /** <code>true</code> if the fixed view should be selectable in the list */        public boolean isSelectable;    }

固定视图的实体类,表示一个list中的固定视图,如listview中在顶部的header或者底部的footer。其变量含义view代表要添加到listview中的视图,data:视图中的数据,isSelectable:是否可以选中。

接着看ListView类中的变量:

private ArrayList<FixedViewInfo> mHeaderViewInfos = Lists.newArrayList();    private ArrayList<FixedViewInfo> mFooterViewInfos = Lists.newArrayList();    Drawable mDivider;    int mDividerHeight;    Drawable mOverScrollHeader;    Drawable mOverScrollFooter;    private boolean mIsCacheColorOpaque;    private boolean mDividerIsOpaque;    private boolean mHeaderDividersEnabled;    private boolean mFooterDividersEnabled;    private boolean mAreAllItemsSelectable = true;    private boolean mItemsCanFocus = false;    // used for temporary calculations.    private final Rect mTempRect = new Rect();    private Paint mDividerPaint;    // the single allocated result <span id="28_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="28_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="28" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span> list view; kinda cheesey but avoids    // allocating these thingies too often.    private final ArrowScrollFocusResult mArrowScrollFocusResult = new ArrowScrollFocusResult();    // Keeps focused children visible through resizes    private FocusSelector mFocusSelector;

头两个参数是用于存储固定视图的列表。

mDivider:分割线。

mOverScrollHeader:当滑动到顶部,再下拉看到的视图。
mOverScrollFooter:当滑动到底部,再上拉看到的视图。

mIsCacheColorOpaque:缓存颜色是否透明。

mDividerIsOpaque:分割线是否透明。

mHeaderDividersEnabled:header分割线是否可用。

mFooterDividersEnabled:footer分割线是否可用。

AreAllItemsSelectable:是否所有的条目都可以被选中。

mItemsCanFocus:是否所有的条目都可以获取焦点。

mTempRect:一个用于临时技术的矩形。

mDividerPaint:分割线画笔。

mArrowScrollFocusResult:listview中内部类,用于保留箭头滑动时,获取焦点区域,避免焦点切换太频繁。

mFocusSelector:获取焦点的条目(通过调整,使条目保持焦点)


先看下ArrowScrollFocusResultFocusSelector两个内部类:

/**     * Holds results of focus aware arrow scrolling.     */    static private class ArrowScrollFocusResult {        private int mSelectedPosition;        private int mAmountToScroll;        /**         * How {@link android.widget.ListView#arrowScrollFocused} returns its values.         */        void populate(int selectedPosition, int amountToScroll) {            mSelectedPosition = selectedPosition;            mAmountToScroll = amountToScroll;        }        public int getSelectedPosition() {            return mSelectedPosition;        }        public int getAmountToScroll() {            return mAmountToScroll;        }    }

里面有两个参数,一个是现在选中条目位置,一个是滑动的item个数。通过这个类,就可以很方便的获取焦点和切换焦点了。


FocusSelector:

 private class FocusSelector implements Runnable {        private int mPosition;        private int mPositionTop;                public FocusSelector setup(int position, int top) {            mPosition = position;            mPositionTop = top;            return this;        }                public void run() {            setSelectionFromTop(mPosition, mPositionTop);        }    }

一个线程类,通过设置,可以不断的调整其获取焦点的条目。

里面调用到了1905行的setSelectionFromTop这个方法:

 /**     * Sets the selected item and positions the selection y pixels from the top edge     * of the ListView. (If in touch mode, the item will not be selected but it will     * still be positioned appropriately.)     *     * @param position Index (starting at 0) of the data item to be selected.     * @param y The distance from the top edge of the ListView (plus padding) that the     *        item will be positioned.     */    public void setSelectionFromTop(int position, int y) {        if (mAdapter == null) {            return;        }        if (!isInTouchMode()) {            position = lookForSelectablePosition(position, true);            if (position >= 0) {                setNextSelectedPositionInt(position);            }        } else {            mResurrectToPosition = position;        }        if (position >= 0) {            mLayoutMode = LAYOUT_SPECIFIC;            mSpecificTop = mListPadding.top + y;            if (mNeedSync) {                mSyncPosition = position;                mSyncRowId = mAdapter.getItemId(position);            }            requestLayout();        }    }

通过listview顶部边缘的对应y像素和position来设置选中项(在滑动状态下,不能被选中,但是会滑动到合适位置)


再看ListView的构造函数:

public ListView(Context context) {        this(context, null);    }    public ListView(Context context, AttributeSet attrs) {        this(context, attrs, com.android.internal.R.attr.listViewStyle);    }    public ListView(Context context, AttributeSet attrs, int defStyle) {        su<span id="27_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="27_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="27" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>(context, attrs, defStyle);        TypedArray a = context.obtainStyledAttributes(attrs,                com.android.internal.R.styleable.ListView, defStyle, 0);        CharSequence[] entries = a.getTextArray(                com.android.internal.R.styleable.ListView_entries);        if (entries != null) {            setAdapter(new ArrayAdapter<CharSequence>(context,                    com.android.internal.R.layout.simple_list_item_1, entries));        }        final Drawable d = a.getDrawable(com.android.internal.R.styleable.ListView_divider);        if (d != null) {            // If a divider is specified use its intrinsic height for divider height            setDivider(d);        }                final Drawable osHeader = a.getDrawable(                com.android.internal.R.styleable.ListView_overScrollHeader);        if (osHeader != null) {            setOverscrollHeader(osHeader);        }        final Drawable osFooter = a.getDrawable(                com.android.internal.R.styleable.ListView_overScrollFooter);        if (osFooter != null) {            setOverscrollFooter(osFooter);        }        // Use the height specified, zero being the default        final int dividerHeight = a.getDimensionPixelSize(                com.android.internal.R.styleable.ListView_dividerHeight, 0);        if (dividerHeight != 0) {            setDividerHeight(dividerHeight);        }        mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true);        mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true);        a.recycle();    }

主要是从TypeArray里面取出各种属性进行设置。

/**     * @return The maximum amount a list view will scroll in response to     *   an arrow event.     */    public int getMaxScrollAmount() {        return (int) (MAX_SCROLL_FACTOR * (mBottom - mTop));    }
相应一个箭头事件,ListView可以滑动的最大距离。关于这个方法,询问高手以后,可以理解为一个箭头((按键事件)非触摸事件(touch)),listivew作出响应,滑动的最大距离。比如说在android电视中,使用遥控器操作listview,按向下箭头,listview滑动距离。

下面是adjustViewsUpOrDown():

/**     * Make sure views are touching the top or bottom edge, as appropriate for     * our gravity     */    private void adjustViewsUpOrDown() {        final int childCount = getChildCount();        int delta;        if (childCount > 0) {            View child;            if (!mStackFromBottom) {                // Uh-oh -- we came up short. Slide all views up to make them                // align with the top                child = getChildAt(0);                delta = child.getTop() - mListPadding.top;                if (mFirstPosition != 0) {                    // It's OK to have some space above the first item if it is                    // part of the vertical spacing                    delta -= mDividerHeight;                }                if (delta < 0) {                    // We only are looking to see if we are too low, not too high                    delta = 0;                }            } else {                // we are too high, slide all views down to align with bottom                child = getChildAt(childCount - 1);                delta = child.getBottom() - (getHeight() - mListPadding.bottom);                if (mFirstPosition + childCount < mItemCount) {                    // It's OK to have some space below the last item if it is                    // part of the vertical spacing                    delta += mDividerHeight;                }                if (delta > 0) {                    delta = 0;                }            }            if (delta != 0) {                offsetChildrenTopAndBottom(-delta);            }        }    }

调整在ListView顶部或底部的view以适应我们的视觉感应。

mStackFromBottom:这个参数是来自父类AbsListView

/**     * Indicates whether the list is stacked from the bottom edge or     * the top edge.     */

使列表与底部对齐或者与顶部对齐。

里面调用的offsetChildrenTopAndBottom()方法(来自父类ViewGroup):

/**     * Offset the vertical location of all children of this view by the specified number of pixels.     *     * @param offset the number of pixels to offset     *     * @hide     */    public void offsetChildrenTopAndBottom(int offset) {        final int count = mChildrenCount;        final View[] children = mChildren;        for (int i = 0; i < count; i++) {            final View v = children[i];            v.mTop += offset;            v.mBottom += offset;        }    }

调整垂直方向上面所有子视图位置。


添加HeaderView:

    /**     * Add a fixed view to appear at the top of the list. If addHeaderView is     * called more than once, the views will appear in the order they were     * added. Views added using this call can take focus if they want.     * <p>     * NOTE: Call this before calling setAdapter. This is so ListView can wrap     * the supplied cursor with one that will also account for header and footer     * views.     *     * @param v The view to add.     * @param data Data to associate with this view     * @param isSelectable whether the item is selectable     */    public void addHeaderView(View v, Object data, boolean isSelectable) {        if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {            throw new IllegalStateException(                    "Cannot add header view to list -- setAdapter has already been called.");        }        FixedViewInfo info = new FixedViewInfo();        info.view = v;        info.data = data;        info.isSelectable = isSelectable;        mHeaderViewInfos.add(info);        // in the case of re-adding a header view, or adding one later on,        // we need to notify the observer        if (mAdapter != null && mDataSetObserver != null) {            mDataSetObserver.onChanged();        }    }    /**     * Add a fixed view to appear at the top of the list. If addHeaderView is     * called more than once, the views will appear in the order they were     * added. Views added using this call can take focus if they want.     * <p>     * NOTE: Call this before calling setAdapter. This is so ListView can wrap     * the supplied cursor with one that will also account for header and footer     * views.     *     * @param v The view to add.     */    public void addHeaderView(View v) {        addHeaderView(v, null, true);    }

为ListView顶部添加一个固定的View,如果HeaderView多于一个,会按照添加的顺序进行排列。

287行,获取添加的HeaderView的个数:

 @Override    public int getHeaderViewsCount() {        return mHeaderViewInfos.size();    }

删除在以前添加的HeaderView:

 /**     * Removes a previously-added header view.     *     * @param v The view to remove     * @return true if the view was removed, false if the view was not a header     *         view     */    public boolean removeHeaderView(View v) {        if (mHeaderViewInfos.size() > 0) {            boolean result = false;            if (mAdapter != null && ((HeaderViewListAdapter) mAdapter).removeHeader(v)) {                if (mDataSetObserver != null) {                    mDataSetObserver.onChanged();                }                result = true;            }            removeFixedViewInfo(v, mHeaderViewInfos);            return result;        }        return false;    }

里面调用了removeFixedViewInfo:

private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {        int len = where.size();        for (int i = 0; i < len; ++i) {            FixedViewInfo info = where.get(i);            if (info.view == v) {                where.remove(i);                break;            }        }    }

找到对应view,从列表里面去除。

 添加FooterView:

/**     * Add a fixed view to appear at the bottom of the list. If addFooterView is     * called more than once, the views will appear in the order they were     * added. Views added using this call can take focus if they want.     * <p>     * NOTE: Call this before calling setAdapter. This is so ListView can wrap     * the supplied cursor with one that will also account for header and footer     * views.     *     * @param v The view to add.     * @param data Data to associate with this view     * @param isSelectable true if the footer view can be selected     */    public void addFooterView(View v, Object data, boolean isSelectable) {        // NOTE: do not enforce the adapter being null here, since unlike in        // addHeaderView, it was never enforced here, and so existing apps are        // relying on being able to add a footer and then calling setAdapter to        // force creation of the HeaderViewListAdapter wrapper        FixedViewInfo info = new FixedViewInfo();        info.view = v;        info.data = data;        info.isSelectable = isSelectable;        mFooterViewInfos.add(info);        // in the case of re-adding a footer view, or adding one later on,        // we need to notify the observer        if (mAdapter != null && mDataSetObserver != null) {            mDataSetObserver.onChanged();        }    }    /**     * Add a fixed view to appear at the bottom of the list. If addFooterView is called more     * than once, the views will appear in the order they were added. Views added using     * this call can take focus if they want.     * <p>NOTE: Call this before calling setAdapter. This is so ListView can wrap the supplied     * cursor with one that will also account for header and footer views.     *     *     * @param v The view to add.     */    public void addFooterView(View v) {        addFooterView(v, null, true);    }

可参考上面对addHeaderView的解释。

获取添加的FooterView的个数:

@Override    public int getFooterViewsCount() {        return mFooterViewInfos.size();    }

删除相对应的FooterView:

/**     * Removes a previously-added footer view.     *     * @param v The view to remove     * @return     * true if the view was removed, false if the view was not a footer view     */    public boolean removeFooterView(View v) {        if (mFooterViewInfos.size() > 0) {            boolean result = false;            if (mAdapter != null && ((HeaderViewListAdapter) mAdapter).removeFooter(v)) {                if (mDataSetObserver != null) {                    mDataSetObserver.onChanged();                }                result = true;            }            removeFixedViewInfo(v, mFooterViewInfos);            return result;        }        return false;    }

获取ListView正在使用的adapter:

 /**     * Returns the adapter currently in use in this ListView. The returned adapter     * might not be the same adapter passed to {@link #setAdapter(ListAdapter)} but     * might be a {@link WrapperListAdapter}.     *     * @return The adapter currently used to display data in this ListView.     *     * @see #setAdapter(ListAdapter)     */    @Override    public ListAdapter getAdapter() {        return mAdapter;    }

返回的adapter不一定是通过setAdapter方法传入的adapter,有可能是一个WrapperListAdapter。

setRemoteViewAdapter():

/**     * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService     * through the specified intent.     * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.     */    @android.view.RemotableViewMethod    public void setRemoteViewsAdapter(Intent intent) {        su<span id="26_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="26_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="26" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>.setRemoteViewsAdapter(intent);    }
 在上面提到,listview可以作为一个remoteView在widget中使用,该方法是设定一个intent,listview的adapter通过其与RemoteViewService通信。

 为listview添加数据:

/**     * Sets the data behind this ListView.     *     * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},     * depending on the ListView features currently in use. For instance, adding     * headers and/or footers will cause the adapter to be wrapped.     *     * @param adapter The ListAdapter which is responsible for maintaining the     *        data backing this list and for producing a view to represent an     *        item in that data set.     *     * @see #getAdapter()      */    @Override    public void setAdapter(ListAdapter adapter) {        if (mAdapter != null && mDataSetObserver != null) {            mAdapter.unregisterDataSetObserver(mDataSetObserver);        }        resetList();        mRecycler.clear();        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);        } else {            mAdapter = adapter;        }        mOldSelectedPosition = INVALID_POSITION;        mOldSelectedRowId = INVALID_ROW_ID;        // AbsListView#setAdapter will update choice mode states.        su<span id="25_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="25_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="25" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>.setAdapter(adapter);        if (mAdapter != null) {            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();            mOldItemCount = mItemCount;            mItemCount = mAdapter.getCount();            checkFocus();            mDataSetObserver = new AdapterDataSetObserver();            mAdapter.registerDataSetObserver(mDataSetObserver);            mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());            int position;            if (mStackFromBottom) {                position = lookForSelectablePosition(mItemCount - 1, false);            } else {                position = lookForSelectablePosition(0, true);            }            setSelectedPositionInt(position);            setNextSelectedPositionInt(position);            if (mItemCount == 0) {                // Nothing selected                checkSelectionChanged();            }        } else {            mAreAllItemsSelectable = true;            checkFocus();            // Nothing selected            checkSelectionChanged();        }        requestLayout();    }
通过setAdapter方法添加的adapter根据当前ListView的使用情况可能被装饰为一个WrapperListAdapter,比如说添加一个HeaderView或者FooterView。

在该方法中,先把以前的数据和观察者去掉,然后再重新设置各种参数。

里面调用到了resetList()方法:

 /**     * The list is empty. Clear everything out.     */    @Override    void resetList() {        // The parent's resetList() will remove all views from the layout so we need to        // cleanup the state of our footers and headers        clearRecycledState(mHeaderViewInfos);        clearRecycledState(mFooterViewInfos);        su<span id="24_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="24_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="24" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>.resetList();        mLayoutMode = LAYOUT_NORMAL;    }

这里面又调用了clearRecycleState()方法:

private void clearRecycledState(ArrayList<FixedViewInfo> infos) {        if (infos != null) {            final int count = infos.size();            for (int i = 0; i < count; i++) {                final View child = infos.get(i).view;                final LayoutParams p = (LayoutParams) child.getLayoutParams();                if (p != null) {                    p.recycledHeaderFooter = false;                }            }        }    }

清除回收状态。


requestChildRectangleOnScreen:

@Override    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {        int rectTopWithinChild = rect.top;        // offset so rect is in coordinates of the this view        rect.offset(child.getLeft(), child.getTop());        rect.offset(-child.getScrollX(), -child.getScrollY());        final int height = getHeight();        int listUnfadedTop = getScrollY();        int listUnfadedBottom = listUnfadedTop + height;        final int fadingEdge = getVerticalFadingEdgeLength();        if (showingTopFadingEdge()) {            // leave room for top fading edge as long as rect isn't at very top            if ((mSelectedPosition > 0) || (rectTopWithinChild > fadingEdge)) {                listUnfadedTop += fadingEdge;            }        }        int childCount = getChildCount();        int bottomOfBottomChild = getChildAt(childCount - 1).getBottom();        if (showingBottomFadingEdge()) {            // leave room for bottom fading edge as long as rect isn't at very bottom            if ((mSelectedPosition < mItemCount - 1)                    || (rect.bottom < (bottomOfBottomChild - fadingEdge))) {                listUnfadedBottom -= fadingEdge;            }        }        int scrollYDelta = 0;        if (rect.bottom > listUnfadedBottom && rect.top > listUnfadedTop) {            // need to MOVE DOWN to get it in view: move down just enough so            // that the entire rectangle is in view (or at least the first            // screen size chunk).            if (rect.height() > height) {                // just enough to get screen size chunk on                scrollYDelta += (rect.top - listUnfadedTop);            } else {                // get entire rect at bottom of screen                scrollYDelta += (rect.bottom - listUnfadedBottom);            }            // make sure we aren't scrolling beyond the end of our children            int distanceToBottom = bottomOfBottomChild - listUnfadedBottom;            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);        } else if (rect.top < listUnfadedTop && rect.bottom < listUnfadedBottom) {            // need to MOVE UP to get it in view: move up just enough so that            // entire rectangle is in view (or at least the first screen            // size chunk of it).            if (rect.height() > height) {                // screen size chunk                scrollYDelta -= (listUnfadedBottom - rect.bottom);            } else {                // entire rect at top                scrollYDelta -= (listUnfadedTop - rect.top);            }            // make sure we aren't scrolling any further than the top our children            int top = getChildAt(0).getTop();            int deltaToTop = top - listUnfadedTop;            scrollYDelta = Math.max(scrollYDelta, deltaToTop);        }        final boolean scroll = scrollYDelta != 0;        if (scroll) {            scrollListItemsBy(-scrollYDelta);            positionSelector(INVALID_POSITION, child);            mSelectedTop = child.getTop();            invalidate();        }        return scroll;    }

    当组里的某个子项需要被定位在屏幕的某个矩形范围时,调用此方法。

里面调用了showingTopFadingEdge()和showingBottomFadingEdge():

/**     * @return Whether the list needs to show the top fading edge     */    private boolean showingTopFadingEdge() {        final int listTop = mScrollY + mListPadding.top;        return (mFirstPosition > 0) || (getChildAt(0).getTop() > listTop);    }    /**     * @return Whether the list needs to show the bottom fading edge     */    private boolean showingBottomFadingEdge() {        final int childCount = getChildCount();        final int bottomOfBottomChild = getChildAt(childCount - 1).getBottom();        final int lastVisiblePosition = mFirstPosition + childCount - 1;        final int listBottom = mScrollY + getHeight() - mListPadding.bottom;        return (lastVisiblePosition < mItemCount - 1)                         || (bottomOfBottomChild < listBottom);    }

 是否显示上边缘或者下边缘。

下面这个方法是默认访问修饰符(同一包中类可以访问):

/**     * {@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());        }    }

根据猜测,该方法也是用于调整listView中item的位置,如果滑动到第一个item或最后一个item时会出现反弹,调用了fillDown()、fillUp()、correctTooHigh()和correctTooLow()方法。


fillDown:

    /**     * Fills the list from pos down to the end of the list view.     *     * @param pos The first position to put in the list     *     * @param nextTop The location where the top of the item associated with pos     *        should be drawn     *     * @return The view that is currently selected, if it happens to be in the     *         range that we draw.     */    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++;        }        return selectedView;    }

填充从pos到list底部所有的item。里面调用到了makeAndAddView方法:

/**     * Obtain the view and add it to our list of children. The view can be made     * <span id="22_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="22_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=fresh&k0=fresh&kdi0=0&luki=2&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="22" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">fresh</span></a></span>, converted from an unused view, or used as is if it was in the     * recycle bin.     *     * @param position Logical position in the list     * @param y Top or bottom edge of the view to add     * @param flow If flow is true, align top edge to y. If false, align bottom     *        edge to y.     * @param childrenLeft Left edge where children should be positioned     * @param selected Is this position selected?     * @return View that was added     */    private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,            boolean selected) {        View child;        if (!mDataChanged) {            // Try to use an existing view for this position            child = mRecycler.getActiveView(position);            if (child != null) {                if (ViewDebug.TRACE_RECYCLER) {                    ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,                            position, getChildCount());                }                // Found it -- we're using an existing child                // This just needs to be positioned                setupChild(child, position, y, flow, childrenLeft, selected, true);                return child;            }        }        // Make a new view for this position, or convert an unused view if possible        child = obtainView(position, mIsScrap);        // This needs to be positioned and <span id="23_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="23_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="23" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>d        setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);        return child;    }

获取视图填充到列表的item中去,视图可以是从未使用过的视图转换过来,也可以使从回收站复用的视图。在该方法中,先从查找是否有可重用视图,如果有,使用可重用视图。然后

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

通过obtainView方法获取一个view(有可能是从未使用视图转换过来(obtainView方法是在AbsListView方法中定义)),再重新测量和定位View。调用到了setupChild方法:

/**     * Add a view as a child and make sure it is <span id="18_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="18_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="18" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>d (if necessary) and     * positioned pro<span id="19_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="19_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="19" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>ly.     *     * @param child The view to add     * @param position The position of this child     * @param y The y position relative to which this view will be positioned     * @param flowDown If true, align top edge to y. If false, align bottom     *        edge to y.     * @param childrenLeft Left edge where children should be positioned     * @param selected Is this position selected?     * @param recycled Has this view been pulled from the recycle bin? If so it     *        does not need to be re<span id="20_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="20_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="20" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>d.     */    private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,            boolean selected, boolean recycled) {        final boolean isSelected = selected && shouldShowSelector();        final boolean updateChildSelected = isSelected != child.isSelected();        final int mode = mTouchMode;        final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&                mMotionPosition == position;        final boolean updateChildPressed = isPressed != child.isPressed();        final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();        // Respect layout params that are already in the view. Otherwise make some up...        // noinspection unchecked        AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();        if (p == null) {            p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                    ViewGroup.LayoutParams.WRAP_CONTENT, 0);        }        p.viewType = mAdapter.getItemViewType(position);        if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter &&                p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {            attachViewToParent(child, flowDown ? -1 : 0, p);        } else {            p.forceAdd = false;            if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {                p.recycledHeaderFooter = true;            }            addViewInLayout(child, flowDown ? -1 : 0, p, true);        }        if (updateChildSelected) {            child.setSelected(isSelected);        }        if (updateChildPressed) {            child.setPressed(isPressed);        }        if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {            if (child instanceof Checkable) {                ((Checkable) child).setChecked(mCheckStates.get(position));            } else if (getContext().getApplicationInfo().targetSdkVersion                    >= android.os.Build.VERSION_CODES.HONEYCOMB) {                child.setActivated(mCheckStates.get(position));            }        }        if (needToMeasure) {            int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,                    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.<span id="21_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="21_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="21" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>(childWidthSpec, childHeightSpec);        } else {            cleanupLayoutState(child);        }        final int w = child.getMeasuredWidth();        final int h = child.getMeasuredHeight();        final int childTop = flowDown ? y : y - h;        if (needToMeasure) {            final int childRight = childrenLeft + w;            final int childBottom = childTop + h;            child.layout(childrenLeft, childTop, childRight, childBottom);        } else {            child.offsetLeftAndRight(childrenLeft - child.getLeft());            child.offsetTopAndBottom(childTop - child.getTop());        }        if (mCachingStarted && !child.isDrawingCacheEnabled()) {            child.setDrawingCacheEnabled(true);        }        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)                != position) {            child.jumpDrawablesToCurrentState();        }    }

添加视图作为一个子视图,确保测量(如果需要的话)和视图在父视图中正确定位。

再接回到fillUp:

/**     * Fills the list from pos up to the top of the list view.     *     * @param pos The first position to put in the list     *     * @param nextBottom The location where the bottom of the item associated     *        with pos should be drawn     *     * @return The view that is currently selected     */    private View fillUp(int pos, int nextBottom) {        View selectedView = null;        int end = 0;        if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {            end = mListPadding.top;        }        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--;        }        mFirstPosition = pos + 1;        return selectedView;    }

其填充原理同上面fillDown。

   还有用到的correctTooHigh():

/**     * Check if we have dragged the bottom of the list too high (we have pushed the     * top element off the top of the screen when we did not need to). Correct by sliding     * everything back down.     *     * @param childCount Number of children     */    private void correctTooHigh(int childCount) {        // First see if the last item is visible. If it is not, it is OK for the        // top of the list to be pushed up.        int lastPosition = mFirstPosition + childCount - 1;        if (lastPosition == mItemCount - 1 && childCount > 0) {            // Get the last child ...            final View lastChild = getChildAt(childCount - 1);            // ... and its bottom edge            final int lastBottom = lastChild.getBottom();            // This is bottom of our drawable area            final int end = (mBottom - mTop) - mListPadding.bottom;            // This is how far the bottom edge of the last view is from the bottom of the            // drawable area            int bottomOffset = end - lastBottom;            View firstChild = getChildAt(0);            final int firstTop = firstChild.getTop();            // Make sure we are 1) Too high, and 2) Either there are more rows above the            // first row or the first row is scrolled off the top of the drawable area            if (bottomOffset > 0 && (mFirstPosition > 0 || firstTop < mListPadding.top))  {                if (mFirstPosition == 0) {                    // Don't pull the top too far down                    bottomOffset = Math.min(bottomOffset, mListPadding.top - firstTop);                }                // Move everything down                offsetChildrenTopAndBottom(bottomOffset);                if (mFirstPosition > 0) {                    // Fill the gap that was opened above mFirstPosition with more rows, if                    // possible                    fillUp(mFirstPosition - 1, firstChild.getTop() - mDividerHeight);                    // Close up the remaining gap                    adjustViewsUpOrDown();                }            }        }    }

该方法的作用是如果拖动listview底部过高,使其能够正确的反弹回来。里面调用到了前面讲到的fillup和adjustViewUpOrDown方法。

与其对应的correctTooLow,不做过多解释。

/**     * Check if we have dragged the bottom of the list too low (we have pushed the     * bottom element off the bottom of the screen when we did not need to). Correct by sliding     * everything back up.     *     * @param childCount Number of children     */    private void correctTooLow(int childCount) {        // First see if the first item is visible. If it is not, it is OK for the        // bottom of the list to be pushed down.        if (mFirstPosition == 0 && childCount > 0) {            // Get the first child ...            final View firstChild = getChildAt(0);            // ... and its top edge            final int firstTop = firstChild.getTop();            // This is top of our drawable area            final int start = mListPadding.top;            // This is bottom of our drawable area            final int end = (mBottom - mTop) - mListPadding.bottom;            // This is how far the top edge of the first view is from the top of the            // drawable area            int topOffset = firstTop - start;            View lastChild = getChildAt(childCount - 1);            final int lastBottom = lastChild.getBottom();            int lastPosition = mFirstPosition + childCount - 1;            // Make sure we are 1) Too low, and 2) Either there are more rows below the            // last row or the last row is scrolled off the bottom of the drawable area            if (topOffset > 0) {                if (lastPosition < mItemCount - 1 || lastBottom > end)  {                    if (lastPosition == mItemCount - 1) {                        // Don't pull the bottom too far up                        topOffset = Math.min(topOffset, lastBottom - end);                    }                    // Move everything up                    offsetChildrenTopAndBottom(-topOffset);                    if (lastPosition < mItemCount - 1) {                        // Fill the gap that was opened below the last position with more rows, if                        // possible                        fillDown(lastPosition + 1, lastChild.getBottom() + mDividerHeight);                        // Close up the remaining gap                        adjustViewsUpOrDown();                    }                } else if (lastPosition == mItemCount - 1) {                    adjustViewsUpOrDown();                                    }            }        }    }

再重新返回到718行:

    /**     * Fills the list from top to bottom, starting with mFirstPosition     *     * @param nextTop The location where the top of the first item should be     *        drawn     *     * @return The view that is currently selected     */    private View fillFromTop(int nextTop) {        mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);        mFirstPosition = Math.min(mFirstPosition, mItemCount - 1);        if (mFirstPosition < 0) {            mFirstPosition = 0;        }        return fillDown(mFirstPosition, nextTop);    }

填充listview从头部到底部,方法内主要是确定MFirstPosition的值,然后调用fillDown方法。

fillFromMiddle:跳转list,是其选中item居中。

/**     * Put mSelectedPosition in the middle of the screen and then build up and     * down from there. This method forces mSelectedPosition to the center.     *     * @param childrenTop Top of the area in which children can be drawn, as     *        <span id="17_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="17_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="17" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>d in pixels     * @param childrenBottom Bottom of the area in which children can be drawn,     *        as measured in pixels     * @return Currently selected view     */    private View fillFromMiddle(int childrenTop, int childrenBottom) {        int height = childrenBottom - childrenTop;        int position = reconcileSelectedPosition();        View sel = makeAndAddView(position, childrenTop, true,                mListPadding.left, true);        mFirstPosition = position;        int selHeight = sel.getMeasuredHeight();        if (selHeight <= height) {            sel.offsetTopAndBottom((height - selHeight) / 2);        }        fillAboveAndBelow(sel, position);        if (!mStackFromBottom) {            correctTooHigh(getChildCount());        } else {            correctTooLow(getChildCount());        }        return sel;    }
里面调用的reconcileSelectedPosition也是来自父类AbsListView,将在该类中详解。


里面调用的fillAboveAndBelow:填充选中项的上下区域(即上下视图)

 

/**     * Once the selected view as been placed, fill up the visible area above and     * below it.     *     * @param sel The selected view     * @param position The position corresponding to sel     */    private void fillAboveAndBelow(View sel, int position) {        final int dividerHeight = mDividerHeight;        if (!mStackFromBottom) {            fillUp(position - 1, sel.getTop() - dividerHeight);            adjustViewsUpOrDown();            fillDown(position + 1, sel.getBottom() + dividerHeight);        } else {            fillDown(position + 1, sel.getBottom() + dividerHeight);            adjustViewsUpOrDown();            fillUp(position - 1, sel.getTop() - dividerHeight);        }    }

fillFromSelection方法:

/**     * Fills the grid based on positioning the new selection at a specific     * location. The selection may be moved so that it does not intersect the     * faded edges. The grid is then filled upwards and downwards from there.     *     * @param selectedTop Where the selected item should be     * @param childrenTop Where to start drawing children     * @param childrenBottom Last pixel where children can be drawn     * @return The view that currently has selection     */    private View fillFromSelection(int selectedTop, int childrenTop, int childrenBottom) {        int fadingEdgeLength = getVerticalFadingEdgeLength();        final int selectedPosition = mSelectedPosition;        View sel;        final int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength,                selectedPosition);        final int bottomSelectionPixel = getBottomSelectionPixel(childrenBottom, fadingEdgeLength,                selectedPosition);        sel = makeAndAddView(selectedPosition, selectedTop, true, mListPadding.left, true);        // Some of the newly selected item extends below the bottom of the list        if (sel.getBottom() > bottomSelectionPixel) {            // Find space available above the selection into which we can scroll            // upwards            final int spaceAbove = sel.getTop() - topSelectionPixel;            // Find space required to bring the bottom of the selected item            // fully into view            final int spaceBelow = sel.getBottom() - bottomSelectionPixel;            final int offset = Math.min(spaceAbove, spaceBelow);            // Now offset the selected item to get it into view            sel.offsetTopAndBottom(-offset);        } else if (sel.getTop() < topSelectionPixel) {            // Find space required to bring the top of the selected item fully            // into view            final int spaceAbove = topSelectionPixel - sel.getTop();            // Find space available below the selection into which we can scroll            // downwards            final int spaceBelow = bottomSelectionPixel - sel.getBottom();            final int offset = Math.min(spaceAbove, spaceBelow);            // Offset the selected item to get it into view            sel.offsetTopAndBottom(offset);        }        // Fill in views above and below        fillAboveAndBelow(sel, selectedPosition);        if (!mStackFromBottom) {            correctTooHigh(getChildCount());        } else {            correctTooLow(getChildCount());        }        return sel;    }

      从一个特定的位置(传入的参数selectedTop)使用一个网格状填充,选中项有可能移动而不与边界相交,然后网格填充上至childrenTop,下至childrenBottom的区域。里面调用getTopSelectionPixel和getBottomSelectionPixel方法。

    /**     * Calculate the bottom-<span id="16_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="16_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=most&k0=most&kdi0=0&luki=1&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="16" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">most</span></a></span> pixel we can draw the selection into     *     * @param childrenBottom Bottom pixel were children can be drawn     * @param fadingEdgeLength Length of the fading edge in pixels, if present     * @param selectedPosition The position that will be selected     * @return The bottom-most pixel we can draw the selection into     */    private int getBottomSelectionPixel(int childrenBottom, int fadingEdgeLength,            int selectedPosition) {        int bottomSelectionPixel = childrenBottom;        if (selectedPosition != mItemCount - 1) {            bottomSelectionPixel -= fadingEdgeLength;        }        return bottomSelectionPixel;    }

计算可以在底部最多绘制多少像素。

getTopSelectionPixel:计算可以在底部最多绘制多少像素。

/**     * Smoothly scroll to the specified adapter position. The view will     * scroll such that the indicated position is displayed.     * @param position Scroll to this adapter position.     */    @android.view.RemotableViewMethod    public void smoothScrollToPosition(int position) {        super.smoothScrollToPosition(position);    }

平滑的滑动到指定位置,继承自父类,将在父类AbsListView中进行说明。

/**     * Smoothly scroll to the specified adapter position offset. The view will     * scroll such that the indicated position is displayed.     * @param offset The amount to offset from the adapter position to scroll to.     */    @android.view.RemotableViewMethod    public void smoothScrollByOffset(int offset) {        su<span id="15_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="15_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="15" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>.smoothScrollByOffset(offset);    }

平滑的滚动offset像素的偏移。

moveSelection:从一个旧的选中项移动到新的选中项。

/**     * Fills the list based on positioning the new selection relative to the old     * selection. The new selection will be placed at, above, or below the     * location of the new selection depending on how the selection is moving.     * The selection will then be pinned to the visible part of the screen,     * excluding the edges that are faded. The list is then filled upwards and     * downwards from there.     *     * @param oldSel The old selected view. Useful for trying to put the new     *        selection in the same place     * @param newSel The view that is to become selected. Useful for trying to     *        put the new selection in the same place     * @param delta Which way we are moving     * @param childrenTop Where to start drawing children     * @param childrenBottom Last pixel where children can be drawn     * @return The view that currently has selection     */    private View moveSelection(View oldSel, View newSel, int delta, int childrenTop,            int childrenBottom) {        int fadingEdgeLength = getVerticalFadingEdgeLength();        final int selectedPosition = mSelectedPosition;        View sel;        final int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength,                selectedPosition);        final int bottomSelectionPixel = getBottomSelectionPixel(childrenTop, fadingEdgeLength,                selectedPosition);        if (delta > 0) {            /*             * Case 1: Scrolling down.             */            /*             *     Before           After             *    |       |        |       |             *    +-------+        +-------+             *    |   A   |        |   A   |             *    |   1   |   =>   +-------+             *    +-------+        |   B   |             *    |   B   |        |   2   |             *    +-------+        +-------+             *    |       |        |       |             *             *    Try to keep the top of the previously selected item where it was.             *    oldSel = A             *    sel = B             */            // Put oldSel (A) where it belongs            oldSel = makeAndAddView(selectedPosition - 1, oldSel.getTop(), true,                    mListPadding.left, false);            final int dividerHeight = mDividerHeight;            // Now put the new selection (B) below that            sel = makeAndAddView(selectedPosition, oldSel.getBottom() + dividerHeight, true,                    mListPadding.left, true);            // Some of the newly selected item extends below the bottom of the list            if (sel.getBottom() > bottomSelectionPixel) {                // Find space available above the selection into which we can scroll upwards                int spaceAbove = sel.getTop() - topSelectionPixel;                // Find space required to bring the bottom of the selected item fully into view                int spaceBelow = sel.getBottom() - bottomSelectionPixel;                // Don't scroll more than half the height of the list                int halfVerticalSpace = (childrenBottom - childrenTop) / 2;                int offset = Math.min(spaceAbove, spaceBelow);                offset = Math.min(offset, halfVerticalSpace);                // We placed oldSel, so offset that item                oldSel.offsetTopAndBottom(-offset);                // Now offset the selected item to get it into view                sel.offsetTopAndBottom(-offset);            }            // Fill in views above and below            if (!mStackFromBottom) {                fillUp(mSelectedPosition - 2, sel.getTop() - dividerHeight);                adjustViewsUpOrDown();                fillDown(mSelectedPosition + 1, sel.getBottom() + dividerHeight);            } else {                fillDown(mSelectedPosition + 1, sel.getBottom() + dividerHeight);                adjustViewsUpOrDown();                fillUp(mSelectedPosition - 2, sel.getTop() - dividerHeight);            }        } else if (delta < 0) {            /*             * Case 2: Scrolling up.             */            /*             *     Before           After             *    |       |        |       |             *    +-------+        +-------+             *    |   A   |        |   A   |             *    +-------+   =>   |   1   |             *    |   B   |        +-------+             *    |   2   |        |   B   |             *    +-------+        +-------+             *    |       |        |       |             *             *    Try to keep the top of the item about to become selected where it was.             *    newSel = A             *    olSel = B             */            if (newSel != null) {                // Try to position the top of newSel (A) where it was before it was selected                sel = makeAndAddView(selectedPosition, newSel.getTop(), true, mListPadding.left,                        true);            } else {                // If (A) was not on screen and so did not have a view, position                // it above the oldSel (B)                sel = makeAndAddView(selectedPosition, oldSel.getTop(), false, mListPadding.left,                        true);            }            // Some of the newly selected item extends above the top of the list            if (sel.getTop() < topSelectionPixel) {                // Find space required to bring the top of the selected item fully into view                int spaceAbove = topSelectionPixel - sel.getTop();               // Find space available below the selection into which we can scroll downwards                int spaceBelow = bottomSelectionPixel - sel.getBottom();                // Don't scroll more than half the height of the list                int halfVerticalSpace = (childrenBottom - childrenTop) / 2;                int offset = Math.min(spaceAbove, spaceBelow);                offset = Math.min(offset, halfVerticalSpace);                // Offset the selected item to get it into view                sel.offsetTopAndBottom(offset);            }            // Fill in views above and below            fillAboveAndBelow(sel, selectedPosition);        } else {            int oldTop = oldSel.getTop();            /*             * Case 3: Staying still             */            sel = makeAndAddView(selectedPosition, oldTop, true, mListPadding.left, true);            // We're staying still...            if (oldTop < childrenTop) {                // ... but the top of the old selection was off screen.                // (This can happen if the data changes size out from under us)                int newBottom = sel.getBottom();                if (newBottom < childrenTop + 20) {                    // Not enough visible -- bring it onscreen                    sel.offsetTopAndBottom(childrenTop - sel.getTop());                }            }            // Fill in views above and below            fillAboveAndBelow(sel, selectedPosition);        }        return sel;    }

当Size变化时:

   @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        if (getChildCount() > 0) {            View focusedChild = getFocusedChild();            if (focusedChild != null) {                final int childPosition = mFirstPosition + indexOfChild(focusedChild);                final int childBottom = focusedChild.getBottom();                final int offset = Math.max(0, childBottom - (h - mPaddingTop));                final int top = focusedChild.getTop() - offset;                if (mFocusSelector == null) {                    mFocusSelector = new FocusSelector();                }                post(mFocusSelector.setup(childPosition, top));            }        }        su<span id="14_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="14_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="14" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>.onSizeChanged(w, h, oldw, oldh);    }

测量控件大小位置,为绘制View做准备:

 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // Sets up mListPadding        su<span id="11_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="11_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="11" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int childWidth = 0;        int childHeight = 0;        int childState = 0;        mItemCount = mAdapter == null ? 0 : mAdapter.getCount();        if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||                heightMode == MeasureSpec.UNSPECIFIED)) {            final View child = obtainView(0, mIsScrap);            <span id="12_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="12_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="12" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>ScrapChild(child, 0, widthMeasureSpec);            childWidth = child.getMeasuredWidth();            childHeight = child.getMeasuredHeight();            childState = combineMeasuredStates(childState, child.getMeasuredState());            if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(                    ((LayoutParams) child.getLayoutParams()).viewType)) {                mRecycler.addScrapView(child, -1);            }        }        if (widthMode == MeasureSpec.UNSPECIFIED) {            widthSize = mListPadding.left + mListPadding.right + childWidth +                    getVerticalScrollbarWidth();        } else {            widthSize |= (childState&MEASURED_STATE_MASK);        }        if (heightMode == MeasureSpec.UNSPECIFIED) {            heightSize = mListPadding.top + mListPadding.bottom + childHeight +                    getVerticalFadingEdgeLength() * 2;        }        if (heightMode == MeasureSpec.AT_MOST) {            // TODO: after first layout we should maybe start at the first visible position, not 0            heightSize = <span id="13_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="13_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="13" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>HeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);        }        setMeasuredDimension(widthSize , heightSize);        mWidthMeasureSpec = widthMeasureSpec;            }

里面调用到measureScrapChild,测量废弃的子View:


 private void measureScrapChild(View child, int position, int widthMeasureSpec) {        LayoutParams p = (LayoutParams) child.getLayoutParams();        if (p == null) {            p = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                    ViewGroup.LayoutParams.WRAP_CONTENT, 0);            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.<span id="10_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="10_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="10" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>(childWidthSpec, childHeightSpec);    }

测量时是否回收资源:

/**     * @return True to recycle the views used to <span id="8_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="8_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="8" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span> this ListView in     *         UNSPECIFIED/AT_MOST modes, false otherwise.     * @hide     */    @ViewDebug.ExportedPro<span id="9_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="9_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="9" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>ty(category = "list")    protected boolean recycleOnMeasure() {        return true;    }

 在UNSPECIFIED或者AT_MOST模式下返回true(即回收),其他模式下返回false。

里面还调用了measureHeightOfChildren:测量子控件的高度

 /**     * Measures the height of the given range of children (inclusive) and     * returns the height with this ListView's padding and divider heights     * included. If maxHeight is provided, the measuring will stop when the     * current height reaches maxHeight.     *     * @param widthMeasureSpec The width <span id="4_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="4_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="4" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span> spec to be given to a child's     *            {@link View#measure(int, int)}.     * @param startPosition The position of the first child to be shown.     * @param endPosition The (inclusive) position of the last child to be     *            shown. Specify {@link #NO_POSITION} if the last child should be     *            the last available child from the adapter.     * @param maxHeight The maximum height that will be returned (if all the     *            children don't fit in this value, this value will be     *            returned).     * @param disallowPartialChildPosition In general, whether the returned     *            height should only contain entire children. This is more     *            powerful--it is the first inclusive position at which partial     *            children will not be allowed. Example: it looks nice to have     *            at least 3 completely visible children, and in portrait this     *            will <span id="5_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="5_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=most&k0=most&kdi0=0&luki=1&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="5" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">most</span></a></span> likely fit; but in landscape there could be times     *            when even 2 children can not be completely shown, so a value     *            of 2 (remember, inclusive) would be good (assuming     *            startPosition is 0).     * @return The height of this ListView with the given children.     */    final int <span id="6_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="6_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="6" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>HeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,            final int maxHeight, int disallowPartialChildPosition) {        final ListAdapter adapter = mAdapter;        if (adapter == null) {            return mListPadding.top + mListPadding.bottom;        }        // Include the padding of the list        int returnedHeight = mListPadding.top + mListPadding.bottom;        final int dividerHeight = ((mDividerHeight > 0) && mDivider != null) ? mDividerHeight : 0;        // The previous height value that was less than maxHeight and contained        // no partial children        int prevHeightWithoutPartialChild = 0;        int i;        View child;        // mItemCount - 1 since endPosition parameter is inclusive        endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;        final AbsListView.RecycleBin recycleBin = mRecycler;        final boolean recyle = recycleOnMeasure();        final boolean[] isScrap = mIsScrap;        for (i = startPosition; i <= endPosition; ++i) {            child = obtainView(i, isScrap);            <span id="7_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="7_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=measure&k0=measure&kdi0=0&luki=5&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="7" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">measure</span></a></span>ScrapChild(child, i, widthMeasureSpec);            if (i > 0) {                // Count the divider for all but one child                returnedHeight += dividerHeight;            }            // Recycle the view before we possibly return from the method            if (recyle && recycleBin.shouldRecycleViewType(                    ((LayoutParams) child.getLayoutParams()).viewType)) {                recycleBin.addScrapView(child, -1);            }            returnedHeight += child.getMeasuredHeight();            if (returnedHeight >= maxHeight) {                // We went over, figure out which height to return.  If returnedHeight > maxHeight,                // then the i'th position did not fit completely.                return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)                            && (i > disallowPartialChildPosition) // We've past the min pos                            && (prevHeightWithoutPartialChild > 0) // We have a prev height                            && (returnedHeight != maxHeight) // i'th child did not fit completely                        ? prevHeightWithoutPartialChild                        : maxHeight;            }            if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {                prevHeightWithoutPartialChild = returnedHeight;            }        }        // At this point, we went through the range of children, and they each        // completely fit, so return the returnedHeight        return returnedHeight;    }

 测量给定的子控件高度,返回的高度中包括ListView距离顶部和分割线高度,如果计算到的高度等于最大高度,则停止测量。


找到移动到的位置:

@Override    int findMotionRow(int y) {        int childCount = getChildCount();        if (childCount > 0) {            if (!mStackFromBottom) {                for (int i = 0; i < childCount; i++) {                    View v = getChildAt(i);                    if (y <= v.getBottom()) {                        return mFirstPosition + i;                    }                }            } else {                for (int i = childCount - 1; i >= 0; i--) {                    View v = getChildAt(i);                    if (y >= v.getTop()) {                        return mFirstPosition + i;                    }                }            }        }        return INVALID_POSITION;    }


填充一个从在屏幕上特定的位置。
/**     * Put a specific item at a specific location on the screen and then build     * up and down from there.     *     * @param position The reference view to use as the starting point     * @param top Pixel offset from the top of this view to the top of the     *        reference view.     *     * @return The selected view, or null if the selected view is outside the     *         visible area.     */    private View fillSpecific(int position, int top) {        boolean tempIsSelected = position == mSelectedPosition;        View temp = makeAndAddView(position, top, true, mListPadding.left, tempIsSelected);        // Possibly changed again in fillUp if we add rows above this one.        mFirstPosition = position;        View above;        View below;        final int dividerHeight = mDividerHeight;        if (!mStackFromBottom) {            above = fillUp(position - 1, temp.getTop() - dividerHeight);            // This will correct for the top of the first view not touching the top of the list            adjustViewsUpOrDown();            below = fillDown(position + 1, temp.getBottom() + dividerHeight);            int childCount = getChildCount();            if (childCount > 0) {                correctTooHigh(childCount);            }        } else {            below = fillDown(position + 1, temp.getBottom() + dividerHeight);            // This will correct for the bottom of the last view not touching the bottom of the list            adjustViewsUpOrDown();            above = fillUp(position - 1, temp.getTop() - dividerHeight);            int childCount = getChildCount();            if (childCount > 0) {                 correctTooLow(childCount);            }        }        if (tempIsSelected) {            return temp;        } else if (above != null) {            return above;        } else {            return below;        }    }

参数

position:指定位置,

top:从特定View顶部到ListView顶部距离

返回:选中的view,如果view不在可视范围内,则返回空。

layoutChild:为每个子控件布局。

@Override    protected void layoutChildren() {        final boolean blockLayoutRequests = mBlockLayoutRequests;        if (!blockLayoutRequests) {            mBlockLayoutRequests = true;        } else {            return;        }        try {            su<span id="3_nwp" style="width: auto; height: auto; float: none;"><a target=_blank id="3_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=16&is_app=0&jk=2d563720f12106bb&k=per&k0=per&kdi0=0&luki=3&n=10&p=baidu&q=v77y4_cpr&rb=0&rs=1&seller_id=1&sid=bb0621f12037562d&ssp2=1&stid=0&t=tpclicked3_hc&tu=u2102575&u=http%3A%2F%2Fwww%2Eylzx8%2Ecn%2Fyidongkaifa%2Fandroid%2F315491%2Ehtml&urlid=0" target="_blank" mpid="3" style="color: rgb(1, 70, 108); text-decoration: none;"><span style="color: rgb(0, 0, 255); width: auto; height: auto;">per</span></a></span>.layoutChildren();            invalidate();            if (mAdapter == null) {                resetList();                invokeOnItemScrollListener();                return;            }            int childrenTop = mListPadding.top;            int childrenBottom = mBottom - mTop - mListPadding.bottom;            int childCount = getChildCount();            int index = 0;            int delta = 0;            View sel;            View oldSel = null;            View oldFirst = null;            View newSel = null;            View focusLayoutRestoreView = null;            // Remember stuff we will need down below            switch (mLayoutMode) {            case LAYOUT_SET_SELECTION:                index = mNextSelectedPosition - mFirstPosition;                if (index >= 0 && index < childCount) {                    newSel = getChildAt(index);                }                break;            case LAYOUT_FORCE_TOP:            case LAYOUT_FORCE_BOTTOM:            case LAYOUT_SPECIFIC:            case LAYOUT_SYNC:                break;            case LAYOUT_MOVE_SELECTION:            default:                // Remember the previously selected view                index = mSelectedPosition - mFirstPosition;                if (index >= 0 && index < childCount) {                    oldSel = getChildAt(index);                }                // Remember the previous first child                oldFirst = getChildAt(0);                if (mNextSelectedPosition >= 0) {                    delta = mNextSelectedPosition - mSelectedPosition;                }                // Caution: newSel might be null                newSel = getChildAt(index + delta);            }            boolean dataChanged = mDataChanged;            if (dataChanged) {                handleDataChanged();            }            // Handle the empty set by removing all views that are visible            // and calling it a day            if (mItemCount == 0) {                resetList();                invokeOnItemScrollListener();                return;            } else if (mItemCount != mAdapter.getCount()) {                throw new IllegalStateException("The content of the adapter has changed but "                        + "ListView did not receive a notification. Make sure the content of "                        + "your adapter is not modified from a background thread, but only "                        + "from the UI thread. [in ListView(" + getId() + ", " + getClass()                         + ") with Adapter(" + mAdapter.getClass() + ")]");            }            setSelectedPositionInt(mNextSelectedPosition);            // Pull all children into the RecycleBin.            // These views will be reused if possible            final int firstPosition = mFirstPosition;            final RecycleBin recycleBin = mRecycler;            // reset the focus restoration            View focusLayoutRestoreDirectChild = null;            // Don't put header or footer views into the Recycler. Those are            // already cached in mHeaderViews;            if (dataChanged) {                for (int i = 0; i < childCount; i++) {                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);                    if (ViewDebug.TRACE_RECYCLER) {                        ViewDebug.trace(getChildAt(i),                                ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i);                    }                }            } else {                recycleBin.fillActiveViews(childCount, firstPosition);            }            // take focus back to us temporarily to avoid the eventual            // call to clear focus when removing the focused child below            // from messing things up when ViewAncestor assigns focus back            // to someone else            final View focusedChild = getFocusedChild();            if (focusedChild != null) {                // TODO: in some cases focusedChild.getParent() == null                // we can remember the focused view to restore after relayout if the                // data hasn't changed, or if the focused position is a header or footer                if (!dataChanged || isDirectChildHeaderOrFooter(focusedChild)) {                    focusLayoutRestoreDirectChild = focusedChild;                    // remember the specific view that had focus                    focusLayoutRestoreView = findFocus();                    if (focusLayoutRestoreView != null) {                        // tell it we are going to mess with it                        focusLayoutRestoreView.onStartTemporaryDetach();                    }                }                requestFocus();            }            // Clear out old views            detachAllViewsFromParent();            switch (mLayoutMode) {            case LAYOUT_SET_SELECTION:                if (newSel != null) {                    sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom);                } else {                    sel = fillFromMiddle(childrenTop, childrenBottom);                }                break;            case LAYOUT_SYNC:                sel = fillSpecific(mSyncPosition, mSpecificTop);                break;            case LAYOUT_FORCE_BOTTOM:                sel = fillUp(mItemCount - 1, childrenBottom);                adjustViewsUpOrDown();                break;            case LAYOUT_FORCE_TOP:                mFirstPosition = 0;                sel = fillFromTop(childrenTop);                adjustViewsUpOrDown();                break;            case LAYOUT_SPECIFIC:                sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop);                break;            case LAYOUT_MOVE_SELECTION:                sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom);                break;            default:                if (childCount == 0) {                    if (!mStackFromBottom) {                        final int position = lookForSelectablePosition(0, true);                        setSelectedPositionInt(position);                        sel = fillFromTop(childrenTop);                    } else {                        final int position = lookForSelectablePosition(mItemCount - 1, false);                        setSelectedPositionInt(position);                        sel = fillUp(mItemCount - 1, childrenBottom);                    }                } else {                    if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {                        sel = fillSpecific(mSelectedPosition,                                oldSel == null ? childrenTop : oldSel.getTop());                    } else if (mFirstPosition < mItemCount) {                        sel = fillSpecific(mFirstPosition,                                oldFirst == null ? childrenTop : oldFirst.getTop());                    } else {                        sel = fillSpecific(0, childrenTop);                    }                }                break;            }            // Flush any cached views that did not get reused above            recycleBin.scrapActiveViews();            if (sel != null) {                // the current selected item should get focus if items                // are focusable                if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) {                    final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild &&                            focusLayoutRestoreView.requestFocus()) || sel.requestFocus();                    if (!focusWasTaken) {                        // selected item didn't take focus, fine, but still want                        // to make sure something else outside of the selected view                        // has focus                        final View focused = getFocusedChild();                        if (focused != null) {                            focused.clearFocus();                        }                        positionSelector(INVALID_POSITION, sel);                    } else {                        sel.setSelected(false);                        mSelectorRect.setEmpty();                    }                } else {                    positionSelector(INVALID_POSITION, sel);                }                mSelectedTop = sel.getTop();            } else {                if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {                    View child = getChildAt(mMotionPosition - mFirstPosition);                    if (child != null) positionSelector(mMotionPosition, child);                } else {                    mSelectedTop = 0;                    mSelectorRect.setEmpty();                }                // even if there is not selected position, we may need to restore                // focus (i.e. something focusable in touch mode)                if (hasFocus() && focusLayoutRestoreView != null) {                    focusLayoutRestoreView.requestFocus();                }            }            // tell focus view we are done mucking with it, if it is still in            // our view hierarchy.            if (focusLayoutRestoreView != null                    && focusLayoutRestoreView.getWindowToken() != null) {                focusLayoutRestoreView.onFinishTemporaryDetach();            }                        mLayoutMode = LAYOUT_NORMAL;            mDataChanged = false;            mNeedSync = false;            setNextSelectedPositionInt(mSelectedPosition);            updateScrollIndicators();            if (mItemCount > 0) {                checkSelectionChanged();            }            invokeOnItemScrollListener();        } finally {            if (!blockLayoutRequests) {                mBlockLayoutRequests = false;            }        }    }

里面调用到了isDirectChildHeaderOrFooter方法:判断传入View是否是HeaderView或者FooterView。

 /**     * @param child a direct child of this list.     * @return Whether child is a header or footer view.     */    private boolean isDirectChildHeaderOrFooter(View child) {        final ArrayList<FixedViewInfo> headers = mHeaderViewInfos;        final int numHeaders = headers.size();        for (int i = 0; i < numHeaders; i++) {            if (child == headers.get(i).view) {                return true;            }        }        final ArrayList<FixedViewInfo> footers = mFooterViewInfos;        final int numFooters = footers.size();        for (int i = 0; i < numFooters; i++) {            if (child == footers.get(i).view) {                return true;            }        }        return false;    }

设置选中项:setSelection

/**     * Sets the currently selected item. If in touch mode, the item will not be selected     * but it will still be positioned appropriately. If the specified selection position     * is less than 0, then the item at position 0 will be selected.     *     * @param position Index (starting at 0) of the data item to be selected.     */    @Override    public void setSelection(int position) {        setSelectionFromTop(position, 0);    }
里面调用setSelectionFromTop方法:

/**     * Sets the selected item and positions the selection y pixels from the top edge     * of the ListView. (If in touch mode, the item will not be selected but it will     * still be positioned appropriately.)     *     * @param position Index (starting at 0) of the data item to be selected.     * @param y The distance from the top edge of the ListView (plus padding) that the     *        item will be positioned.     */    public void setSelectionFromTop(int position, int y) {        if (mAdapter == null) {            return;        }        if (!isInTouchMode()) {            position = lookForSelectablePosition(position, true);            if (position >= 0) {                setNextSelectedPositionInt(position);            }        } else {            mResurrectToPosition = position;        }        if (position >= 0) {            mLayoutMode = LAYOUT_SPECIFIC;            mSpecificTop = mListPadding.top + y;            if (mNeedSync) {                mSyncPosition = position;                mSyncRowId = mAdapter.getItemId(position);            }            requestLayout();        }    }

重新设置选中项,并且重新布局。

设置第几条被选中,SelectionInt:

/**     * Makes the item at the supplied position selected.     *      * @param position the position of the item to select     */    @Override    void setSelectionInt(int position) {        setNextSelectedPositionInt(position);        boolean awakeScrollbars = false;        final int selectedPosition = mSelectedPosition;        if (selectedPosition >= 0) {            if (position == selectedPosition - 1) {                awakeScrollbars = true;            } else if (position == selectedPosition + 1) {                awakeScrollbars = true;            }        }        layoutChildren();        if (awakeScrollbars) {            awakenScrollBars();        }    }

寻找可供选中的Position(最大值或最小值):

/**     * Find a position that can be selected (i.e., is not a separator).     *     * @param position The starting position to look at.     * @param lookDown Whether to look down for other positions.     * @return The next selectable position starting at position and then searching either up or     *         down. Returns {@link #INVALID_POSITION} if nothing can be found.     */    @Override    int lookForSelectablePosition(int position, boolean lookDown) {        final ListAdapter adapter = mAdapter;        if (adapter == null || isInTouchMode()) {            return INVALID_POSITION;        }        final int count = adapter.getCount();        if (!mAreAllItemsSelectable) {            if (lookDown) {                position = Math.max(0, position);                while (position < count && !adapter.isEnabled(position)) {                    position++;                }            } else {                position = Math.min(position, count - 1);                while (position >= 0 && !adapter.isEnabled(position)) {                    position--;                }            }            if (position < 0 || position >= count) {                return INVALID_POSITION;            }            return position;        } else {            if (position < 0 || position >= count) {                return INVALID_POSITION;            }            return position;        }    }

lookDown:是否往下选择。

 PS:3600行的代码,在一篇文章里面偏多,所以把本文分为了上下两篇,分析的比较简陋,但会在以后慢慢补充。





(本文来自互联网,不代表搜站(http://www.ylzx8.cn/)的观点和立场)
0 0
原创粉丝点击