ListView源码分析

简介

在Android开发中,ListView是最常用的View之一,用来展示大量的数据;但是大多数情况下,只是机械式地不断重复地使用,并没有注意内部原理,不太清楚其内部的原理是多么的巧妙,Item如何不断的复用,我们在实际工作中如何参考这种方式,去设计符合我们需要的自定义View。在此我们开始学习ListView的内部设计原理。

ListView的继承关系
在这里插入图片描述

先来看下AdapterView类是抽象,它的子View由Adapter来决定,是公共的基类;定义一些公共的方法;如以下方法;

setOnItemClickListener()
setOnItemLongClickListener()
getAdapter()
setAdapter()
getPositionForView()
getFirstVisiblePosition()//返回第一个可见条目的pos;
getLastVisiblePosition()//返回最后一个可见条目的pos
handleDataChanged()//在子类(ListView、GridView)的layoutChildren()调用

BaseAdapter类

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    //如果返回true,有稳定的id;  默认返回false;
    public boolean hasStableIds() {
        return false;
    }
    
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    
    public boolean areAllItemsEnabled() {
        return true;
    }

    public boolean isEnabled(int position) {
        return true;
    }
    
    public int getItemViewType(int position) {
        return 0;
    }

    public int getViewTypeCount() {
        return 1;
    }

AbsListView是ListView和GridView的直接父类;

AbsListView类RecycleBin

RecycleBin是ListView最核心的地方,用来复用View;其中有两个数组mActiveViews和mScrapViews;

mActiveViews:是一个用来存储可见的Item View的数组,即在屏幕上显示的Item View;

根据getViewTypeCount()来初始化数组长度,每种类型的View存放在不同的ArrayList中。

在getItemViewType()方法的返回值一定要小于getViewTypeCount()的值,否则在复用时报数组下标越界。

mScrapViews:用来存放废弃的View(从可见随滑动变为不可见的View),当下次getView()的时候,从mScrapViews数组中取出一个来复用;

RecycleBin类

class RecycleBin {
    private RecyclerListener mRecyclerListener;

    /**
     * 第一条可见的Item的position
     */
    private int mFirstActivePosition;

   	//可见的Item的集合
    private View[] mActiveViews = new View[0];

    //根据不同ViewType存储废弃View集合的数组
    private ArrayList<View>[] mScrapViews;
	
    private int mViewTypeCount;
	//当前的存储废弃View集合
    private ArrayList<View> mCurrentScrap;

	//默认情况下viewTypeCount为1;根据viewTypeCount初始化scrapViews数组,不同viewType对应不同的ArrayList
    public void setViewTypeCount(int viewTypeCount) {
        if (viewTypeCount < 1) {
            throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
        }
        //根据viewTypeCount来初始化数组
        //noinspection unchecked
        ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
        for (int i = 0; i < viewTypeCount; i++) {
            scrapViews[i] = new ArrayList<View>();
        }
        mViewTypeCount = viewTypeCount;
        mCurrentScrap = scrapViews[0];
        mScrapViews = scrapViews;
    }
    
    //从mScrapViews中取出一个View复用;并将其从ArrayList中删除;
    View getScrapView(int position) {
        //先判断当前position的ViewType,避免getView()中强转异常;
        final int whichScrap = mAdapter.getItemViewType(position);
        if (whichScrap < 0) {
            return null;
        }
        if (mViewTypeCount == 1) {
            return retrieveFromScrap(mCurrentScrap, position);
        } else if (whichScrap < mScrapViews.length) {
            return retrieveFromScrap(mScrapViews[whichScrap], position);
        }
        return null;
    }
    //向mScrapViews添加view;当Item滑出屏幕,就会被回收,放入mScrapViews中,以供复用;
    void addScrapView(View scrap, int position) {
        //存储相关参数的类;
        final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) 		              	   scrap.getLayoutParams();
        //回收时所在的position;
        lp.scrappedFromPosition = position;

        //header、footer和不能被回收的View(viewType<0),return;
        
        // Don't scrap views that have transient state.
        final boolean scrapHasTransientState = scrap.hasTransientState();
        if (scrapHasTransientState) {
           
        } else {
            //根据ViewType放入对应的List中
            clearScrapForRebind(scrap);
            if (mViewTypeCount == 1) {
                mCurrentScrap.add(scrap);
            } else {
                mScrapViews[viewType].add(scrap);
            }

            if (mRecyclerListener != null) {
                mRecyclerListener.onMovedToScrapHeap(scrap);
            }
        }
    }
}

