简介
在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原理的主要内容和整体的实现流程,如有问题,请多指教!