1,listView的childView 与adapter 中数据项的对应关系
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();
}
}
}
2,child view的复用
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;
}
3, ViewPager 的adapter 方法 instantiateItem 方法 必须执行 container.addView(..), 否则不能显示,这和listView是完全不同的。
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;
}