观察者模式Observer:ListView的setAdapter()会为Adapter注册监听,当数据集发生改变时,在Adapter中调用notifyDataSetChanged()就会AdapterDataSetObserver的onChanged()方法进而调用requestLayout()来重绘;

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
    @Override
    public void onChanged() {
        super.onChanged();
        if (mFastScroll != null) {
            mFastScroll.onSectionsChanged();
        }
    }

    @Override
    public void onInvalidated() {
        super.onInvalidated();
        if (mFastScroll != null) {
            mFastScroll.onSectionsChanged();
        }
    }
}

从setAdapter()开始

首先,我们从setAdapter()方法开始,先看下setAdapter()方法:

 @Override
public void setAdapter(ListAdapter adapter) {
	//1:取消观察者监听
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
	//2:重置头布局,脚布局状态,并且将mLayoutMode设置为LAYOUT_NORMAL;
    resetList();
	//3:RecycleBin的实例,清空缓存的废弃的View
    mRecycler.clear();
	//4:添加头布局或者脚布局(必须在setAdapter之前)
    if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
        mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
        mAdapter = adapter;
    }

    mOldSelectedPosition = INVALID_POSITION;
    mOldSelectedRowId = INVALID_ROW_ID;

    // AbsListView#setAdapter will update choice mode states.
    super.setAdapter(adapter);

    if (mAdapter != null) {
        mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
        mOldItemCount = mItemCount;
        mItemCount = mAdapter.getCount();
        checkFocus();
		//5:重新注册观察者监听
        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
		//6:根据getViewTypeCount()返回的数据,决定 ArrayList<View>[] scrapViews数组的长度;在复用的情况下,根据每种类型获取对应缓存View集合。
        mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

        int position;
		//7:true表示从底部向上渲染,常用于聊天列表;setStackFromBottom()设置;
        if (mStackFromBottom) {
            //从mItemCount - 1开始,向上搜索
            position = lookForSelectablePosition(mItemCount - 1, false);
        } else {
            //从0开始,向下搜索;
            position = lookForSelectablePosition(0, true);
        }
        setSelectedPositionInt(position);
        setNextSelectedPositionInt(position);

        if (mItemCount == 0) {
            // Nothing selected
            checkSelectionChanged();
        }
    } else {
        mAreAllItemsSelectable = true;
        checkFocus();
        // Nothing selected
        checkSelectionChanged();
    }
	//8 重绘
    requestLayout();
}

从上面的代码可以看出主要做了一下几件事:1)取消观察者监听;2)重置头布局,脚布局状态,并且将mLayoutMode设置为LAYOUT_NORMAL;3)清空缓存的废弃的View;4)添加头布局或者脚布局(必须在setAdapter之前);5)重新注册观察者监听;6)根据getViewTypeCount()返回的数据,决定 ArrayList[] scrapViews数组的长度;在复用的情况下,根据每种类型获取对应缓存View集合;7)是否从底部向上展示,常用于聊天列表;8)重绘。

lookForSelectablePosition():默认情况下,直接返回position;

@Override
int lookForSelectablePosition(int position, boolean lookDown) {
    final ListAdapter adapter = mAdapter;
    if (adapter == null || isInTouchMode()) {
        return INVALID_POSITION;
    }

    final int count = adapter.getCount();
    //默认情况下mAreAllItemsSelectable为true
    if (!mAreAllItemsSelectable) {
        
    }

    if (position < 0 || position >= count) {
        return INVALID_POSITION;
    }

    return position;
}

接着看下requestLayout()方法,调用AbsListView的requestLayout()方法:可以先阅读View的绘制过程

 @Override
public void requestLayout() {
    if (!mBlockLayoutRequests && !mInLayout) {
        super.requestLayout();
    }
}

最后分别依次调用AbsListView的onMeasure()、onLayout()、draw()方法来绘制子View;onLayout()是重点

ListView的绘制过程

当调用setAdapter()或者notifyDataSetChanged()之后都会调用requestLayout()方法,重新绘制Item View;

AbsListView的onMeasure()

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
    final Rect listPadding = mListPadding;
    listPadding.left = mSelectionLeftPadding + mPaddingLeft;
    listPadding.top = mSelectionTopPadding + mPaddingTop;
    listPadding.right = mSelectionRightPadding + mPaddingRight;
    listPadding.bottom = mSelectionBottomPadding + mPaddingBottom;
}

