listView.getChildCount() 包含了headerView、footerView 以及 cellView,
而 Adapter 的 getItemCount() 只包含数据项(对应cellView 的个数),
所以如果有通过listview的childView 找对应的数据item的话,使用时需要注意计算
//在listview 的 adapter中 int cc = listView.getChildCount(); int headerCount = listView.getHeaderViewsCount(); //int footerCount = listView.getFooterViewsCount(); int itemCount = getCount(); for (int i = 0; i < cc; i++) { View child = listView.getChildAt(i); int tmp_pos = listView.getPositionForView(child); int position = tmp_pos - headerCount; if (position < 0 || position >= itemCount) { // 是headerView 或 footerView continue; } // 现在的position 才对应adapter的数据项的index //可以进行 getItemType(position) 等操作 }
原因是在listview 内部会检查是否有 header 或footer ,有的话会用一个 HeaderViewListAdapter 包我们提供的listAdapter 包装起来:
@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. super.setAdapter(adapter); ... } ... public void addFooterView(View v, Object data, boolean isSelectable) { final FixedViewInfo info = new FixedViewInfo(); info.view = v; info.data = data; info.isSelectable = isSelectable; mFooterViewInfos.add(info); // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewListAdapter)) { mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); } // In the case of re-adding a footer view, or adding one later on, // we need to notify the observer. if (mDataSetObserver != null) { mDataSetObserver.onChanged(); } } }
view的复用都体现在方法makeAndAddView调用的obtainView;
保存了listView由于非数据变化原因导致layout时,其所有child view 保存在RecyleBin 的 mActiveView 中。
用到的position参数 都是指adapter中数据项位置。
private View makeAndAddView(int position, int y, boolean flowDown, boolean selected) { View child; onChildCreated(position, flowDown); if (!mDataChanged) { // Try to use an existing view for this position child = mRecycleBin.getActiveView(position); if (child != null) { setupChild(child, position, y, flowDown, selected, true); return child; } } child = obtainView(position, mIsScrap); setupChild(child, position, y, flowDown, selected, mIsScrap[0]); return child; }
obtainView:
private View obtainView(int position, boolean[] isScrap) { isScrap[0] = false; View scrapView; scrapView = mRecycleBin.getScrapView(position); View child; if (scrapView != null) { child = mAdapter.getView(position, scrapView, this); if (child != scrapView) { mRecycleBin.addScrapView(scrapView, position); } else { isScrap[0] = true; } } else { child = mAdapter.getView(position, null, this); } return child; }
public Object instantiateItem(ViewGroup container, final int arg1) { container.addView(mViews.get(arg1),0); return mViews.get(arg1); }
在源码的注释里清楚标注了这一点:
在finishUpdate (ViewGroup) 方法返回前需要完成addView 操作
/**
* Create the page for the given position. The adapter is responsible
* for adding the view to the container given here, although it only
* must ensure this is done by the time it returns from
* {@link #finishUpdate(ViewGroup)}.
*
* @param container The containing View in which the page will be shown.
* @param position The page position to be instantiated.
* @return Returns an Object representing the new page. This does not
* need to be a View, but can be some other container of the page.
*/
public Object instantiateItem(ViewGroup container, int position) {
return instantiateItem((View) container, position);
}
那么 instantiateItem 返回的object 到底什么意义呢?从ViewPager的代码片段可以看出这个object就是(必须)view,它被ViewPager 包装进ItemInfo来记录view的信息:
static class ItemInfo { Object object; int position; boolean scrolling; float widthFactor; float offset; } ItemInfo infoForChild(View child) { for (int i=0; i<mItems.size(); i++) { ItemInfo ii = mItems.get(i); if (mAdapter.isViewFromObject(child, ii.object)) { return ii; } } return null; }