AbsListView的onLayout()子类不能重写该方法,重写layoutChildren()方法;

 @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);

    mInLayout = true;

    final int childCount = getChildCount();
    if (changed) {
        for (int i = 0; i < childCount; i++) {
            getChildAt(i).forceLayout();//重绘Item View;
        }
        mRecycler.markChildrenDirty();
    }
	//由子类重写实现;
    layoutChildren();

    mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;

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

主要看下ListView的layoutChildren()的具体实现,这是最核心的地方:

@Override
protected void layoutChildren() {
  	final int childrenTop = mListPadding.top;
    final int childrenBottom = mBottom - mTop - mListPadding.bottom;
    final int childCount = getChildCount();
    //1:由setAdapter方法resetList()可知,mLayoutMode为LAYOUT_NORMAL,走default分支
    // 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_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);
    }
    //在notifyDataSetChanged之后mDataChanged设置为true;
    boolean dataChanged = mDataChanged;
    if (dataChanged) {
        //1:自上次layout之后,数据集发生变化;
        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();
    }

    // Pull all children into the RecycleBin.
    // These views will be reused if possible
    final int firstPosition = mFirstPosition;
    final RecycleBin recycleBin = mRecycler;
    //数据集发生改变,将所有的item view放入mScrapViews数组中;
    if (dataChanged) {
        for (int i = 0; i < childCount; i++) {
            recycleBin.addScrapView(getChildAt(i), firstPosition+i);
        }
    } else {
        //将所有的 item view放入mActiveViews数组中
        recycleBin.fillActiveViews(childCount, firstPosition);
    }

    // Clear out old views
    detachAllViewsFromParent();
    recycleBin.removeSkippedScrap();

    //由setAdapter方法可知,mLayoutMode为LAYOUT_NORMAL,走default分支
    switch (mLayoutMode) {
        default:
            //当前ListView为空;
            if (childCount == 0) {
                //true表示从底部向上渲染,false:从上往下渲染;
                if (!mStackFromBottom) {
                    final int position = lookForSelectablePosition(0, true);
                    setSelectedPositionInt(position);
                    //从上往下渲染;从mFirstPosition开始
                    sel = fillFromTop(childrenTop);
                } else {
                    final int position = lookForSelectablePosition(mItemCount - 1, false);
                    setSelectedPositionInt(position);
                    //从下往上渲染;类似聊天记录;
                    sel = fillUp(mItemCount - 1, childrenBottom);
                }
            } else {
                if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {
                    //从当前item view 向上或者向下渲染;
                    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;
    }

    //将所有的mActiveViews清空,放入到mScrapViews中;(清空当前显示的Item View)
    // Flush any cached views that did not get reused above
    recycleBin.scrapActiveViews();
	//清空headerView、footerView
    // remove any header/footer that has been temp detached and not re-attached
    removeUnusedFixedViews(mHeaderViewInfos);
    removeUnusedFixedViews(mFooterViewInfos);

    //重置属性
    mLayoutMode = LAYOUT_NORMAL;
    mDataChanged = false;
}

fillFromTop()、fillUp()和fillSpecific()方法中主要调用makeAndAddView(),不断地去获取item View,添加到ListView中;看下makeAndAddView()方法,

  private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
        boolean selected) {
	//数据集没有改变;从mActiveViews数组中获取对应的pos的View;
    if (!mDataChanged) {
        // Try to use an existing view for this position.
        final View activeView = mRecycler.getActiveView(position);
        if (activeView != null) {
            // Found it. We're reusing an existing child, so it just needs
            // to be positioned like a scrap view.
            setupChild(activeView, position, y, flow, childrenLeft, selected, true);
            return activeView;
        }
    }
	//1:获取一个View
    // Make a new view for this position, or convert an unused view if
    // possible.
    final View child = obtainView(position, mIsScrap);
	//2:添加一个View作为ListView的item,重新定位,measure、layout该子View;
    // This needs to be positioned and measured.
    setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);

    return child;
}

makeAndAddView()方法主要做了两件事:1)获取一个View;2)重新定位并且测量;

先来看下obtainView()方法

View obtainView(int position, boolean[] outMetadata) {
    //从mScrapViews数组中获取一个废弃的View;
    final View scrapView = mRecycler.getScrapView(position);
	//调用具体Adapter的getView()方法,这是我们在Adapter中最主要的方法,也是最熟悉的方法,如果scrapView为null,就创建,并赋值;不为空,就复用;
    final View child = mAdapter.getView(position, scrapView, this);
    //为child设置相关参数;
    setItemViewLayoutParams(child, position);
    return child;
}

setupChild():添加一个View作为ListView的Item View,重新定位,measure、layout该子View;

private void setupChild(View child, int position, int y, boolean flowDown, int 	childrenLeft, boolean selected, boolean isAttachedToWindow) {
   
    final boolean updateChildSelected = isSelected != child.isSelected();
 	//是否需要Measure
    final boolean needToMeasure = !isAttachedToWindow || updateChildSelected
            || child.isLayoutRequested();

    if (needToMeasure) {
        //调用child的measure();
        child.measure(childWidthSpec, childHeightSpec);
    } 

    if (needToMeasure) {
        final int childRight = childrenLeft + w;
        final int childBottom = childTop + h;
        //调用child的layout();
        child.layout(childrenLeft, childTop, childRight, childBottom);
    } else {
        child.offsetLeftAndRight(childrenLeft - child.getLeft());
        child.offsetTopAndBottom(childTop - child.getTop());
    }
}

AbsListView的draw();

@Override
public void draw(Canvas canvas) {
    super.draw(canvas);
}

AbsListView的dispatchDraw();

@Override
protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
}

到此一屏的Item View就展示出来了,那上下滑动的时候是如何实现的呢?

滑动操作

对View的事件传递及分发不了解的,可先阅读View的事件传递及分发机制

AbsListView的onInterceptTouchEvent();

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    final int actionMasked = ev.getActionMasked();
    View v;

    if (mPositionScroller != null) {
        mPositionScroller.stop();
    }

    //setFastScrollEnabled()设置,默认情况下为false;mFastScroll为null;
    if (mFastScroll != null && mFastScroll.onInterceptTouchEvent(ev)) {
        return true;
    }

    switch (actionMasked) {
    case MotionEvent.ACTION_DOWN: {
        //主要做一些参数的初始化;
        if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) {
            mMotionCorrection = 0;
            return true;
        }
        
        if (touchMode == TOUCH_MODE_FLING) {
            return true;
        }
        
        break;
    }

    case MotionEvent.ACTION_MOVE: {
        switch (mTouchMode) {
        case TOUCH_MODE_DOWN:
            //判断是否满足滑动条件(竖直方向上滑动距离大于最小的滑动距离),满足就拦截;
            if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) {
                return true;
            }
            break;
        }
        break;
    }

    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP: {
       //回调滑动状态接口;
        reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
        break;
    }
    }

    return false;
}

startScrollIfNeeded():满足滑动条件,返回true;onInterceptTouchEvent()返回true拦截事件,调用AbsListView 的onTouchEvent()方法决定是否消费该事件;

private boolean startScrollIfNeeded(int x, int y, MotionEvent vtev) {
    // Check if we have moved far enough that it looks more like a
    // scroll than a tap
    final int deltaY = y - mMotionY;
    final int distance = Math.abs(deltaY);
    final boolean overscroll = mScrollY != 0;
    //判断是否满足滑动条件;
    if ((overscroll || distance > mTouchSlop) &&
            (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
       
        if (overscroll) {
            mTouchMode = TOUCH_MODE_OVERSCROLL;
            mMotionCorrection = 0;
        } else {
            mTouchMode = TOUCH_MODE_SCROLL;
            mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop;
        }
       //调用onScrollStateChanged();
        reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
        //让父View不拦截事件;
        final ViewParent parent = getParent();
        if (parent != null) {
            parent.requestDisallowInterceptTouchEvent(true);
        }
        scrollIfNeeded(x, y, vtev);
        return true;
    }

    return false;
}

onTouchEvent();

@Override
public boolean onTouchEvent(MotionEvent ev) {
    //disable View依然可以消费事件,只是不响应而已;
    if (!isEnabled()) {
        // A disabled view that is clickable still consumes the touch
        // events, it just doesn't respond to them.
        return isClickable() || isLongClickable();
    }

    if (mPositionScroller != null) {
        mPositionScroller.stop();
    }

	//这一类的方法适用于ListView;
    startNestedScroll(SCROLL_AXIS_VERTICAL);
    
    final int actionMasked = ev.getActionMasked();
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        mNestedYOffset = 0;
    }
    vtev.offsetLocation(0, mNestedYOffset);
    switch (actionMasked) {
        case MotionEvent.ACTION_DOWN: {
            onTouchDown(ev);//主要做一些参数重新赋值;
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            //将滑出的item View回收以备复用,同时创建、复用创建新的Item View填充到ListView;
            onTouchMove(ev, vtev);
            break;
        }

        case MotionEvent.ACTION_UP: {
            onTouchUp(ev);
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            onTouchCancel();
            break;
        }
  
    }

    if (mVelocityTracker != null) {
        mVelocityTracker.addMovement(vtev);
    }
    vtev.recycle();
    return true;
}

onTouchMove()

private void onTouchMove(MotionEvent ev, MotionEvent vtev) {
    //长按事件
    if (mHasPerformedLongPress) {
        // Consume all move events following a successful long press.
        return;
    }

    //数据集发生变化,layoutChildre由ListView具体实现;
    if (mDataChanged) {
        // Re-sync everything if data has been changed
        // since the scroll operation can query the adapter.
        layoutChildren();
    }

    final int y = (int) ev.getY(pointerIndex);

    switch (mTouchMode) {
        case TOUCH_MODE_DOWN:
        case TOUCH_MODE_TAP:
        case TOUCH_MODE_DONE_WAITING:
            //判断是否满足滑动条件;mTouchMode会TOUCH_MODE_OVERSCROLL或者TOUCH_MODE_SCROLL;
            // Check if we have moved far enough that it looks more like a
            // scroll than a tap. If so, we'll enter scrolling mode.
            if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, vtev)) {
                break;
            }
            break;
        case TOUCH_MODE_SCROLL:
        case TOUCH_MODE_OVERSCROLL:
            scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev);
            break;
    }
}

scrollIfNeeded()主要是竖直方向的变化值incrementalDeltaY!= 0,调用trackMotionScroll()方法

private void scrollIfNeeded(int x, int y, MotionEvent vtev) {
    int incrementalDeltaY =
            mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY;
    
    int lastYCorrection = 0;

    if (mTouchMode == TOUCH_MODE_SCROLL) {
        if (y != mLastY) {
            // No need to do all this work if we're not going to move anyway
            boolean atEdge = false;
            if (incrementalDeltaY != 0) {
                atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
            }
        }
    } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
        if (y != mLastY) {
            if (incrementalDeltaY != 0) {
                // Coming back to 'real' list scrolling
                if (mScrollY != 0) {
                    mScrollY = 0;
                    invalidateParentIfNeeded();
                }
                trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
            }
        }
    }
}

trackMotionScroll():判断是否已经处于不可滑动的状态;可滑动状态,回收废弃的Item View,同时创建、复用创建新的Item View填充ListView;

boolean trackMotionScroll(int deltaY, int incrementalDeltaY) {
    final int childCount = getChildCount();
    if (childCount == 0) {
        return true;
    }

    final int firstPosition = mFirstPosition;
	//不能再往下滑动(已经在最顶部)
    final boolean cannotScrollDown = (firstPosition == 0 &&
            firstTop >= listPadding.top && incrementalDeltaY >= 0);
    //不能在往上滑动(已经在最底部)
    final boolean cannotScrollUp = (firstPosition + childCount == mItemCount &&
            lastBottom <= getHeight() - listPadding.bottom && incrementalDeltaY <= 0);
	//incrementalDeltaY在y方向改变的值;
    if (cannotScrollDown || cannotScrollUp) {
        return incrementalDeltaY != 0;
    }
	//是否往下滑动(incrementalDeltaY=event.getY()-lastY)true:手指往上滑动;false:手指往下滑动;
    final boolean down = incrementalDeltaY < 0;

    int start = 0;
    int count = 0;
	//滑动过程中;不断有item View滑出屏幕,将其废弃回收以供复用;
    if (down) {
        
    } else {
        
    }

    final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
    //由其子类重写;既然有View滑出屏幕被回收,就要不断有item View被创建或者复用填充ListView;
    if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
        fillGap(down);
    }
    
    invokeOnItemScrollListener();

    return false;
}

ListView的fillGap():既然有View滑出屏幕被回收,就要不断有item View被创建或者复用填充ListView;不断是向上滑动还是向下滑动,通过fillDown()或者fillUp(),最终都是调用makeAndAddView()方法;这个过程在**layoutChildren()**中已经讲述了;

onTouchUp():主要分为两种情况:1)点击事件执行AbsListView的performItemClick类的run(),调用performItemClick();2)响应fling行为,通过FlingRunnable类不断重复自我调用,直到滑动完成;

private void onTouchUp(MotionEvent ev) {
    switch (mTouchMode) {
    case TOUCH_MODE_DOWN:
    case TOUCH_MODE_TAP:
    case TOUCH_MODE_DONE_WAITING:
        //点击
        final View child = getChildAt(motionPosition - mFirstPosition);
        if (child != null) {
            if (inList && !child.hasExplicitFocusable()) {
                if (mPerformClick == null) {
                    mPerformClick = new PerformClick();
                }

                final AbsListView.PerformClick performClick = mPerformClick;
                if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
                    
                    if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
                        
                        layoutChildren();
                        mTouchModeReset = new Runnable() {
                            @Override
                            public void run() {
                                if (!mDataChanged && !mIsDetaching && isAttachedToWindow()) {
                                    //响应Item View点击事件
                                    performClick.run();
                                }
                            }
                        };
                        postDelayed(mTouchModeReset,
                                ViewConfiguration.getPressedStateDuration());
                    } else {
                       
                    }
                    return;
                } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
                    performClick.run();
                }
            }
        }
        break;
    case TOUCH_MODE_SCROLL:
        final int childCount = getChildCount();
        if (childCount > 0) {
            //已经到ListView顶部,IDLE状态
            if (mFirstPosition == 0 && firstChildTop >= contentTop &&
                    mFirstPosition + childCount < mItemCount &&
                    lastChildBottom <= getHeight() - contentBottom) {
                mTouchMode = TOUCH_MODE_REST;
                reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
            } else {
                //Fling状态中
                boolean flingVelocity = Math.abs(initialVelocity) > mMinimumVelocity;
                if (flingVelocity &&
                        !((mFirstPosition == 0 &&
                                firstChildTop == contentTop - mOverscrollDistance) ||
                          (mFirstPosition + childCount == mItemCount &&
                                lastChildBottom == contentBottom + mOverscrollDistance))) {
                    if (!dispatchNestedPreFling(0, -initialVelocity)) {
                        if (mFlingRunnable == null) {
                            mFlingRunnable = new FlingRunnable();
                        }
                        reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
                        //Fling效果处理
                        mFlingRunnable.start(-initialVelocity);
                        dispatchNestedFling(0, -initialVelocity, true);
                    } else {
                       //IDLE状态
                    }
                } else {
                     //IDLE状态,停止Fling;
                }
            }
        } else {
             //IDLE状态
        }
        break;

    case TOUCH_MODE_OVERSCROLL:
            //Fling
        if (mFlingRunnable == null) {
            mFlingRunnable = new FlingRunnable();
        }
        final VelocityTracker velocityTracker = mVelocityTracker;
        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
        final int initialVelocity =(int)velocityTracker.getYVelocity(mActivePointerId);

        reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
        if (Math.abs(initialVelocity) > mMinimumVelocity) {
            mFlingRunnable.startOverfling(-initialVelocity);
        } else {
            mFlingRunnable.startSpringback();
        }

        break;
    }
    
    // Need to redraw since we probably aren't drawing the selector anymore
    invalidate();
}

1)AbsListView 的PerformClick类的run():主要调用performItemClick()

@Override
public boolean performItemClick(View view, int position, long id) {
    boolean handled = false;
    boolean dispatchItemClick = true;

    //默认为CHOICE_MODE_NONE;
    if (mChoiceMode != CHOICE_MODE_NONE) {
        handled = true;
    }

    if (dispatchItemClick) {
        //AdapterView的performItemClick()只要返回了true,handled就是true;
        handled |= super.performItemClick(view, position, id);
    }

    return handled;
}

AdapterView的performItemClick():设置了ItemClickListener就返回true;

public boolean performItemClick(View view, int position, long id) {
    final boolean result;
    if (mOnItemClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        mOnItemClickListener.onItemClick(this, view, position, id);
        result = true;
    } else {
        result = false;
    }
    return result;
}

以上就是ListView原理的主要内容和整体的实现流程,如有问题,请多指教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值